diff --git a/Knot.files b/Knot.files
index df5fb284a7e851811ab705f8e4198536b4bec4de..767a1d3752b332b109c4a4c06c744042add99516 100644
--- a/Knot.files
+++ b/Knot.files
@@ -280,6 +280,8 @@ src/knot/zone/adjust.c
 src/knot/zone/adjust.h
 src/knot/zone/backup.c
 src/knot/zone/backup.h
+src/knot/zone/backup_label.c
+src/knot/zone/backup_label.h
 src/knot/zone/contents.c
 src/knot/zone/contents.h
 src/knot/zone/digest.c
diff --git a/src/knot/Makefile.inc b/src/knot/Makefile.inc
index 4bc38c2036b66633f9a56f5e3777ba727b34d6ff..efce8983e13e54589dec705525a547a675ce2f7d 100644
--- a/src/knot/Makefile.inc
+++ b/src/knot/Makefile.inc
@@ -174,6 +174,8 @@ libknotd_la_SOURCES = \
 	knot/zone/adjust.h			\
 	knot/zone/backup.c			\
 	knot/zone/backup.h			\
+	knot/zone/backup_label.c		\
+	knot/zone/backup_label.h		\
 	knot/zone/contents.c			\
 	knot/zone/contents.h			\
 	knot/zone/digest.c			\
diff --git a/src/knot/zone/backup.c b/src/knot/zone/backup.c
index bb5a0bf2a3e8df7d09c59246c0b4510dbf487096..038455a28ed05a58c3c2109d244b4a0fdb976231 100644
--- a/src/knot/zone/backup.c
+++ b/src/knot/zone/backup.c
@@ -34,9 +34,13 @@
 #include "knot/dnssec/kasp/kasp_zone.h"
 #include "knot/dnssec/kasp/keystore.h"
 #include "knot/journal/journal_metadata.h"
+#include "knot/zone/backup_label.h"
 #include "knot/zone/zonefile.h"
 #include "libdnssec/error.h"
 
+// Current backup format version for output.
+#define BACKUP_VERSION BACKUP_FORMAT_2  // Starting with release 3.1.0.
+
 static void _backup_swap(zone_backup_ctx_t *ctx, void **local, void **remote)
 {
 	if (ctx->restore_mode) {
@@ -46,133 +50,8 @@ static void _backup_swap(zone_backup_ctx_t *ctx, void **local, void **remote)
 	}
 }
 
-// Current backup format version for output.
-#define BACKUP_VERSION BACKUP_FORMAT_2  // Starting with release 3.1.0.
-
-#define LABEL_FILE "knot_backup.label"
-#define LOCK_FILE  "lock.knot_backup"
-
-#define LABEL_FILE_HEAD         "label: Knot DNS Backup\n"
-#define LABEL_FILE_FORMAT       "backup_format: %d\n"
-#define LABEL_FILE_TIME_FORMAT  "%Y-%m-%d %H:%M:%S %Z"
-
-#define FNAME_MAX (MAX(sizeof(LABEL_FILE), sizeof(LOCK_FILE)))
 #define BACKUP_SWAP(ctx, from, to) _backup_swap((ctx), (void **)&(from), (void **)&(to))
 
-static const char *label_file_name = LABEL_FILE;
-static const char *lock_file_name =  LOCK_FILE;
-static const char *label_file_head = LABEL_FILE_HEAD;
-
-static int make_label_file(zone_backup_ctx_t *ctx, char *full_path)
-{
-	FILE *file = fopen(full_path, "w");
-	if (file == NULL) {
-		return knot_map_errno();
-	}
-
-	// Prepare the server identity.
-	conf_val_t val = conf_get(conf(), C_SRV, C_IDENT);
-	const char *ident = conf_str(&val);
-	if (ident == NULL || ident[0] == '\0') {
-		ident = conf()->hostname;
-	}
-
-	// Prepare the timestamps.
-	char started_time[64], finished_time[64];
-	struct tm tm;
-
-	localtime_r(&ctx->init_time, &tm);
-	strftime(started_time, sizeof(started_time), LABEL_FILE_TIME_FORMAT, &tm);
-
-	time_t now = time(NULL);
-	localtime_r(&now, &tm);
-	strftime(finished_time, sizeof(finished_time), LABEL_FILE_TIME_FORMAT, &tm);
-
-	// Print the label contents.
-	int ret = fprintf(file,
-	              "%s"
-	              LABEL_FILE_FORMAT
-	              "identity: %s\n"
-	              "started_time: %s\n"
-	              "finished_time: %s\n"
-	              "knot_version: %s\n"
-	              "parameters: +%szonefile +%sjournal +%stimers +%skaspdb +%scatalog "
-	                  "+backupdir %s\n"
-	              "zone_count: %d\n",
-	              label_file_head,
-	              BACKUP_VERSION, ident, started_time, finished_time, PACKAGE_VERSION,
-	              ctx->backup_zonefile ? "" : "no",
-	              ctx->backup_journal ? "" : "no",
-	              ctx->backup_timers ? "" : "no",
-	              ctx->backup_kaspdb ? "" : "no",
-	              ctx->backup_catalog ? "" : "no",
-	              ctx->backup_dir,
-	              ctx->zone_count);
-
-	ret = (ret < 0) ? knot_map_errno() : KNOT_EOK;
-
-	fclose(file);
-	return ret;
-}
-
-static int get_backup_format(const char *full_path, bool forced, knot_backup_format_t *format)
-{
-	int ret = KNOT_EMALF;
-
-	struct stat sb;
-	if (stat(full_path, &sb) != 0) {
-		ret = knot_map_errno();
-		if (ret == KNOT_ENOENT) {
-			if (forced) {
-				*format = BACKUP_FORMAT_1;
-				ret = KNOT_EOK;
-			} else {
-				ret = KNOT_EMALF;
-			}
-		}
-		return ret;
-	}
-
-	// getline() from an empty file results in EAGAIN, therefore avoid doing so.
-	if (!S_ISREG(sb.st_mode) || sb.st_size == 0) {
-		return ret;
-	}
-
-	FILE *file = fopen(full_path, "r");
-	if (file == NULL) {
-		return knot_map_errno();
-	}
-
-	char *line = NULL;
-	size_t line_size = 0;
-
-	// Check for the header line first.
-	if (knot_getline(&line, &line_size, file) == -1) {
-		ret = knot_map_errno();
-		goto done;
-	}
-
-	if (strcmp(line, label_file_head) != 0) {
-		goto done;
-	}
-
-	int value;
-	while (knot_getline(&line, &line_size, file) != -1) {
-		if (sscanf(line, LABEL_FILE_FORMAT, &value) != 0) {
-			if ((BACKUP_FORMAT_1 < value) && (value < BACKUP_FORMAT_TERM)) {
-				*format = value;
-				ret = KNOT_EOK;
-			}
-			break;
-		}
-	}
-
-done:
-	free(line);
-	fclose(file);
-	return ret;
-}
-
 int zone_backup_init(bool restore_mode, bool forced, const char *backup_dir,
                      size_t kasp_db_size, size_t timer_db_size, size_t journal_db_size,
                      size_t catalog_db_size, zone_backup_ctx_t **out_ctx)
@@ -188,6 +67,7 @@ int zone_backup_init(bool restore_mode, bool forced, const char *backup_dir,
 		return KNOT_ENOMEM;
 	}
 	ctx->restore_mode = restore_mode;
+	ctx->forced = forced;
 	ctx->backup_format = BACKUP_VERSION;
 	ctx->backup_global = false;
 	ctx->readers = 1;
@@ -218,42 +98,13 @@ int zone_backup_init(bool restore_mode, bool forced, const char *backup_dir,
 		}
 	}
 
-	// The \0 terminator is already included in the sizeof() value, thus the sum
-	// covers one additional char for '/'.
-	char full_path[backup_dir_len + FNAME_MAX];
-
-	// Check for existence of a label file and the backup format used.
-	sprintf(full_path, "%s/%s", (ctx)->backup_dir, label_file_name);
-	if (restore_mode) {
-		ret = get_backup_format(full_path, forced, &ctx->backup_format);
-		if (ret != KNOT_EOK) {
-			free(ctx);
-			return ret;
-		}
-	} else {
-		if (stat(full_path, &sb) == 0) {
-			free(ctx);
-			return KNOT_EEXIST;
-		}
-	}
-
-	// Make (or check for existence of) a lock file.
-	sprintf(full_path, "%s/%s", (ctx)->backup_dir, lock_file_name);
-	if (restore_mode) {
-		// Just check.
-		if (stat(full_path, &sb) == 0) {
-			free(ctx);
-			return KNOT_EBUSY;
-		}
-	} else {
-		// Create it (which also checks for its existence).
-		int lock_file = open(full_path, O_CREAT|O_EXCL, S_IRUSR|S_IWUSR);
-		if (lock_file < 0) {
-			free(ctx);
-			// Make the reported error better understandable than KNOT_EEXIST.
-			return errno == EEXIST ? KNOT_EBUSY : knot_map_errno();
-		}
-		close(lock_file);
+	// Depending on the restore mode, create (or check for existence of) the label file
+	// and the lock file. If the label file exists, the backup version is identified and
+	// stored in ctx.
+	ret = init_backup_label_lock(ctx);
+	if (ret != KNOT_EOK) {
+		free(ctx);
+		return ret;
 	}
 
 	pthread_mutex_init(&ctx->readers_mutex, NULL);
@@ -295,24 +146,8 @@ int zone_backup_deinit(zone_backup_ctx_t *ctx)
 		knot_lmdb_deinit(&ctx->bck_kasp_db);
 		pthread_mutex_destroy(&ctx->readers_mutex);
 
-		size_t backup_dir_len = strlen((ctx)->backup_dir) + 1;
-		char full_path[backup_dir_len + FNAME_MAX];
-
-		if (!ctx->restore_mode && !ctx->failed) {
-			// Create the label file first.
-			sprintf(full_path, "%s/%s", (ctx)->backup_dir, label_file_name);
-			ret = make_label_file(ctx, full_path);
-			if (ret == KNOT_EOK) {
-				// Remove the lock file only when the label file has been created.
-				sprintf(full_path, "%s/%s", (ctx)->backup_dir, lock_file_name);
-				unlink(full_path);
-			} else {
-				log_error("failed to create a backup label in %s", (ctx)->backup_dir);
-			}
-		}
-
+		ret = deinit_backup_label_lock(ctx);
 		zone_backups_rem(ctx);
-
 		free(ctx);
 	}
 
diff --git a/src/knot/zone/backup.h b/src/knot/zone/backup.h
index 5ac1c3eb05ba15637447577efd96b758a25443f8..b1d0e3e1301fc6a132a9b098116dae7af1e545f4 100644
--- a/src/knot/zone/backup.h
+++ b/src/knot/zone/backup.h
@@ -32,6 +32,7 @@ typedef enum {
 typedef struct zone_backup_ctx {
 	node_t n;                           // ability to be put into list_t
 	bool restore_mode;                  // if true, this is not a backup, but restore
+	bool forced;                        // if true, the force flag has been set
 	bool backup_zonefile;               // if true, also backup zone contents to a zonefile (default on)
 	bool backup_journal;                // if true, also backup journal (default off)
 	bool backup_timers;                 // if true, also backup timers (default on)
diff --git a/src/knot/zone/backup_label.c b/src/knot/zone/backup_label.c
new file mode 100644
index 0000000000000000000000000000000000000000..11d2adf0f9100db688a68f3a27d87d245f4ea3ed
--- /dev/null
+++ b/src/knot/zone/backup_label.c
@@ -0,0 +1,226 @@
+/*  Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> 
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "knot/zone/backup_label.h"
+
+#include "contrib/getline.h"
+#include "knot/common/log.h"
+
+#define LABEL_FILE "knot_backup.label"
+#define LOCK_FILE  "lock.knot_backup"
+
+#define LABEL_FILE_HEAD         "label: Knot DNS Backup\n"
+#define LABEL_FILE_FORMAT       "backup_format: %d\n"
+#define LABEL_FILE_TIME_FORMAT  "%Y-%m-%d %H:%M:%S %Z"
+
+#define FNAME_MAX (MAX(sizeof(LABEL_FILE), sizeof(LOCK_FILE)))
+#define PREPARE_PATH(var, file) \
+		char var[path_size(ctx)]; \
+		get_full_path(ctx, file, var);
+
+static const char *label_file_name = LABEL_FILE;
+static const char *lock_file_name =  LOCK_FILE;
+static const char *label_file_head = LABEL_FILE_HEAD;
+
+static void get_full_path(zone_backup_ctx_t *ctx, const char *filename, char *full_path)
+{
+	(void)sprintf(full_path, "%s/%s", ctx->backup_dir, filename);
+}
+
+static size_t path_size(zone_backup_ctx_t *ctx)
+{
+	// The \0 terminator is already included in the sizeof()/FNAME_MAX value,
+	// thus the sum covers one additional char for '/'.
+	return (strlen(ctx->backup_dir) + 1 + FNAME_MAX);
+}
+
+static int make_label_file(zone_backup_ctx_t *ctx)
+{
+	PREPARE_PATH(label_path, label_file_name);
+
+	FILE *file = fopen(label_path, "w");
+	if (file == NULL) {
+		return knot_map_errno();
+	}
+
+	// Prepare the server identity.
+	conf_val_t val = conf_get(conf(), C_SRV, C_IDENT);
+	const char *ident = conf_str(&val);
+	if (ident == NULL || ident[0] == '\0') {
+		ident = conf()->hostname;
+	}
+
+	// Prepare the timestamps.
+	char started_time[64], finished_time[64];
+	struct tm tm;
+
+	localtime_r(&ctx->init_time, &tm);
+	strftime(started_time, sizeof(started_time), LABEL_FILE_TIME_FORMAT, &tm);
+
+	time_t now = time(NULL);
+	localtime_r(&now, &tm);
+	strftime(finished_time, sizeof(finished_time), LABEL_FILE_TIME_FORMAT, &tm);
+
+	// Print the label contents.
+	int ret = fprintf(file,
+	              "%s"
+	              LABEL_FILE_FORMAT
+	              "identity: %s\n"
+	              "started_time: %s\n"
+	              "finished_time: %s\n"
+	              "knot_version: %s\n"
+	              "parameters: +%szonefile +%sjournal +%stimers +%skaspdb +%scatalog "
+	                  "+backupdir %s\n"
+	              "zone_count: %d\n",
+	              label_file_head,
+	              ctx->backup_format, ident, started_time, finished_time, PACKAGE_VERSION,
+	              ctx->backup_zonefile ? "" : "no",
+	              ctx->backup_journal ? "" : "no",
+	              ctx->backup_timers ? "" : "no",
+	              ctx->backup_kaspdb ? "" : "no",
+	              ctx->backup_catalog ? "" : "no",
+	              ctx->backup_dir,
+	              ctx->zone_count);
+
+	ret = (ret < 0) ? knot_map_errno() : KNOT_EOK;
+
+	fclose(file);
+	return ret;
+}
+
+static int get_backup_format(zone_backup_ctx_t *ctx)
+{
+	PREPARE_PATH(label_path, label_file_name);
+
+	int ret = KNOT_EMALF;
+
+	struct stat sb;
+	if (stat(label_path, &sb) != 0) {
+		ret = knot_map_errno();
+		if (ret == KNOT_ENOENT) {
+			if (ctx->forced) {
+				ctx->backup_format = BACKUP_FORMAT_1;
+				ret = KNOT_EOK;
+			} else {
+				ret = KNOT_EMALF;
+			}
+		}
+		return ret;
+	}
+
+	// getline() from an empty file results in EAGAIN, therefore avoid doing so.
+	if (!S_ISREG(sb.st_mode) || sb.st_size == 0) {
+		return ret;
+	}
+
+	FILE *file = fopen(label_path, "r");
+	if (file == NULL) {
+		return knot_map_errno();
+	}
+
+	char *line = NULL;
+	size_t line_size = 0;
+
+	// Check for the header line first.
+	if (knot_getline(&line, &line_size, file) == -1) {
+		ret = knot_map_errno();
+		goto done;
+	}
+
+	if (strcmp(line, label_file_head) != 0) {
+		goto done;
+	}
+
+	while (knot_getline(&line, &line_size, file) != -1) {
+		int value;
+		if (sscanf(line, LABEL_FILE_FORMAT, &value) != 0) {
+			if ((BACKUP_FORMAT_1 < value) && (value < BACKUP_FORMAT_TERM)) {
+				ctx->backup_format = value;
+				ret = KNOT_EOK;
+			}
+			break;
+		}
+	}
+
+done:
+	free(line);
+	fclose(file);
+	return ret;
+}
+
+int init_backup_label_lock(zone_backup_ctx_t *ctx)
+{
+	char full_path[path_size(ctx)];
+	struct stat sb;
+
+	// Check for existence of a label file and the backup format used.
+	if (ctx->restore_mode) {
+		int ret = get_backup_format(ctx);
+		if (ret != KNOT_EOK) {
+			return ret;
+		}
+	} else {
+		get_full_path(ctx, label_file_name, full_path);
+		if (stat(full_path, &sb) == 0) {
+			return KNOT_EEXIST;
+		}
+	}
+
+	// Make (or check for existence of) a lock file.
+	get_full_path(ctx, lock_file_name, full_path);
+	if (ctx->restore_mode) {
+		// Just check.
+		if (stat(full_path, &sb) == 0) {
+			return KNOT_EBUSY;
+		}
+	} else {
+		// Create it (which also checks for its existence).
+		int lock_file = open(full_path, O_CREAT|O_EXCL, S_IRUSR|S_IWUSR);
+		if (lock_file < 0) {
+			// Make the reported error better understandable than KNOT_EEXIST.
+			return errno == EEXIST ? KNOT_EBUSY : knot_map_errno();
+		}
+		close(lock_file);
+	}
+
+	return KNOT_EOK;
+}
+
+int deinit_backup_label_lock(zone_backup_ctx_t *ctx)
+{
+	int ret = KNOT_EOK;
+
+	if (!ctx->restore_mode && !ctx->failed) {
+		// Create the label file first.
+		ret = make_label_file(ctx);
+		if (ret == KNOT_EOK) {
+			// Remove the lock file only when the label file has been created.
+			PREPARE_PATH(lock_path, lock_file_name);
+			unlink(lock_path);
+		} else {
+			log_error("failed to create a backup label in %s", (ctx)->backup_dir);
+		}
+	}
+
+	return ret;
+}
diff --git a/src/knot/zone/backup_label.h b/src/knot/zone/backup_label.h
new file mode 100644
index 0000000000000000000000000000000000000000..6a14982c1da782e1d8c5538dc2509741c367d893
--- /dev/null
+++ b/src/knot/zone/backup_label.h
@@ -0,0 +1,38 @@
+/*  Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> 
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "knot/zone/backup.h"
+
+/*!
+ * Verifies existence/non-existence of a lock file and a label file, in the
+ * backup mode it creates them, in the restore mode it sets ctx->backup_format.
+ *
+ * \param[in/out] ctx   Backup context.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int init_backup_label_lock(zone_backup_ctx_t *ctx);
+
+/*!
+ * If the backup has been successful, it creates the label file
+ * and removes the lock file. Nothing for the restore mode.
+ *
+ * \param[in] ctx   Backup context.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int deinit_backup_label_lock(zone_backup_ctx_t *ctx);