diff --git a/src/knot/zone/zonedb-load.c b/src/knot/zone/zonedb-load.c
index 4449928dc93ec953f979aa5e5ac57d637e1cb6c6..5126a4ca2bedc5a0901138b643b5edfe38fdd217 100644
--- a/src/knot/zone/zonedb-load.c
+++ b/src/knot/zone/zonedb-load.c
@@ -432,6 +432,20 @@ static knot_zonedb_t *create_zonedb(conf_t *conf, server_t *server, list_t *expi
 		knot_zonedb_insert(db_new, zone);
 	}
 
+	/* Remove deleted cataloged zones from conf before catalog removals are commited. */
+	catalog_it_t *cat_it = catalog_it_begin(&server->catalog_upd);
+	while (!catalog_it_finished(cat_it)) {
+		catalog_upd_val_t *upd = catalog_it_val(cat_it);
+		if (upd->type == CAT_UPD_REM) {
+			zone_t *zone = knot_zonedb_find(db_old, upd->member);
+			if (zone != NULL) {
+				zone_purge(conf, zone);
+			}
+		}
+		catalog_it_next(cat_it);
+	}
+	catalog_it_free(cat_it);
+
 	int ret = catalog_update_commit(&server->catalog_upd, &server->catalog);
 	if (ret != KNOT_EOK) {
 		log_error("catalog, failed to apply changes (%s)", knot_strerror(ret));
@@ -526,19 +540,6 @@ static void remove_old_zonedb(conf_t *conf, knot_zonedb_t *db_old,
 	knot_zonedb_iter_free(it);
 
 catalog_only:
-	; /* Remove deleted cataloged zones from conf. */
-	catalog_it_t *cat_it = catalog_it_begin(&server->catalog_upd);
-	while (!catalog_it_finished(cat_it)) {
-		catalog_upd_val_t *upd = catalog_it_val(cat_it);
-		if (upd->type == CAT_UPD_REM) {
-			zone_t *zone = knot_zonedb_find(db_old, upd->member);
-			if (zone != NULL) {
-				zone_purge(conf, zone);
-			}
-		}
-		catalog_it_next(cat_it);
-	}
-	catalog_it_free(cat_it);
 
 	/* Clear catalog changes. No need to use mutex as this is done from main
 	 * thread while all zone events are paused. */
diff --git a/tests-extra/tests/catalog/basic/test.py b/tests-extra/tests/catalog/basic/test.py
index f3b95166613e45a9f9049caf5a13770be7cf0fea..cdb148e3ddb09c971b31c80c4179e6029c637463 100644
--- a/tests-extra/tests/catalog/basic/test.py
+++ b/tests-extra/tests/catalog/basic/test.py
@@ -8,6 +8,7 @@ from dnstest.module import ModOnlineSign
 import dnstest.params
 
 import glob
+import os
 import shutil
 from subprocess import DEVNULL, PIPE, Popen
 import subprocess
@@ -21,6 +22,9 @@ def check_keys(server, zone_name, expect_keys):
     if lines != expect_keys:
         set_err("CHECK # of KEYS (%d != %d)" % (lines, expect_keys))
 
+def member_zonefile(server, zone):
+    return server.dir + "/catalog/" + zone + "zone"
+
 t = Test()
 
 master = t.server("knot")
@@ -39,8 +43,9 @@ if random.choice([True, False]):
 else:
     slave.add_module(zone[1], ModOnlineSign(algorithm="ECDSAP256SHA256", single_type_signing=False))
 
+os.mkdir(master.dir + "/catalog")
 for zf in glob.glob(t.data_dir + "/*.zone"):
-    shutil.copy(zf, master.dir + "/master")
+    shutil.copy(zf, master.dir + "/catalog")
 
 t.start()
 
@@ -60,7 +65,7 @@ resp = master.dig("not-cataloged3.", "SOA")
 resp.check(rcode="REFUSED")
 
 # Updating a cataloged zone
-subprocess.run(["sed", "-i", "s/10001/10002/;$s/$/\\nxyz A 1.2.3.4/", master.dir + "/master/cataloged1.zone"])
+subprocess.run(["sed", "-i", "s/10001/10002/;$s/$/\\nxyz A 1.2.3.4/", member_zonefile(master, "cataloged1.")])
 master.ctl("zone-reload cataloged1.")
 t.sleep(4)
 resp = slave.dig("xyz.cataloged1.", "A", dnssec=True)
@@ -116,7 +121,9 @@ up.delete("bar.zones.catalog1.", "PTR", "cataloged2.")
 up.add("bar2.zones.catalog1.", 0, "PTR", "cataloged2.")
 up.send("NOERROR")
 t.sleep(4)
-shutil.copy(t.data_dir + "/cataloged2.zone", master.dir + "/master") # because the purge deletes even zonefile
+if os.path.exists(member_zonefile(master, "cataloged2.")):
+    set_err("removed member zone file not purged")
+shutil.copy(t.data_dir + "/cataloged2.zone", member_zonefile(master, "cataloged2.")) # because the purge deletes even zonefile
 master.ctl("zone-reload cataloged2.")
 t.sleep(6)
 resp2 = slave.dig("cataloged2.", "DNSKEY")
@@ -177,6 +184,8 @@ resp.check(rcode="REFUSED")
 resp = slave.dig("cataloged1.", "DNSKEY")
 resp.check(rcode="REFUSED")
 check_keys(slave, "cataloged1", 0)
+if os.path.exists(member_zonefile(master, "cataloged1.")):
+    set_err("removed member zone file 2 not purged")
 
 # Check restoring catalog from backup
 master.ctl("zone-restore +journal +backupdir %s/backup %s" % (master.dir, zone[1].name))
diff --git a/tests-extra/tests/catalog/groups/test.py b/tests-extra/tests/catalog/groups/test.py
index c31847664374ef8b80f88c4b8dffa6e9c89b5203..607def27d46dc8e01eefc5ff57ebb933ba4b727a 100644
--- a/tests-extra/tests/catalog/groups/test.py
+++ b/tests-extra/tests/catalog/groups/test.py
@@ -8,6 +8,7 @@ from dnstest.module import ModOnlineSign
 import dnstest.params
 
 import glob
+import os
 import shutil
 from subprocess import DEVNULL, PIPE, Popen
 import subprocess
@@ -32,8 +33,9 @@ t.link(zone, master)
 
 master.cat_interpret(zone)
 
+os.mkdir(master.dir + "/catalog")
 for zf in glob.glob(t.data_dir + "/*.zone"):
-    shutil.copy(zf, master.dir + "/master")
+    shutil.copy(zf, master.dir + "/catalog")
 
 t.start()
 
@@ -84,7 +86,7 @@ up.delete("group.foo.zones.catalog2.", "TXT")
 up.send("NOERROR")
 t.sleep(4)
 # check that DNSSEC no longer works
-with open(master.dir + "/master/cataloged1.zone", "a") as c1zf:
+with open(master.dir + "/catalog/cataloged1.zone", "a") as c1zf:
     c1zf.write("added A 1.2.3.4")
 master.ctl("zone-reload cataloged1.")
 t.sleep(4)
diff --git a/tests-extra/tests/catalog/rapid_updates/test.py b/tests-extra/tests/catalog/rapid_updates/test.py
index ff44f9479f12f68850dff0914563912003f10eb8..a49762cd7dc5019d71b44ac194101ad742149db9 100644
--- a/tests-extra/tests/catalog/rapid_updates/test.py
+++ b/tests-extra/tests/catalog/rapid_updates/test.py
@@ -26,6 +26,8 @@ catz = t.zone("catalog1.", storage=".")
 t.link(catz, knot)
 knot.cat_interpret(catz)
 
+os.mkdir(knot.dir + "/catalog")
+
 t.start()
 
 knot.zone_wait(catz, udp=False, tsig=True)
@@ -45,7 +47,7 @@ for i in range(UPDATES):
     zone_add = "member%d." % i
     name_hash = hashlib.md5(zone_add.encode()).hexdigest()
 
-    shutil.copyfile(t.data_dir + "generic.zone" , knot.dir + "/master/" + zone_add + "zone")
+    shutil.copyfile(t.data_dir + "generic.zone" , knot.dir + "/catalog/" + zone_add + "zone")
 
     up = knot.update(catz)
     up.add(name_hash + ".zones", 0, "PTR", zone_add)
diff --git a/tests-extra/tools/dnstest/server.py b/tests-extra/tools/dnstest/server.py
index 342aa06157c218b254f177f5ffa8eb527e66935e..401abadf999e520bf77d5e3e9bdaa50a98f7d64d 100644
--- a/tests-extra/tools/dnstest/server.py
+++ b/tests-extra/tools/dnstest/server.py
@@ -1500,7 +1500,7 @@ class Knot(Server):
                 break
         if have_catalog is not None:
             s.id_item("id", "catalog-default")
-            s.item_str("file", self.dir + "/master/%s.zone")
+            s.item_str("file", self.dir + "/catalog/%s.zone")
             s.item_str("zonefile-load", "difference")
             s.item_str("journal-content", z.journal_content)
 
@@ -1515,13 +1515,13 @@ class Knot(Server):
             self.config_xfr(z, s)
 
             s.id_item("id", "catalog-signed")
-            s.item_str("file", self.dir + "/master/%s.zone")
+            s.item_str("file", self.dir + "/catalog/%s.zone")
             s.item_str("journal-content", z.journal_content)
             s.item_str("dnssec-signing", "on")
             self.config_xfr(z, s)
 
             s.id_item("id", "catalog-unsigned")
-            s.item_str("file", self.dir + "/master/%s.zone")
+            s.item_str("file", self.dir + "/catalog/%s.zone")
             s.item_str("journal-content", z.journal_content)
             self.config_xfr(z, s)