diff --git a/changelog.d/8011.bugfix b/changelog.d/8011.bugfix
new file mode 100644
index 0000000000000000000000000000000000000000..c673040de9383b6ccc602072e7bff4035848790f
--- /dev/null
+++ b/changelog.d/8011.bugfix
@@ -0,0 +1 @@
+Fix a long-standing bug which caused two copies of some log lines to be written when synctl was used along with a MemoryHandler logger.
diff --git a/changelog.d/8011.misc b/changelog.d/8011.misc
deleted file mode 100644
index dfeb4bdaf1c6b19a257855ec13f6c04b1bc3a5be..0000000000000000000000000000000000000000
--- a/changelog.d/8011.misc
+++ /dev/null
@@ -1 +0,0 @@
-Replace daemonize library with a local implementation.
diff --git a/changelog.d/8012.bugfix b/changelog.d/8012.bugfix
new file mode 100644
index 0000000000000000000000000000000000000000..c673040de9383b6ccc602072e7bff4035848790f
--- /dev/null
+++ b/changelog.d/8012.bugfix
@@ -0,0 +1 @@
+Fix a long-standing bug which caused two copies of some log lines to be written when synctl was used along with a MemoryHandler logger.
diff --git a/synapse/util/daemonize.py b/synapse/util/daemonize.py
index a7913fa1afa98e697fede7b6a9b22a5e2ca38e9a..23393cf49bb7d0bf04c7a4f0e5c131dcd25b704e 100644
--- a/synapse/util/daemonize.py
+++ b/synapse/util/daemonize.py
@@ -60,8 +60,14 @@ def daemonize_process(pid_file: str, logger: logging.Logger, chdir: str = "/") -
     process_id = os.fork()
 
     if process_id != 0:
-        # parent process
-        sys.exit(0)
+        # parent process: exit.
+
+        # we use os._exit to avoid running the atexit handlers. In particular, that
+        # means we don't flush the logs. This is important because if we are using
+        # a MemoryHandler, we could have logs buffered which are now buffered in both
+        # the main and the child process, so if we let the main process flush the logs,
+        # we'll get two copies.
+        os._exit(0)
 
     # This is the child process. Continue.