diff --git a/src/knot/server/zones.c b/src/knot/server/zones.c
index 7c027acef1b2209e7f27be9ebf2123c9e913541c..8a0df1d01ea1da3300fa612eff06d746ef088750 100644
--- a/src/knot/server/zones.c
+++ b/src/knot/server/zones.c
@@ -48,6 +48,37 @@ static const size_t XFRIN_CHANGESET_BINARY_SIZE = 100;
 static const size_t XFRIN_CHANGESET_BINARY_STEP = 100;
 static const size_t XFRIN_BOOTSTRAP_DELAY = 2000; /*!< AXFR bootstrap avg. delay */
 
+
+#include "libknot/rrset-dump.h"
+
+static void knot_zone_diff_dump_changeset(knot_changeset_t *ch)
+{
+	if (ch == NULL) {
+		return;
+	}
+	printf("Changeset FROM: %d\n", ch->serial_from);
+	knot_rrset_dump(ch->soa_from);
+	printf("\n");
+	printf("Changeset TO: %d\n", ch->serial_to);
+	knot_rrset_dump(ch->soa_to);
+	printf("\n");
+
+	printf("ADD section:\n");
+	printf("**********************************************\n");
+	char buf[1024];
+	knot_rr_ln_t *rr_node;
+	WALK_LIST(rr_node, ch->add) {
+		knot_rrset_txt_dump(rr_node->rr, buf, 1024, &KNOT_DUMP_STYLE_DEFAULT);
+		printf("%s\n", buf);
+	}
+	printf("REMOVE section:\n");
+	printf("**********************************************\n");
+	WALK_LIST(rr_node, ch->remove) {
+		knot_rrset_txt_dump(rr_node->rr, buf, 1024, &KNOT_DUMP_STYLE_DEFAULT);
+		printf("%s\n", buf);
+	}
+}
+
 /* Forward declarations. */
 static int zones_dump_zone_text(knot_zone_contents_t *zone,  const char *zf);
 
@@ -442,6 +473,9 @@ static int zones_ixfrdb_sync_apply(journal_t *j, journal_node_t *n)
 static int zones_store_changesets_to_disk(knot_zone_t *zone,
                                           knot_changesets_t *chgsets)
 {
+	if (EMPTY_LIST(chgsets->sets)) {
+		return KNOT_EOK;
+	}
 	journal_t *journal = zones_store_changesets_begin(zone);
 	if (journal == NULL) {
 		dbg_zones("zones: create_changesets: "
@@ -1042,9 +1076,14 @@ static int zones_merge_and_store_changesets(knot_zone_t *zone,
 		return KNOT_EOK;
 	}
 	if (diff_chs != NULL && sec_chs == NULL) {
+
+	printf("storing diff:\n");
+	knot_zone_diff_dump_changeset(knot_changesets_get_last(diff_chs));
 		return zones_store_changesets_to_disk(zone, diff_chs);
 	}
 	if (diff_chs == NULL && sec_chs != NULL) {
+	printf("storing sec:\n");
+	knot_zone_diff_dump_changeset(knot_changesets_get_last(sec_chs));
 		return zones_store_changesets_to_disk(zone, sec_chs);
 	}
 
@@ -1052,12 +1091,45 @@ static int zones_merge_and_store_changesets(knot_zone_t *zone,
 	 * Merge changesets (only one in each 'changesets_t' structure),
 	 * use serial from diff (it's user supplied).
 	 */
+	printf("storing merge:\n");
 	int ret = knot_changeset_merge(knot_changesets_get_last(diff_chs),
 	                               knot_changesets_get_last(sec_chs));
 	if (ret != KNOT_EOK) {
 		return ret;
 	}
 
+	/* Rewrite SOAs in 'sec_chs' - we need to use SOAs from 'diff_chs' */
+	/* SOA to */
+	knot_rrset_deep_free(&knot_changesets_get_last(diff_chs)->soa_to, 1, 1);
+	knot_rrset_t *soa_copy = NULL;
+	ret = knot_rrset_deep_copy_no_sig(
+		knot_changesets_get_last(diff_chs)->soa_to, &soa_copy, 1);
+	if (ret != KNOT_EOK) {
+		return ret;
+	}
+	knot_changeset_add_soa(knot_changesets_get_last(sec_chs), soa_copy,
+	                       KNOT_CHANGESET_ADD);
+
+	/* SOA from */
+	knot_rrset_deep_free(&knot_changesets_get_last(diff_chs)->soa_from, 1, 1);
+	ret = knot_rrset_deep_copy_no_sig(
+		knot_changesets_get_last(diff_chs)->soa_from, &soa_copy, 1);
+	if (ret != KNOT_EOK) {
+		return ret;
+	}
+	knot_changeset_add_soa(knot_changesets_get_last(sec_chs), soa_copy,
+	                       KNOT_CHANGESET_REMOVE);
+
+	/* First SOA */
+	knot_rrset_deep_free(&sec_chs->first_soa, 1, 1);
+	ret = knot_rrset_deep_copy_no_sig(sec_chs->first_soa, &soa_copy, 1);
+	if (ret != KNOT_EOK) {
+		return ret;
+	}
+	sec_chs->first_soa = soa_copy;
+	knot_zone_diff_dump_changeset(knot_changesets_get_last(diff_chs));
+
+	/* Store ALL changes to disk. */
 	ret = zones_store_changesets_to_disk(zone, diff_chs);
 	if (ret != KNOT_EOK) {
 		return ret;
@@ -1323,6 +1395,7 @@ static int zones_insert_zone(conf_zone_t *z, knot_zone_t **dst,
 
 		/* Run DNSSEC signing if enabled (no zone change needed) */
 		knot_changesets_t *sec_chs = NULL;
+		knot_changeset_t *sec_ch = NULL;
 		knot_zone_contents_t *new_contents = NULL;
 		if (z->dnssec_enable) {
 			sec_chs =
@@ -1333,8 +1406,15 @@ static int zones_insert_zone(conf_zone_t *z, knot_zone_t **dst,
 				return KNOT_ENOMEM;
 			}
 			/* Extra changeset is needed. */
-			knot_changeset_t *sec_ch =
-				knot_changesets_create_changeset(sec_chs);
+			sec_ch = knot_changesets_create_changeset(sec_chs);
+			if (sec_ch == NULL) {
+				knot_changesets_free(&diff_chs);
+				knot_changesets_free(&sec_chs);
+				rcu_read_unlock();
+				return KNOT_ENOMEM;
+			}
+
+			/* Sign the zone. */
 			int ret = knot_dnssec_zone_sign(zone, sec_ch);
 			if (ret != KNOT_EOK) {
 				knot_changesets_free(&diff_chs);
@@ -1342,20 +1422,6 @@ static int zones_insert_zone(conf_zone_t *z, knot_zone_t **dst,
 				rcu_read_unlock();
 				return ret;
 			}
-
-			if (!knot_changeset_is_empty(sec_ch)) {
-				/* Apply DNSSEC changeset. */
-				ret = xfrin_apply_changesets(zone, sec_chs,
-				                             &new_contents);
-				if (ret != KNOT_EOK) {
-					knot_changesets_free(&diff_chs);
-					knot_changesets_free(&sec_chs);
-					rcu_read_unlock();
-					return ret;
-				}
-				/* If server dies now, we have to resign. */
-				assert(new_contents);
-			}
 		}
 
 		/* Merge changesets created by diff and sign. */
@@ -1368,20 +1434,29 @@ static int zones_insert_zone(conf_zone_t *z, knot_zone_t **dst,
 			rcu_read_unlock();
 			return ret;
 		}
+
+		/* Apply DNSSEC changeset. */
+		if (!knot_changeset_is_empty(sec_ch)) {
+			ret = xfrin_apply_changesets(zone, sec_chs,
+			                             &new_contents);
+			if (ret != KNOT_EOK) {
+				knot_changesets_free(&diff_chs);
+				knot_changesets_free(&sec_chs);
+				rcu_read_unlock();
+				return ret;
+			}
+			assert(new_contents);
+		}
+
 		/* Switch zone contents. */
 		rcu_read_unlock(); // TODO isn't this unlock too soon?
 		if (new_contents) {
 			ret = xfrin_switch_zone(zone, new_contents,
 			                        XFR_TYPE_UPDATE);
 			if (ret != KNOT_EOK) {
-				rcu_read_unlock();
 				return ret;
 			}
 		}
-		knot_changesets_free(&diff_chs);
-		knot_changesets_free(&sec_chs);
-		xfrin_cleanup_successful_update(diff_chs ? diff_chs->changes : NULL);
-		xfrin_cleanup_successful_update(sec_chs ? sec_chs->changes : NULL);
 	}
 
 	knot_dname_free(&dname);
@@ -3298,6 +3373,8 @@ static int zones_dnssec_ev(event_t *event)
 		return ret;
 	}
 
+	knot_zone_diff_dump_changeset(ch);
+
 	knot_zone_contents_t *new_c = NULL;
 	ret = zones_store_and_apply_chgsets(chs, zone, &new_c, "DNSSEC",
 	                                    XFR_TYPE_UPDATE);
diff --git a/src/libknot/dnssec/zone-events.c b/src/libknot/dnssec/zone-events.c
index 4c6b1260b375e04958194d4494b3a7bc08cd99d7..9bec8074f015b7134a218aa4ff136603744aba32 100644
--- a/src/libknot/dnssec/zone-events.c
+++ b/src/libknot/dnssec/zone-events.c
@@ -121,6 +121,7 @@ static int zone_sign(knot_zone_t *zone, knot_changeset_t *out_ch, bool force)
 		free(zname);
 		free_sign_contexts(&zone_keys);
 		free_zone_keys(&zone_keys);
+		assert(knot_changeset_is_empty(out_ch));
 		return KNOT_EOK;
 	}
 
@@ -141,7 +142,7 @@ static int zone_sign(knot_zone_t *zone, knot_changeset_t *out_ch, bool force)
 	free_sign_contexts(&zone_keys);
 	free_zone_keys(&zone_keys);
 
-	printf("%d %d\n", list_size(&out_ch->add), list_size(&out_ch->remove));
+	printf("OK:%d %d\n", list_size(&out_ch->add), list_size(&out_ch->remove));
 
 	return KNOT_EOK;
 }
diff --git a/src/libknot/dnssec/zone-sign.c b/src/libknot/dnssec/zone-sign.c
index 1f69f4a349984663cfdc24506edffaaf2fd50188..d5e2b88bcfcd801882b4b63b5ef247ed87243514 100644
--- a/src/libknot/dnssec/zone-sign.c
+++ b/src/libknot/dnssec/zone-sign.c
@@ -123,7 +123,7 @@ static int sign_rrset_one(knot_rrset_t *rrsigs,
 	rrsig_write_rdata(rdata, key, knot_rrset_owner(covered), covered,
 	                  sig_incept, sig_expire);
 
-	// RFC 4034: The signature coveres RRSIG RDATA field (excluding the
+	// RFC 4034: The signature covers RRSIG RDATA field (excluding the
 	// signature) and all matching RR records, which are ordered
 	// canonically.
 
@@ -202,7 +202,10 @@ static bool full_sig_check(const knot_rrset_t *covered,
                            knot_dnssec_sign_context_t *ctx,
                            const knot_dnssec_policy_t *policy)
 {
-	assert(rrsigs && covered && ctx && policy);
+	assert(rrsigs && covered && policy);
+	if (key == NULL || ctx == NULL) {
+		return false;
+	}
 	// Do cheaper check first
 	if (!is_valid_signature(rrsigs, pos, policy)) {
 		return false;
@@ -277,7 +280,30 @@ static bool all_signatures_valid(const knot_rrset_t *covered,
 	return true;
 }
 
-static int remove_expired_rrsigs(const knot_rrset_t *rrsigs,
+static void get_matching_signing_data(const knot_rrset_t *rrsigs,
+				      size_t pos,
+				      const knot_zone_keys_t *keys,
+				      const knot_dnssec_key_t **key,
+				      knot_dnssec_sign_context_t **ctx)
+{
+	uint16_t keytag = knot_rrset_rdata_rrsig_key_tag(rrsigs, pos);
+	for (int i = 0; i < keys->count; i++) {
+		const knot_dnssec_key_t *found_key = &keys->keys[i];
+		if (keytag != found_key->keytag)
+			continue;
+		*ctx = keys->contexts[i];
+		*key = &keys->keys[i];
+		return;
+	}
+
+	*ctx = NULL;
+	*key = NULL;
+	return;
+}
+
+static int remove_expired_rrsigs(const knot_rrset_t *covered,
+				 const knot_rrset_t *rrsigs,
+				 const knot_zone_keys_t *zone_keys,
 				 const knot_dnssec_policy_t *policy,
 				 knot_changeset_t *changeset)
 {
@@ -292,8 +318,13 @@ static int remove_expired_rrsigs(const knot_rrset_t *rrsigs,
 	int result = KNOT_EOK;
 
 	for (int i = 0; i < rrsigs->rdata_count; i++) {
-		if (is_valid_signature(rrsigs, i, policy))
+		// Get key that matches RRSIGs'
+		const knot_dnssec_key_t *key = NULL;
+		knot_dnssec_sign_context_t *ctx = NULL;
+		get_matching_signing_data(rrsigs, i, zone_keys, &key, &ctx);
+		if (full_sig_check(covered, rrsigs, i, key, ctx, policy))
 			continue;
+		assert(key && ctx);
 
 		if (to_remove == NULL) {
 			to_remove = create_empty_rrsigs_for(rrsigs);
@@ -377,7 +408,6 @@ static int remove_rrset_rrsigs(const knot_rrset_t *rrset,
 	assert(rrset && rrset->rrsigs);
 	knot_rrset_t *to_remove = NULL;
 	int res = knot_rrset_deep_copy(rrset->rrsigs, &to_remove, 1);
-
 	if (res != KNOT_EOK) {
 		return res;
 	}
@@ -408,7 +438,8 @@ static int resign_rrset(const knot_rrset_t *rrset,
                         const knot_dnssec_policy_t *policy,
                         knot_changeset_t *ch)
 {
-	int ret = remove_expired_rrsigs(rrset->rrsigs, policy, ch);
+	int ret = remove_expired_rrsigs(rrset, rrset->rrsigs, zone_keys,
+	                                policy, ch);
 	if (ret != KNOT_EOK) {
 		return ret;
 	}
diff --git a/src/libknot/updates/changesets.c b/src/libknot/updates/changesets.c
index 845315241c23cb1025dac59ef7f7615230bdd11b..4322a1a2954c34dbff9cafda55304a1649c8f977 100644
--- a/src/libknot/updates/changesets.c
+++ b/src/libknot/updates/changesets.c
@@ -293,10 +293,6 @@ int knot_changeset_merge(knot_changeset_t *ch1, knot_changeset_t *ch2)
 	add_tail(&ch1->add, HEAD(ch2->add));
 	add_tail(&ch1->remove, HEAD(ch2->remove));
 
-	// Destroy the lists in the second changeset
-	init_list(&ch2->add);
-	init_list(&ch2->remove);
-
 	return KNOT_EOK;
 }
 
diff --git a/src/libknot/updates/changesets.h b/src/libknot/updates/changesets.h
index 7d5a203bb62d0df2885cd5e52bb0caf9240de086..b19a09ffb4134005560b6408946b32f4caa21b63 100644
--- a/src/libknot/updates/changesets.h
+++ b/src/libknot/updates/changesets.h
@@ -277,8 +277,7 @@ int knot_changes_add_node(knot_changes_t *ch, knot_node_t *kn_node,
                           knot_changes_part_t part);
 
 /*!
- * \brief Merges two changesets together, second changeset's lists are nilled.
- *
+ * \brief Merges two changesets together, second changeset's lists are kept.
  *
  * \param ch1 Changeset to merge into
  * \param ch2 Changeset to merge
diff --git a/src/libknot/zone/zone-diff.c b/src/libknot/zone/zone-diff.c
index a13532db5ed6c4357f38c7955997bbf767bb7e23..507c0c78c743103d5a3c518084814e296c016d3b 100644
--- a/src/libknot/zone/zone-diff.c
+++ b/src/libknot/zone/zone-diff.c
@@ -971,7 +971,7 @@ int knot_zone_contents_create_diff(const knot_zone_contents_t *z1,
 	dbg_zonediff("Changesets created successfully!\n");
 	dbg_zonediff_detail("Changeset dump:\n");
 dbg_zonediff_exec_detail(
-	knot_zone_diff_dump_changeset(HEAD((*changeset)->sets));
+	knot_zone_diff_dump_changeset(changeset);
 );
 
 	return KNOT_EOK;