diff --git a/Knot.files b/Knot.files
index 5562463f41daed35d8c2c30034e91b8550265d2c..b7aa3eb0a015b3731387e96bbf43b9d75d4f402e 100644
--- a/Knot.files
+++ b/Knot.files
@@ -451,6 +451,8 @@ src/utils/keymgr/legacy/privkey.c
 src/utils/keymgr/legacy/privkey.h
 src/utils/keymgr/legacy/pubkey.c
 src/utils/keymgr/legacy/pubkey.h
+src/utils/keymgr/options.c
+src/utils/keymgr/options.h
 src/utils/khost/khost_main.c
 src/utils/khost/khost_params.c
 src/utils/khost/khost_params.h
diff --git a/Knot.includes b/Knot.includes
index 9d34de5639b1d0d8ba7d8a6d65d8d7b054228077..076d39e8627270c745a8bd79bf582fbfbc6d53e9 100644
--- a/Knot.includes
+++ b/Knot.includes
@@ -1,10 +1,10 @@
 libtap
 src
 src/contrib
+src/dnssec
 src/dnssec/lib
 src/dnssec/lib/dnssec
 src/dnssec/shared
-src/dnssec/utils
 src/zscanner
 tests
 tests-fuzz
diff --git a/src/Makefile.am b/src/Makefile.am
index 7d40b8e4e4675d610cd02ec1e5c802a5c0a9a965..71e56e6191ea9041d352360278d9f274989f1f92 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -246,6 +246,8 @@ keymgr_SOURCES =				\
 	utils/keymgr/legacy/privkey.h		\
 	utils/keymgr/legacy/pubkey.c		\
 	utils/keymgr/legacy/pubkey.h		\
+	utils/keymgr/options.c			\
+	utils/keymgr/options.h			\
 	utils/keymgr/keymgr.c
 
 knotc_SOURCES =					\
@@ -422,7 +424,8 @@ libknotd_la_LDFLAGS = $(AM_LDFLAGS) $(systemd_LIBS) $(liburcu_LIBS)
 libknotd_la_LIBADD = libknot.la libknot-yparser.la zscanner/libzscanner.la $(liburcu_LIBS)
 
 keymgr_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/dnssec/lib/dnssec -I$(srcdir)/dnssec $(gnutls_CFLAGS)
-keymgr_LDADD = dnssec/libdnssec.la dnssec/libshared.la zscanner/libzscanner.la $(gnutls_LIBS)
+keymgr_LDADD = libknotd.la libknotus.la dnssec/libdnssec.la dnssec/libshared.la \
+               zscanner/libzscanner.la $(gnutls_LIBS)
 knotd_CPPFLAGS = $(AM_CPPFLAGS) $(liburcu_CFLAGS)
 knotd_LDADD = libknotd.la $(liburcu_LIBS)
 knotc_CPPFLAGS = $(AM_CPPFLAGS) $(libedit_CFLAGS)
diff --git a/src/utils/keymgr/keymgr.c b/src/utils/keymgr/keymgr.c
index 05355ce421ef4c4ed5d8b51393101890e23e2046..67bc88ee18c8c57502349fee786e230751b1834e 100644
--- a/src/utils/keymgr/keymgr.c
+++ b/src/utils/keymgr/keymgr.c
@@ -31,6 +31,7 @@
 #include "cmdparse/parameter.h"
 #include "cmdparse/value.h"
 #include "legacy/key.h"
+#include "options.h"
 #include "shared/dname.h"
 #include "shared/print.h"
 #include "shared/shared.h"
@@ -46,12 +47,6 @@
 
 /* -- global options ------------------------------------------------------- */
 
-struct options {
-	char *kasp_dir;
-};
-
-typedef struct options options_t;
-
 static options_t global = { 0 };
 
 /* -- internal ------------------------------------------------------------- */
@@ -95,12 +90,21 @@ static void cleanup_list(dnssec_list_t **list_ptr)
 
 /* -- frequent operations -------------------------------------------------- */
 
-static dnssec_kasp_t *get_kasp(void)
+static dnssec_kasp_t *get_zone_kasp(const char *zone_name)
 {
+	int r = options_zone_kasp_path(&global, zone_name);
+	if (r != DNSSEC_EOK) {
+		return NULL;
+	}
+
 	dnssec_kasp_t *kasp = NULL;
 
-	dnssec_kasp_init_dir(&kasp);
-	int r = dnssec_kasp_open(kasp, global.kasp_dir);
+	r = options_zone_kasp_init(&global, zone_name, &kasp);
+	if (r != DNSSEC_EOK) {
+		return NULL;
+	}
+
+	r = dnssec_kasp_open(kasp, global.kasp_dir);
 	if (r != DNSSEC_EOK) {
 		error("Cannot open KASP directory (%s).", dnssec_strerror(r));
 		dnssec_kasp_deinit(kasp);
@@ -110,6 +114,11 @@ static dnssec_kasp_t *get_kasp(void)
 	return kasp;
 }
 
+static dnssec_kasp_t *get_kasp(void)
+{
+	return get_zone_kasp(NULL);
+}
+
 static dnssec_kasp_zone_t *get_zone(dnssec_kasp_t *kasp, const char *name)
 {
 	dnssec_kasp_zone_t *zone = NULL;
@@ -660,7 +669,7 @@ static int cmd_zone_add(int argc, char *argv[])
 
 	// create zone
 
-	_cleanup_kasp_ dnssec_kasp_t *kasp = get_kasp();
+	_cleanup_kasp_ dnssec_kasp_t *kasp = get_zone_kasp(zone_name);
 	if (!kasp) {
 		return 1;
 	}
@@ -710,7 +719,7 @@ static int cmd_zone_list(int argc, char *argv[])
 		return 1;
 	}
 
-	_cleanup_kasp_ dnssec_kasp_t *kasp = get_kasp();
+	_cleanup_kasp_ dnssec_kasp_t *kasp = get_zone_kasp(match);
 	if (!kasp) {
 		return 1;
 	}
@@ -740,7 +749,7 @@ static int cmd_zone_show(int argc, char *argv[])
 
 	char *zone_name = argv[0];
 
-	_cleanup_kasp_ dnssec_kasp_t *kasp = get_kasp();
+	_cleanup_kasp_ dnssec_kasp_t *kasp = get_zone_kasp(zone_name);
 	if (!kasp) {
 		return 1;
 	}
@@ -796,7 +805,7 @@ static int cmd_zone_remove(int argc, char *argv[])
 
 	// delete zone
 
-	_cleanup_kasp_ dnssec_kasp_t *kasp = get_kasp();
+	_cleanup_kasp_ dnssec_kasp_t *kasp = get_zone_kasp(zone_name);
 	if (!kasp) {
 		return 1;
 	}
@@ -836,7 +845,7 @@ static int cmd_zone_key_list(int argc, char *argv[])
 
 	// list the keys
 
-	_cleanup_kasp_ dnssec_kasp_t *kasp = get_kasp();
+	_cleanup_kasp_ dnssec_kasp_t *kasp = get_zone_kasp(zone_name);
 	if (!kasp) {
 		return 1;
 	}
@@ -871,7 +880,7 @@ static int cmd_zone_key_show(int argc, char *argv[])
 
 	// list the keys
 
-	_cleanup_kasp_ dnssec_kasp_t *kasp = get_kasp();
+	_cleanup_kasp_ dnssec_kasp_t *kasp = get_zone_kasp(zone_name);
 	if (!kasp) {
 		return 1;
 	}
@@ -960,7 +969,7 @@ static int cmd_zone_key_ds(int argc, char *argv[])
 	const char *zone_name = argv[0];
 	const char *key_name = argv[1];
 
-	_cleanup_kasp_ dnssec_kasp_t *kasp = get_kasp();
+	_cleanup_kasp_ dnssec_kasp_t *kasp = get_zone_kasp(zone_name);
 	_cleanup_zone_ dnssec_kasp_zone_t *zone = get_zone(kasp, zone_name);
 	dnssec_list_t *keys = dnssec_kasp_zone_get_keys(zone);
 
@@ -1048,7 +1057,7 @@ static int cmd_zone_key_generate(int argc, char *argv[])
 
 	// open KASP and key store
 
-	_cleanup_kasp_ dnssec_kasp_t *kasp = get_kasp();
+	_cleanup_kasp_ dnssec_kasp_t *kasp = get_zone_kasp(config.name);
 	if (!kasp) {
 		return 1;
 	}
@@ -1144,7 +1153,7 @@ static int cmd_zone_key_set(int argc, char *argv[])
 
 	// update the key
 
-	_cleanup_kasp_ dnssec_kasp_t *kasp = get_kasp();
+	_cleanup_kasp_ dnssec_kasp_t *kasp = get_zone_kasp(zone_name);
 	if (!kasp) {
 		return 1;
 	}
@@ -1188,7 +1197,7 @@ static int cmd_zone_key_import(int argc, char *argv[])
 	char *zone_name = argv[0];
 	char *input_file = argv[1];
 
-	_cleanup_kasp_ dnssec_kasp_t *kasp = get_kasp();
+	_cleanup_kasp_ dnssec_kasp_t *kasp = get_zone_kasp(zone_name);
 	if (!kasp) {
 		return 1;
 	}
@@ -1275,7 +1284,7 @@ static int cmd_zone_set(int argc, char *argv[])
 
 	char *zone_name = argv[0];
 
-	_cleanup_kasp_ dnssec_kasp_t *kasp = get_kasp();
+	_cleanup_kasp_ dnssec_kasp_t *kasp = get_zone_kasp(zone_name);
 	if (!kasp) {
 		return 1;
 	}
@@ -1313,16 +1322,22 @@ static int cmd_zone_set(int argc, char *argv[])
 
 static int cmd_zone(int argc, char *argv[])
 {
-	static const command_t commands[] = {
+	static command_t commands[] = {
+		{ "key",    cmd_zone_key },
+		// legacy operations
 		{ "add",    cmd_zone_add },
 		{ "list",   cmd_zone_list },
 		{ "remove", cmd_zone_remove },
 		{ "show",   cmd_zone_show },
-		{ "key",    cmd_zone_key },
 		{ "set",    cmd_zone_set },
 		{ NULL }
 	};
 
+	// disable legacy operations
+	if (!global.legacy) {
+		commands[1].name = NULL;
+	}
+
 	return subcommand(commands, argc, argv);
 }
 
@@ -1800,15 +1815,24 @@ int main(int argc, char *argv[])
 	// global options
 
 	static const struct option opts[] = {
+		{ "config",  required_argument, NULL, 'c' },
+		{ "confdb",  required_argument, NULL, 'C' },
 		{ "dir",     required_argument, NULL, 'd' },
 		{ "help",    no_argument,       NULL, 'h' },
+		{ "legacy",  no_argument,       NULL, 'l' },
 		{ "version", no_argument,       NULL, 'V' },
 		{ NULL }
 	};
 
 	int c = 0;
-	while (c = getopt_long(argc, argv, "+", opts, NULL), c != -1) {
+	while (c = getopt_long(argc, argv, "+c:C:d:hlV", opts, NULL), c != -1) {
 		switch (c) {
+		case 'c':
+			global.config = optarg;
+			break;
+		case 'C':
+			global.confdb = optarg;
+			break;
 		case 'd':
 			free(global.kasp_dir);
 			global.kasp_dir = strdup(optarg);
@@ -1817,50 +1841,48 @@ int main(int argc, char *argv[])
 			print_help();
 			exit_code = 0;
 			goto failed;
+		case 'l':
+			global.legacy = true;
+			break;
 		case 'V':
 			print_version();
 			exit_code = 0;
 			goto failed;
-		case '?':
-			goto failed;
 		default:
-			assert_unreachable();
 			goto failed;
 		}
 	}
 
 	// global configuration
 
-	if (global.kasp_dir == NULL) {
-		char *env = getenv("KEYMGR_DIR");
-		if (env) {
-			global.kasp_dir = strdup(env);
-		} else {
-			global.kasp_dir = getcwd(NULL, 0);
-		}
+	int r = options_init(&global);
+	if (r != DNSSEC_EOK) {
+		goto failed;
 	}
 
-	assert(global.kasp_dir);
-
 	// subcommands
 
-	static const command_t commands[] = {
-		{ "init",     cmd_init },
+	static command_t commands[] = {
+		{ "tsig",     cmd_tsig },
 		{ "zone",     cmd_zone },
+		// legacy operations
+		{ "init",     cmd_init },
 		{ "policy",   cmd_policy },
 		{ "keystore", cmd_keystore },
-		{ "tsig",     cmd_tsig },
 		{ NULL }
 	};
 
-	dnssec_crypto_init();
+	// disable legacy operations
+	if (!global.legacy) {
+		commands[2].name = NULL;
+	}
 
+	dnssec_crypto_init();
 	exit_code = subcommand(commands, argc - optind, argv + optind);
-
-failed:
 	dnssec_crypto_cleanup();
 
-	free(global.kasp_dir);
+failed:
+	options_cleanup(&global);
 
 	return exit_code;
 }
diff --git a/src/utils/keymgr/options.c b/src/utils/keymgr/options.c
new file mode 100644
index 0000000000000000000000000000000000000000..370e4bdf27505cdee651bdba93d4a150d1fb6507
--- /dev/null
+++ b/src/utils/keymgr/options.c
@@ -0,0 +1,179 @@
+/*  Copyright (C) 2016 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <dnssec/error.h>
+#include "knot/conf/conf.h"
+#include "knot/dnssec/context.h"
+#include "options.h"
+#include "shared/print.h"
+
+int options_init(options_t *options)
+{
+	if (options == NULL) {
+		return DNSSEC_EINVAL;
+	}
+
+	if (options->kasp_dir == NULL) {
+		char *env = getenv("KEYMGR_DIR");
+		if (env != NULL) {
+			options->kasp_dir = strdup(env);
+		}
+	}
+
+	if (options->legacy) {
+		if (options->kasp_dir == NULL) {
+			options->kasp_dir = getcwd(NULL, 0);
+		}
+		return DNSSEC_EOK;
+	}
+
+	if (options->config != NULL && options->confdb != NULL) {
+		error("Ambiguous configuration source.");
+		return DNSSEC_EINVAL;
+	}
+
+	// Choose the optimal config source.
+	struct stat st;
+	bool import = false;
+	if (options->confdb != NULL) {
+		import = false;
+	} else if (options->config != NULL) {
+		import = true;
+	} else if (stat(CONF_DEFAULT_DBDIR, &st) == 0) {
+		import = false;
+		options->confdb = CONF_DEFAULT_DBDIR;
+	} else if (stat(CONF_DEFAULT_FILE, &st) == 0) {
+		import = true;
+		options->config = CONF_DEFAULT_FILE;
+	} else if (options->kasp_dir == NULL) {
+		options->kasp_dir = getcwd(NULL, 0);
+	}
+
+	// Prepare config flags.
+	conf_flag_t flags = CONF_FNOHOSTNAME;
+	if (options->confdb != NULL) {
+		flags |= CONF_FREADONLY;
+	}
+
+	// Open confdb.
+	conf_t *new_conf = NULL;
+	if (conf_new(&new_conf, conf_scheme, options->confdb, flags) != KNOT_EOK) {
+		error("Failed to open configuration database '%s'.",
+		      (options->confdb != NULL) ? options->confdb : "");
+		return DNSSEC_EINVAL;
+	}
+
+	// Import the config file.
+	if (import) {
+		if (conf_import(new_conf, options->config, true) != KNOT_EOK) {
+			error("Failed to open configuration file '%s'.",
+			      options->config);
+			conf_free(new_conf);
+			return DNSSEC_EINVAL;
+		}
+	}
+
+	// Update to the new config.
+	conf_update(new_conf);
+
+	return DNSSEC_EOK;
+}
+
+void options_cleanup(options_t *options)
+{
+	if (options == NULL) {
+		return;
+	}
+
+	if (!options->legacy) {
+		conf_update(NULL);
+	}
+
+	free(options->kasp_dir);
+}
+
+int options_zone_kasp_path(options_t *options, const char *zone_name)
+{
+	if (options == NULL) {
+		return DNSSEC_EINVAL;
+	}
+
+	if (options->kasp_dir != NULL) {
+		return DNSSEC_EOK;
+	}
+
+	if (options->legacy || zone_name == NULL) {
+		return DNSSEC_EINVAL;
+	}
+
+	uint8_t buff[KNOT_DNAME_MAXLEN];
+
+	knot_dname_t *zone = knot_dname_from_str(buff, zone_name, sizeof(buff));
+	if (zone == NULL || knot_dname_to_lower(zone) != KNOT_EOK) {
+		error("Invalid zone name.");
+		return DNSSEC_EINVAL;
+	}
+
+	// Check if such a zone is configured.
+	if (!conf_rawid_exists(conf(), C_ZONE, zone, knot_dname_size(zone))) {
+		error("Zone not configured.");
+		return DNSSEC_EINVAL;
+	}
+
+	conf_val_t val = conf_zone_get(conf(), C_STORAGE, zone);
+	char *storage = conf_abs_path(&val, NULL);
+	val = conf_zone_get(conf(), C_KASP_DB, zone);
+	options->kasp_dir = conf_abs_path(&val, storage);
+	free(storage);
+	if (options->kasp_dir == NULL) {
+		return DNSSEC_EINVAL;
+	}
+
+	return DNSSEC_EOK;
+}
+
+int options_zone_kasp_init(options_t *options, const char *zone_name,
+                           dnssec_kasp_t **kasp)
+{
+	if (options == NULL) {
+		return DNSSEC_EINVAL;
+	}
+
+	if (options->legacy) {
+		dnssec_kasp_init_dir(kasp);
+		return DNSSEC_EOK;
+	}
+
+	if (zone_name == NULL || kasp == NULL) {
+		return DNSSEC_EINVAL;
+	}
+
+	kdnssec_ctx_t ctx = {
+		.legacy = options->legacy
+	};
+
+	int ret = kdnssec_kasp_init(&ctx, options->kasp_dir, zone_name);
+	kdnssec_ctx_deinit(&ctx);
+	if (ret != DNSSEC_EOK) {
+		error("Failed to initialize KASP directory '%s'.", options->kasp_dir);
+		return ret;
+	}
+
+	return kdnssec_kasp(kasp, options->legacy);
+}
diff --git a/src/utils/keymgr/options.h b/src/utils/keymgr/options.h
new file mode 100644
index 0000000000000000000000000000000000000000..41894b986a00eb9a6b019895e68c1719159ea454
--- /dev/null
+++ b/src/utils/keymgr/options.h
@@ -0,0 +1,37 @@
+/*  Copyright (C) 2016 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 <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include "knot/dnssec/context.h"
+
+struct options {
+	bool legacy;
+	char *kasp_dir;
+	char *config;
+	char *confdb;
+};
+
+typedef struct options options_t;
+
+int options_init(options_t *options);
+
+void options_cleanup(options_t *options);
+
+int options_zone_kasp_path(options_t *options, const char *zone_name);
+
+int options_zone_kasp_init(options_t *options, const char *zone_name,
+                           dnssec_kasp_t **kasp);