diff --git a/doc/man/knotc.8in b/doc/man/knotc.8in
index 557f2aa6a26572ac283421a2fc6773fe4e569db1..79e8515ec88d5642e594e71bf2dfb7a9087e306a 100644
--- a/doc/man/knotc.8in
+++ b/doc/man/knotc.8in
@@ -38,167 +38,127 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
 .INDENT 0.0
 .TP
 \fB\-c\fP, \fB\-\-config\fP \fIfile\fP
-Use a textual configuration file (default is \fB@conf_dir@/knot.conf\fP).
+Use a textual configuration file (default is \fB@config_dir@/knot.conf\fP).
 .TP
 \fB\-C\fP, \fB\-\-confdb\fP \fIdirectory\fP
-Use a binary configuration database.
+Use a binary configuration database directory (default is \fB@storage_dir@/confdb\fP).
+The default configuration database, if exists, has a preference to the default
+configuration file.
 .TP
-\fB\-s\fP, \fB\-\-server\fP \fIserver\fP
-Remote UNIX socket/IP address (default is \fB@run_dir@/knot.sock\fP).
-.TP
-\fB\-p\fP, \fB\-\-port\fP \fIport\fP
-Remote server port (only for IP).
-.TP
-\fB\-y\fP, \fB\-\-key\fP [\fIalg\fP:]\fIname\fP:\fIkey\fP
-Use the TSIG key specified on the command line (default algorithm is hmac\-md5).
-.TP
-\fB\-k\fP, \fB\-\-keyfile\fP \fIfile\fP
-Use the TSIG key stored in a file \fIfile\fP to authenticate the request. The
-file must contain the key in the same format, which is accepted by the
-\fB\-y\fP option.
+\fB\-s\fP, \fB\-\-socket\fP \fIpath\fP
+Use a remote control UNIX socket path (default is \fB@run_dir@/knot.sock\fP).
 .TP
 \fB\-f\fP, \fB\-\-force\fP
-Force operation. Overrides some checks.
+Forced operation. Overrides some checks.
 .TP
 \fB\-v\fP, \fB\-\-verbose\fP
-Verbose mode. Print additional runtime information.
+Enable debug output.
+.TP
+\fB\-h\fP, \fB\-\-help\fP
+Print the program help.
 .TP
 \fB\-V\fP, \fB\-\-version\fP
 Print the program version.
-.TP
-\fB\-h\fP, \fB\-\-help\fP
-Print help and usage.
 .UNINDENT
 .SS Actions
-.sp
-If the optional \fIzone\fP argument is not specified, the command is applied to all
-zones.
-Configuration \fIitem\fP is in the \fIsection\fP[\fB[\fP\fIid\fP\fB]\fP][\fB\&.\fP\fIitem\fP]
-format.
 .INDENT 0.0
 .TP
+\fBstatus\fP
+Check if the server is running.
+.TP
 \fBstop\fP
-Stop server (no\-op if not running).
+Stop the server if running.
 .TP
-\fBreload\fP [\fIzone\fP\&...]
-Reload particular zones or reload the whole configuration and changed zones.
+\fBreload\fP
+Reload the server configuration.
 .TP
-\fBflush\fP [\fIzone\fP\&...]
-Flush journal and update zone files.
+\fBzone\-check\fP [\fIzone\fP\&...]
+Check the zone. (*)
 .TP
-\fBstatus\fP
-Check if server is running.
+\fBzone\-memstats\fP [\fIzone\fP\&...]
+Estimate memory use for the zone. (*)
+.TP
+\fBzone\-status\fP [\fIzone\fP\&...]
+Show the status of the zone. (*)
 .TP
-\fBzonestatus\fP [\fIzone\fP\&...]
-Show the status of listed zones.
+\fBzone\-reload\fP [\fIzone\fP\&...]
+Trigger a zone reload.
 .TP
-\fBrefresh\fP [\fIzone\fP\&...]
-Refresh slave zones. The \fB\-f\fP flag forces re\-transfer (zones must be specified).
+\fBzone\-refresh\fP [\fIzone\fP\&...]
+Trigger a zone refresh (if slave).
 .TP
-\fBcheckconf\fP
-Check the current configuration.
+\fBzone\-retransfer\fP [\fIzone\fP\&...]
+Trigger a zone retransfer (if slave).
 .TP
-\fBcheckzone\fP [\fIzone\fP\&...]
-Check zones.
+\fBzone\-flush\fP [\fIzone\fP\&...]
+Trigger a zone journal flush into the zone file.
 .TP
-\fBmemstats\fP [\fIzone\fP\&...]
-Estimate memory consumption for zones.
+\fBzone\-sing\fP [\fIzone\fP\&...]
+Trigger a zone resign (if enabled).
 .TP
-\fBsignzone\fP \fIzone\fP\&...
-Re\-sign the zone (drop all existing signatures and create new ones).
+\fBconf\-check\fP
+Check the server configuration. (*)
 .TP
 \fBconf\-import\fP \fIfilename\fP
-Offline import of the configuration DB from a file. This is a
-potentially dangerous operation so the \fB\-f\fP flag is required. Also the
-destination configuration DB must be specified via \fB\-C\fP\&. Ensure the server
-is not running!
+Import a config file into the confdb. Ensure the server is not accessing
+the confdb! (*)
 .TP
 \fBconf\-export\fP \fIfilename\fP
-Export the configuration DB to a file. If no source configuration DB is
-specified, the temporary DB, corresponding to textual configuration file, is
-used.
+Export the confdb into a config file. (*)
 .TP
-\fBconf\-desc\fP [\fIsection\fP]
-Get the configuration section items list. If no section is specified,
-the list of sections is returned.
+\fBconf\-list\fP [\fIitem\fP]
+List the confdb sections or section items.
 .TP
 \fBconf\-read\fP [\fIitem\fP]
-Read from the current configuration DB.
+Read the item from the active confdb.
 .TP
 \fBconf\-begin\fP
-Begin a writing configuration DB transaction. Only one transaction can be
-opened at a time.
+Begin a writing confdb transaction. Only one transaction can be opened at a time.
 .TP
 \fBconf\-commit\fP
-Commit the current writing configuration DB transaction.
+Commit the confdb transaction.
 .TP
 \fBconf\-abort\fP
-Abort the current writing configuration DB transaction.
+Rollback the confdb transaction.
 .TP
 \fBconf\-diff\fP [\fIitem\fP]
-Get the difference between the active writing transaction and the current
-configuration DB. Requires active writing configuration DB transaction.
+Get the item difference in the transaction.
 .TP
 \fBconf\-get\fP [\fIitem\fP]
-Read from the active writing configuration DB transaction.
-Requires active writing configuration DB transaction.
+Get the item data from the transaction.
 .TP
 \fBconf\-set\fP \fIitem\fP [\fIdata\fP\&...]
-Write to the active writing configuration DB transaction.
-Requires active writing configuration DB transaction.
+Set the item data in the transaction.
 .TP
 \fBconf\-unset\fP [\fIitem\fP] [\fIdata\fP\&...]
-Delete from the active writing configuration DB transaction.
-Requires active writing configuration DB transaction.
-.UNINDENT
-.SH EXAMPLES
-.SS Setup a key file for remote control
-.INDENT 0.0
-.INDENT 3.5
-.sp
-.nf
-.ft C
-$ keymgr tsig generate knotc\-key > knotc\-key.conf
-.ft P
-.fi
-.UNINDENT
+Unset the item data in the transaction.
 .UNINDENT
+.SH NOTE
 .sp
-The generated key file contains a key in the server configuration format and
-thus can be directly included into the server configuration file.
-.sp
-Knot DNS utilities accept one\-line format which is included in the generated
-key file on the first line as a comment. It can be extracted easily:
-.INDENT 0.0
-.INDENT 3.5
+Empty \fIzone\fP parameter means all zones.
 .sp
-.nf
-.ft C
-$ head \-1 knotc\-key.conf | sed \(aqs/^#\es*//\(aq > knotc.key
-.ft P
-.fi
-.UNINDENT
-.UNINDENT
+Type \fIitem\fP parameter in the form of \fIsection\fP[\fB[\fP\fIid\fP\fB]\fP][\fB\&.\fP\fIname\fP].
 .sp
-Make sure the key file can be read only by the owner for security reasons.
-.SS Reload server remotely
+(*) indicates a local operation requiring a configuration specified.
+.SH EXAMPLES
+.SS Reload the whole server configuration
 .INDENT 0.0
 .INDENT 3.5
 .sp
 .nf
 .ft C
-$ knotc \-s 127.0.0.1 \-k knotc.key reload
+$ knotc reload
 .ft P
 .fi
 .UNINDENT
 .UNINDENT
-.SS Flush all zones locally
+.SS Flush the example.com and example.eu zones
 .INDENT 0.0
 .INDENT 3.5
 .sp
 .nf
 .ft C
-$ knotc \-c knot.conf flush
+$ knotc zone\-flush example.com example.eu
 .ft P
 .fi
 .UNINDENT
diff --git a/doc/man/knotd.8in b/doc/man/knotd.8in
index 4a72710c602ed00c6877135f9d05ba20a67e880b..19926353f97782f814da07b44f91ad11fd854c97 100644
--- a/doc/man/knotd.8in
+++ b/doc/man/knotd.8in
@@ -41,7 +41,9 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
 Use a textual configuration file (default is \fB@config_dir@/knot.conf\fP).
 .TP
 \fB\-C\fP, \fB\-\-confdb\fP \fIdirectory\fP
-Use a binary configuration database directory.
+Use a binary configuration database directory (default is \fB@storage_dir@/confdb\fP).
+The default configuration database, if exists, has a preference to the default
+configuration file.
 .TP
 \fB\-d\fP, \fB\-\-daemonize\fP [\fIdirectory\fP]
 Run the server as a daemon. New root directory may be specified
diff --git a/doc/man_knotc.rst b/doc/man_knotc.rst
index 087471d0493c7f04172388ced04b9a662822b776..3451ac34e5179dd6973f1427371e21566ea30819 100644
--- a/doc/man_knotc.rst
+++ b/doc/man_knotc.rst
@@ -15,152 +15,131 @@ Parameters
 ..........
 
 **-c**, **--config** *file*
-  Use a textual configuration file (default is :file:`@conf_dir@/knot.conf`).
+  Use a textual configuration file (default is :file:`@config_dir@/knot.conf`).
 
 **-C**, **--confdb** *directory*
-  Use a binary configuration database.
+  Use a binary configuration database directory (default is :file:`@storage_dir@/confdb`).
+  The default configuration database, if exists, has a preference to the default
+  configuration file.
 
-**-s**, **--server** *server*
-  Remote UNIX socket/IP address (default is :file:`@run_dir@/knot.sock`).
-
-**-p**, **--port** *port*
-  Remote server port (only for IP).
-
-**-y**, **--key** [*alg*:]\ *name*:*key*
-  Use the TSIG key specified on the command line (default algorithm is hmac-md5).
-
-**-k**, **--keyfile** *file*
-  Use the TSIG key stored in a file *file* to authenticate the request. The
-  file must contain the key in the same format, which is accepted by the
-  **-y** option.
+**-s**, **--socket** *path*
+  Use a remote control UNIX socket path (default is :file:`@run_dir@/knot.sock`).
 
 **-f**, **--force**
-  Force operation. Overrides some checks.
+  Forced operation. Overrides some checks.
 
 **-v**, **--verbose**
-  Verbose mode. Print additional runtime information.
+  Enable debug output.
+
+**-h**, **--help**
+  Print the program help.
 
 **-V**, **--version**
   Print the program version.
 
-**-h**, **--help**
-  Print help and usage.
-
 Actions
 .......
 
-If the optional *zone* argument is not specified, the command is applied to all
-zones.
-Configuration *item* is in the *section*\ [**[**\ *id*\ **]**\ ][**.**\ *item*]
-format.
+**status**
+  Check if the server is running.
 
 **stop**
-  Stop server (no-op if not running).
+  Stop the server if running.
 
-**reload** [*zone*...]
-  Reload particular zones or reload the whole configuration and changed zones.
+**reload**
+  Reload the server configuration.
 
-**flush** [*zone*...]
-  Flush journal and update zone files.
 
-**status**
-  Check if server is running.
+**zone-check** [*zone*...]
+  Check the zone. (*)
+
+**zone-memstats** [*zone*...]
+  Estimate memory use for the zone. (*)
+
+**zone-status** [*zone*...]
+  Show the status of the zone. (*)
 
-**zonestatus** [*zone*...]
-  Show the status of listed zones.
+**zone-reload** [*zone*...]
+  Trigger a zone reload.
 
-**refresh** [*zone*...]
-  Refresh slave zones. The **-f** flag forces re-transfer (zones must be specified).
+**zone-refresh** [*zone*...]
+  Trigger a zone refresh (if slave).
 
-**checkconf**
-  Check the current configuration.
+**zone-retransfer** [*zone*...]
+  Trigger a zone retransfer (if slave).
 
-**checkzone** [*zone*...]
-  Check zones.
+**zone-flush** [*zone*...]
+  Trigger a zone journal flush into the zone file.
 
-**memstats** [*zone*...]
-  Estimate memory consumption for zones.
+**zone-sing** [*zone*...]
+  Trigger a zone resign (if enabled).
 
-**signzone** *zone*...
-  Re-sign the zone (drop all existing signatures and create new ones).
+
+**conf-init**
+  Initialize the confdb. (*)
+
+**conf-check**
+  Check the server configuration. (*)
 
 **conf-import** *filename*
-  Offline import of the configuration DB from a file. This is a
-  potentially dangerous operation so the **-f** flag is required. Also the
-  destination configuration DB must be specified via **-C**. Ensure the server
-  is not running!
+  Import a config file into the confdb. Ensure the server is not accessing
+  the confdb! (*)
 
 **conf-export** *filename*
-  Export the configuration DB to a file. If no source configuration DB is
-  specified, the temporary DB, corresponding to textual configuration file, is
-  used.
+  Export the confdb into a config file. (*)
 
-**conf-desc** [*section*]
-  Get the configuration section items list. If no section is specified,
-  the list of sections is returned.
+**conf-list** [*item*]
+  List the confdb sections or section items.
 
 **conf-read** [*item*]
-  Read from the current configuration DB.
+  Read the item from the active confdb.
 
 **conf-begin**
-  Begin a writing configuration DB transaction. Only one transaction can be
-  opened at a time.
+  Begin a writing confdb transaction. Only one transaction can be opened at a time.
 
 **conf-commit**
-  Commit the current writing configuration DB transaction.
+  Commit the confdb transaction.
 
 **conf-abort**
-  Abort the current writing configuration DB transaction.
+  Rollback the confdb transaction.
 
 **conf-diff** [*item*]
-  Get the difference between the active writing transaction and the current
-  configuration DB. Requires active writing configuration DB transaction.
+  Get the item difference in the transaction.
 
 **conf-get** [*item*]
-  Read from the active writing configuration DB transaction.
-  Requires active writing configuration DB transaction.
+  Get the item data from the transaction.
 
 **conf-set** *item* [*data*...]
-  Write to the active writing configuration DB transaction.
-  Requires active writing configuration DB transaction.
+  Set the item data in the transaction.
 
 **conf-unset** [*item*] [*data*...]
-  Delete from the active writing configuration DB transaction.
-  Requires active writing configuration DB transaction.
-
-Examples
---------
+  Unset the item data in the transaction.
 
-Setup a key file for remote control
-...................................
+Note
+----
 
-::
-
-  $ keymgr tsig generate knotc-key > knotc-key.conf
-
-The generated key file contains a key in the server configuration format and
-thus can be directly included into the server configuration file.
+Empty *zone* parameter means all zones.
 
-Knot DNS utilities accept one-line format which is included in the generated
-key file on the first line as a comment. It can be extracted easily::
+Type *item* parameter in the form of *section*\ [**[**\ *id*\ **]**\ ][**.**\ *name*].
 
-  $ head -1 knotc-key.conf | sed 's/^#\s*//' > knotc.key
+(*) indicates a local operation requiring a configuration specified.
 
-Make sure the key file can be read only by the owner for security reasons.
+Examples
+--------
 
-Reload server remotely
-......................
+Reload the whole server configuration
+.....................................
 
 ::
 
-  $ knotc -s 127.0.0.1 -k knotc.key reload
+  $ knotc reload
 
-Flush all zones locally
-.......................
+Flush the example.com and example.eu zones
+..........................................
 
 ::
 
-  $ knotc -c knot.conf flush
+  $ knotc zone-flush example.com example.eu
 
 Get the current server configuration
 ....................................
diff --git a/doc/man_knotd.rst b/doc/man_knotd.rst
index 9b8bfa7c6e5e37f1ed2940ebf3400e6edc9a7045..29212f9aba11ebc7e1ad9487af7d7b3b5a0e23ca 100644
--- a/doc/man_knotd.rst
+++ b/doc/man_knotd.rst
@@ -18,7 +18,9 @@ Parameters
   Use a textual configuration file (default is :file:`@config_dir@/knot.conf`).
 
 **-C**, **--confdb** *directory*
-  Use a binary configuration database directory.
+  Use a binary configuration database directory (default is :file:`@storage_dir@/confdb`).
+  The default configuration database, if exists, has a preference to the default
+  configuration file.
 
 **-d**, **--daemonize** [*directory*]
   Run the server as a daemon. New root directory may be specified
diff --git a/src/utils/knotc/main.c b/src/utils/knotc/main.c
index 2734b99ad526caf4c2b7d6da2f7944f24510227d..8791cb5d73eab896c69653e9585e74a2402cdcc0 100644
--- a/src/utils/knotc/main.c
+++ b/src/utils/knotc/main.c
@@ -18,114 +18,191 @@
 #include <stdio.h>
 #include <sys/stat.h>
 
-#include "contrib/sockaddr.h"
 #include "dnssec/crypto.h"
 #include "knot/common/log.h"
 #include "knot/conf/conf.h"
 #include "libknot/libknot.h"
 #include "utils/knotc/commands.h"
+#include "utils/common/params.h"
 
-/*! \brief Print help. */
-static void help(void)
+#define PROGRAM_NAME "knotc"
+
+static void print_help(void)
 {
-	printf("Usage: knotc [parameters] <action> [action_args]\n"
+	printf("Usage: %s [parameters] <action> [action_args]\n"
 	       "\n"
 	       "Parameters:\n"
-	       " -c, --config <file>                Select configuration file.\n"
-	       "                                     (default %s)\n"
-	       " -C, --confdb <dir>                 Select configuration database directory.\n"
-	       " -s, --socket <path>                Remote control UNIX socket.\n"
-	       "                                     (default %s)\n"
-	       " -f, --force                        Force operation - override some checks.\n"
-	       " -v, --verbose                      Verbose mode - additional runtime information.\n"
-	       " -V, --version                      Print %s server version.\n"
-	       " -h, --help                         Print help and usage.\n"
+	       " -c, --config <file>                  Use a textual configuration file.\n"
+	       "                                       (default %s)\n"
+	       " -C, --confdb <dir>                   Use a binary configuration database directory.\n"
+	       "                                       (default %s)\n"
+	       " -s, --socket <path>                  Use a remote control UNIX socket path.\n"
+	       "                                       (default %s)\n"
+	       " -f, --force                          Forced operation. Overrides some checks.\n"
+	       " -v, --verbose                        Enable debug output.\n"
+	       " -h, --help                           Print the program help.\n"
+	       " -V, --version                        Print the program version.\n"
 	       "\n"
 	       "Actions:\n",
-	       CONF_DEFAULT_FILE, RUN_DIR "/knot.sock", PACKAGE_NAME);
-	cmd_help_t *c = cmd_help_table;
-	while (c->name != NULL) {
-		printf(" %-13s %-20s %s\n", c->name, c->params, c->desc);
-		++c;
-	}
-	printf("\nThe item argument must be in the section[identifier].item format.\n");
-	printf("\nIf optional <zone> parameter is not specified, command is applied to all zones.\n\n");
+	       PROGRAM_NAME, CONF_DEFAULT_FILE, CONF_DEFAULT_DBDIR, RUN_DIR "/knot.sock");
+
+	for (const cmd_help_t *cmd = cmd_help_table; cmd->name != NULL; cmd++) {
+		printf(" %-15s %-20s %s\n", cmd->name, cmd->params, cmd->desc);
+	}
+
+	printf("\n"
+	       "Note:\n"
+	       " Empty <zone> parameter means all zones.\n"
+	       " Type <item> parameter in the form of <section>[<identifier>].<name>.\n"
+	       " (*) indicates a local operation requiring a configuration specified.\n");
+}
+
+static int set_config(const cmd_desc_t *desc, const char *confdb,
+                      const char *config, char *socket)
+{
+	if (config != NULL && confdb != NULL) {
+		log_error("ambiguous configuration source");
+		return KNOT_EINVAL;
+	}
+
+	/* Choose the optimal config source. */
+	struct stat st;
+	bool import = false;
+	if (desc->flags == CMD_CONF_FNONE && socket != NULL) {
+		import = false;
+		confdb = NULL;
+	} else if (confdb != NULL) {
+		import = false;
+	} else if (desc->flags == CMD_CONF_FWRITE) {
+		import = false;
+		confdb = CONF_DEFAULT_DBDIR;
+	} else if (config != NULL){
+		import = true;
+	} else if (stat(CONF_DEFAULT_DBDIR, &st) == 0) {
+		import = false;
+		confdb = CONF_DEFAULT_DBDIR;
+	} else if (stat(CONF_DEFAULT_FILE, &st) == 0) {
+		import = true;
+		config = CONF_DEFAULT_FILE;
+	} else if (desc->flags != CMD_CONF_FNONE) {
+		log_error("no configuration source available");
+		return KNOT_EINVAL;
+	}
+
+	const char *src = import ? config : confdb;
+	log_debug("%s '%s'", import ? "config" : "confdb",
+	          (src != NULL) ? src : "empty");
+
+	/* Prepare config flags. */
+	conf_flag_t conf_flags = CONF_FNONE;
+	if (confdb != NULL && !(desc->flags & CMD_CONF_FWRITE)) {
+		conf_flags |= CONF_FREADONLY;
+	}
+
+	/* Open confdb. */
+	conf_t *new_conf = NULL;
+	int ret = conf_new(&new_conf, conf_scheme, confdb, conf_flags);
+	if (ret != KNOT_EOK) {
+		log_error("failed to open configuration database '%s' (%s)",
+		          (confdb != NULL) ? confdb : "", knot_strerror(ret));
+		return ret;
+	}
+
+	/* Import the config file. */
+	if (import) {
+		ret = conf_import(new_conf, config, true);
+		if (ret != KNOT_EOK) {
+			log_error("failed to load configuration file '%s' (%s)",
+			          config, knot_strerror(ret));
+			conf_free(new_conf);
+			return ret;
+		}
+	}
+
+	/* Finalize the config (needed for conf check and cached items). */
+	ret = conf_post_open(new_conf);
+	if (ret != KNOT_EOK) {
+		log_error("failed to use configuration (%s)", knot_strerror(ret));
+		conf_free(new_conf);
+		return ret;
+	}
+
+	/* Update to the new config. */
+	conf_update(new_conf);
+
+	return KNOT_EOK;
 }
 
 int main(int argc, char **argv)
 {
-	/* Parse command line arguments */
-	int c = 0, li = 0, rc = 0;
-	unsigned flags = CMD_NONE;
-	const char *config_fn = CONF_DEFAULT_FILE;
-	const char *config_db = NULL;
+	cmd_flag_t flags = CMD_FNONE;
+	const char *config = NULL;
+	const char *confdb = NULL;
 	char *socket = NULL;
-
-	/* Initialize. */
-	log_init();
-	log_levels_set(LOG_SYSLOG, LOG_ANY, 0);
+	bool verbose = false;
 
 	/* Long options. */
 	struct option opts[] = {
-		{ "config",  required_argument, 0, 'c' },
-		{ "confdb",  required_argument, 0, 'C' },
-		{ "socket",  required_argument, 0, 's' },
-		{ "force",   no_argument,       0, 'f' },
-		{ "verbose", no_argument,       0, 'v' },
-		{ "help",    no_argument,       0, 'h' },
-		{ "version", no_argument,       0, 'V' },
+		{ "config",  required_argument, NULL, 'c' },
+		{ "confdb",  required_argument, NULL, 'C' },
+		{ "socket",  required_argument, NULL, 's' },
+		{ "force",   no_argument,       NULL, 'f' },
+		{ "verbose", no_argument,       NULL, 'v' },
+		{ "help",    no_argument,       NULL, 'h' },
+		{ "version", no_argument,       NULL, 'V' },
 		{ NULL }
 	};
 
-	while ((c = getopt_long(argc, argv, "s:fc:C:vVh", opts, &li)) != -1) {
-		switch (c) {
+	/* Parse command line arguments */
+	int opt = 0, li = 0;
+	while ((opt = getopt_long(argc, argv, "c:C:s:fvhV", opts, &li)) != -1) {
+		switch (opt) {
 		case 'c':
-			config_fn = optarg;
+			config = optarg;
 			break;
 		case 'C':
-			config_db = optarg;
+			confdb = optarg;
 			break;
 		case 's':
-			socket = strdup(optarg);
+			socket = optarg;
 			break;
 		case 'f':
-			flags |= CMD_FORCE;
+			flags |= CMD_FFORCE;
 			break;
 		case 'v':
-			log_levels_add(LOGT_STDOUT, LOG_ANY,
-			               LOG_MASK(LOG_INFO) | LOG_MASK(LOG_DEBUG));
+			verbose = true;
 			break;
-		case 'V':
-			rc = 0;
-			printf("%s, version %s\n", "Knot DNS", PACKAGE_VERSION);
-			goto exit;
 		case 'h':
-		case '?':
-			rc = 0;
-			help();
-			goto exit;
+			print_help();
+			return EXIT_SUCCESS;
+		case 'V':
+			print_version(PROGRAM_NAME);
+			return EXIT_SUCCESS;
 		default:
-			rc = 1;
-			help();
-			goto exit;
+			print_help();
+			return EXIT_FAILURE;
 		}
 	}
 
 	/* Check if there's at least one remaining non-option. */
 	if (argc - optind < 1) {
-		rc = 1;
-		help();
-		goto exit;
+		print_help();
+		return EXIT_FAILURE;
 	}
 
-	/* Check for existing config DB destination. */
-	struct stat st;
-	if (config_db != NULL && stat(config_db, &st) != 0) {
-		flags |= CMD_NOCONFDB;
+	/* Set up simplified logging just to stdout/stderr. */
+	log_init();
+	log_levels_set(LOGT_STDOUT, LOG_ANY, LOG_MASK(LOG_INFO) | LOG_MASK(LOG_NOTICE));
+	log_levels_set(LOGT_STDERR, LOG_ANY, LOG_UPTO(LOG_WARNING));
+	log_levels_set(LOGT_SYSLOG, LOG_ANY, 0);
+	log_flag_set(LOG_FNO_TIMESTAMP | LOG_FNO_INFO);
+	if (verbose) {
+		log_levels_add(LOGT_STDOUT, LOG_ANY, LOG_MASK(LOG_DEBUG));
 	}
 
+	/* Translate old command name. */
 	const char *command = argv[optind];
-	for (cmd_desc_old_t *desc = cmd_table_old; desc->old_name != NULL; desc++) {
+	for (const cmd_desc_old_t *desc = cmd_table_old; desc->old_name != NULL; desc++) {
 		if (strcmp(desc->old_name, command) == 0) {
 			log_notice("obsolete command '%s', using '%s' instead",
 			           desc->old_name, desc->new_name);
@@ -135,94 +212,56 @@ int main(int argc, char **argv)
 	}
 
 	/* Find requested command. */
-	cmd_desc_t *desc = cmd_table;
+	const cmd_desc_t *desc = cmd_table;
 	while (desc->name != NULL) {
 		if (strcmp(desc->name, command) == 0) {
 			break;
 		}
-		++desc;
+		desc++;
 	}
-
-	/* Command not found. */
 	if (desc->name == NULL) {
-		log_fatal("invalid command: '%s'", argv[optind]);
-		rc = 1;
-		goto exit;
+		log_error("invalid command '%s'", command);
+		log_close();
+		return EXIT_FAILURE;
 	}
 
-	/* Open configuration. */
-	conf_t *new_conf = NULL;
-	if (config_db == NULL) {
-		int ret = conf_new(&new_conf, conf_scheme, NULL, false);
-		if (ret != KNOT_EOK) {
-			log_fatal("failed to initialize configuration database "
-			          "(%s)", knot_strerror(ret));
-			rc = 1;
-			goto exit;
-		}
-
-		/* Import the configuration file. */
-		ret = conf_import(new_conf, config_fn, true);
-		if (ret != KNOT_EOK) {
-			log_fatal("failed to load configuration file (%s)",
-			          knot_strerror(ret));
-			conf_free(new_conf, false);
-			rc = 1;
-			goto exit;
-		}
-
-		new_conf->filename = strdup(config_fn);
-	} else {
-		/* Open configuration database. */
-		bool ronly = !(desc->flags & CMD_CONF_WRITE);
-		int ret = conf_new(&new_conf, conf_scheme, config_db, ronly);
-		if (ret != KNOT_EOK) {
-			log_fatal("failed to open configuration database '%s' "
-			          "(%s)", config_db, knot_strerror(ret));
-			rc = 1;
-			goto exit;
-		}
-	}
-
-	/* Run post-open config operations. */
-	int ret = conf_post_open(new_conf);
+	/* Set up the configuration */
+	int ret = set_config(desc, confdb, config, socket);
 	if (ret != KNOT_EOK) {
-		log_fatal("failed to use configuration (%s)", knot_strerror(ret));
-		conf_free(new_conf, false);
-		rc = 1;
-		goto exit;
+		log_close();
+		return EXIT_FAILURE;
 	}
 
-	/* Update to the new config. */
-	conf_update(new_conf);
+	/* Prepare command parameters. */
+	cmd_args_t args = {
+		socket,
+		argc - optind - 1,
+		argv + optind + 1,
+		flags
+	};
 
-	/* Get control socket path. */
+	/* Get the control socket from confdb if not specified. */
 	if (socket == NULL) {
 		conf_val_t listen_val = conf_get(conf(), C_CTL, C_LISTEN);
 		conf_val_t rundir_val = conf_get(conf(), C_SRV, C_RUNDIR);
 		char *rundir = conf_abs_path(&rundir_val, NULL);
-		socket = conf_abs_path(&listen_val, rundir);
+		args.socket = conf_abs_path(&listen_val, rundir);
 		free(rundir);
 	}
 
-	cmd_args_t args = {
-		socket,
-		argc - optind - 1,
-		argv + optind + 1,
-		flags,
-		config_db
-	};
+	log_debug("socket '%s'", (args.socket != NULL) ? args.socket : "");
 
-	/* Execute command. */
+	/* Execute the command. */
 	dnssec_crypto_init();
-	rc = desc->cmd(&args);
+	ret = desc->cmd(&args);
 	dnssec_crypto_cleanup();
 
-exit:
-	/* Finish */
-	conf_free(conf(), false);
+	/* Cleanup */
+	if (socket == NULL) {
+		free(args.socket);
+	}
+	conf_free(conf());
 	log_close();
-	free(socket);
 
-	return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+	return ret == KNOT_EOK ? EXIT_SUCCESS : EXIT_FAILURE;
 }