diff --git a/src/common/errcode.c b/src/common/errcode.c
index 681f96eac726b39e5183f319affe49eade6bec6d..c03b47321be38dd3957501f011b2c5582a35b748 100644
--- a/src/common/errcode.c
+++ b/src/common/errcode.c
@@ -74,6 +74,10 @@ const error_table_t knot_error_msgs[] = {
 	{ KNOT_ENOTSIG, "expected a TSIG or SIG(0)" },
 	{ KNOT_ELIMIT, "Exceeded response rate limit." },
 
+	/* Control states. */
+	{ KNOT_CTL_STOP, "Stopping server." },
+	{ KNOT_CTL_RESTART, "Restarting server." },
+
 	/* Network errors. */
 	{ KNOT_NET_EADDR, "Bad address or host name." },
 	{ KNOT_NET_ESOCKET, "Can't create socket." },
diff --git a/src/common/errcode.h b/src/common/errcode.h
index 3a653c5566afe1038ace603ec653ff20de2eee0e..400a10e07a4e43c4e5222dbf5db02ccc15e32259 100644
--- a/src/common/errcode.h
+++ b/src/common/errcode.h
@@ -87,6 +87,10 @@ enum knot_error {
 	KNOT_ENOTSIG,         /*!< Expected a TSIG or SIG(0). */
 	KNOT_ELIMIT,          /*!< Exceeded response rate limit. */
 
+	/* Control states. */
+	KNOT_CTL_STOP,        /*!< Stop requested. */
+	KNOT_CTL_RESTART,     /*!< Restart requested. */
+
 	/* Network errors. */
 	KNOT_NET_EADDR,
 	KNOT_NET_ESOCKET,
diff --git a/src/knot/ctl/knotc_main.c b/src/knot/ctl/knotc_main.c
index 3f6517a48c7eee33f86b47a054f25bbc95fc969a..e81feefb95a193061865c2201b9b2f6c9b2f456d 100644
--- a/src/knot/ctl/knotc_main.c
+++ b/src/knot/ctl/knotc_main.c
@@ -45,11 +45,6 @@
 #include "libknot/packet/response.h"
 #include "knot/zone/zone-load.h"
 
-/*! \brief Controller constants. */
-enum knotc_constants_t {
-	WAITPID_TIMEOUT = 120   /*!< \brief Timeout for waiting for process. */
-};
-
 /*! \brief Controller flags. */
 enum knotc_flag_t {
 	F_NULL = 0 << 0,
@@ -82,7 +77,6 @@ typedef struct knot_cmd {
 } knot_cmd_t;
 
 /* Forward decls. */
-static int cmd_start(int argc, char *argv[], unsigned flags);
 static int cmd_stop(int argc, char *argv[], unsigned flags);
 static int cmd_restart(int argc, char *argv[], unsigned flags);
 static int cmd_reload(int argc, char *argv[], unsigned flags);
@@ -95,9 +89,8 @@ static int cmd_checkzone(int argc, char *argv[], unsigned flags);
 
 /*! \brief Table of remote commands. */
 knot_cmd_t knot_cmd_tbl[] = {
-	{&cmd_start,      1, "start",      "",       "\t\tStart server (if not running)."},
-	{&cmd_stop,       1, "stop",       "",       "\t\tStop server."},
-	{&cmd_restart,    1, "restart",    "",       "\tRestart server."},
+	{&cmd_stop,       0, "stop",       "",       "\t\tStop server."},
+	{&cmd_restart,    0, "restart",    "",       "\tRestart server."},
 	{&cmd_reload,     0, "reload",     "",       "\tReload configuration and changed zones."},
 	{&cmd_refresh,    0, "refresh",    "[zone]", "\tRefresh slave zone (all if not specified)."},
 	{&cmd_flush,      0, "flush",      "",       "\t\tFlush journal and update zone files."},
@@ -122,7 +115,6 @@ void help(void)
 	       " -f, --force               \tForce operation - override some checks.\n"
 	       " -v, --verbose             \tVerbose mode - additional runtime information.\n"
 	       " -V, --version             \tPrint %s server version.\n"
-	       " -w, --wait                \tWait for the server to finish start/stop operations.\n"
 	       " -i, --interactive         \tInteractive mode (do not daemonize).\n"
 	       " -h, --help                \tPrint help and usage.\n",
 	       "127.0.0.1", REMOTE_DPORT, PACKAGE_NAME);
@@ -570,167 +562,22 @@ exit:
 	return rc;
 }
 
-static int cmd_start(int argc, char *argv[], unsigned flags)
-{
-	/* Check config. */
-	if (has_flag(flags, F_NOCONF)) {
-		log_server_error("Couldn't parse config file, refusing to "
-		                 "continue.\n");
-		return 1;
-	}
-
-	/* Fetch PID. */
-	char *pidfile = pid_filename();
-	pid_t pid = pid_read(pidfile);
-	log_server_info("Starting server...\n");
-
-	/* Prevent concurrent daemon launch. */
-	int rc = 0;
-	struct stat st;
-	int is_pidf = 0;
-
-	/* Check PID. */
-	if (pid > 0 && pid_running(pid)) {
-		log_server_error("Server PID found, already running.\n");
-		is_pidf = 1;
-	} else if (stat(pidfile, &st) == 0) {
-		log_server_warning("PID file '%s' exists, another process "
-		                   "is starting or PID file is stale.\n",
-		                   pidfile);
-		is_pidf = 1;
-	}
-	if (is_pidf) {
-		if (!has_flag(flags, F_FORCE)) {
-			free(pidfile);
-			return 1;
-		} else {
-			log_server_info("Forcing server start.\n");
-			pid_remove(pidfile);
-		}
-	}
-
-	/* Prepare command */
-	const char *cfg = conf()->filename;
-	size_t args_c = 6;
-	const char *args[] = {
-		PROJECT_EXEC,
-		has_flag(flags, F_INTERACTIVE) ? "" : "-d",
-		cfg ? "-c" : "",
-		cfg ? cfg : "",
-		has_flag(flags, F_VERBOSE) ? "-v" : "",
-		argc > 0 ? argv[0] : ""
-	};
-
-	/* Execute command */
-	if (has_flag(flags, F_INTERACTIVE)) {
-		log_server_info("Running in interactive mode.\n");
-	} else {
-		log_server_info("Starting as daemon, 'stdout' and 'stderr' log "
-		                "sinks will be closed.\n");
-	}
-	fflush(stderr);
-	fflush(stdout);
-
-	if ((rc = cmd_exec(args, args_c)) < 0) {
-		rc = 1;
-	}
-	fflush(stderr);
-	fflush(stdout);
-
-	/* Wait for finish */
-	if (has_flag(flags, F_WAIT) && !has_flag(flags, F_INTERACTIVE)) {
-		if (has_flag(flags, F_VERBOSE)) {
-			log_server_info("Waiting for server to load.\n");
-		}
-
-		/* Periodically read pidfile and wait for valid result. */
-		pid = 0;
-		while (pid == 0 || !pid_running(pid)) {
-			pid = pid_read(pidfile);
-			struct timeval tv;
-			tv.tv_sec = 0;
-			tv.tv_usec = 500 * 1000;
-			select(0, 0, 0, 0, &tv);
-		}
-	}
-
-	free(pidfile);
-	return rc;
-}
-
 static int cmd_stop(int argc, char *argv[], unsigned flags)
 {
 	UNUSED(argc);
 	UNUSED(argv);
+	UNUSED(flags);
 
-	/* Check config. */
-	if (has_flag(flags, F_NOCONF)) {
-		log_server_error("Couldn't parse config file, refusing to "
-		                 "continue.\n");
-		return 1;
-	}
-
-	/* Fetch PID. */
-	char *pidfile = pid_filename();
-	pid_t pid = pid_read(pidfile);
-	int rc = 0;
-	struct stat st;
-
-	/* Check for non-existent PID file. */
-	int has_pidf = (stat(pidfile, &st) == 0);
-	if(has_pidf && pid <= 0) {
-		log_server_warning("Empty PID file '%s' exists, daemon process "
-		                   "is starting or PID file is stale.\n",
-		                   pidfile);
-		free(pidfile);
-		return 1;
-	} else if (pid <= 0 || !pid_running(pid)) {
-		log_server_warning("Server PID not found, "
-		                   "probably not running.\n");
-		if (!has_flag(flags, F_FORCE)) {
-			free(pidfile);
-			return 1;
-		} else {
-			log_server_info("Forcing server stop.\n");
-		}
-	}
-
-	/* Stop */
-	log_server_info("Stopping server...\n");
-	if (kill(pid, SIGTERM) < 0) {
-		pid_remove(pidfile);
-		rc = 1;
-	}
-
-
-	/* Wait for finish */
-	if (rc == 0 && has_flag(flags, F_WAIT)) {
-		log_server_info("Waiting for server to finish.\n");
-		while (pid_running(pid)) {
-			struct timeval tv;
-			tv.tv_sec = 0;
-			tv.tv_usec = 500 * 1000;
-			select(0, 0, 0, 0, &tv);
-			pid = pid_read(pidfile); /* Update */
-		}
-	}
-
-	return rc;
+	return cmd_remote("stop", KNOT_RRTYPE_TXT, 0, NULL);
 }
 
 static int cmd_restart(int argc, char *argv[], unsigned flags)
 {
-	/* Check config. */
-	if (has_flag(flags, F_NOCONF)) {
-		log_server_error("Couldn't parse config file, refusing to "
-		                 "continue.\n");
-		return 1;
-	}
+	UNUSED(argc);
+	UNUSED(argv);
+	UNUSED(flags);
 
-	int rc = 0;
-	rc |= cmd_stop(argc, argv, flags | F_WAIT);
-	rc |= cmd_start(argc, argv, flags);
-	return rc;
+	return cmd_remote("restart", KNOT_RRTYPE_TXT, 0, NULL);
 }
 
 static int cmd_reload(int argc, char *argv[], unsigned flags)
diff --git a/src/knot/ctl/process.c b/src/knot/ctl/process.c
index cf558772d91f882f5bfe50159f5ec4475ac9d7d5..c0358cfedc6fd7357d1f54b37d99b96970ee2f9c 100644
--- a/src/knot/ctl/process.c
+++ b/src/knot/ctl/process.c
@@ -176,63 +176,3 @@ int proc_update_privileges(int uid, int gid)
 	free(lfile);
 	return ret;
 }
-
-pid_t pid_wait(pid_t proc, int *rc)
-{
-	/* Wait for finish. */
-	sigset_t newset;
-	sigfillset(&newset);
-	sigprocmask(SIG_BLOCK, &newset, 0);
-	proc = waitpid(proc, rc, 0);
-	sigprocmask(SIG_UNBLOCK, &newset, 0);
-	return proc;
-}
-
-
-pid_t pid_start(const char *argv[], int argc, int drop_privs)
-{
-	pid_t chproc = fork();
-	if (chproc == 0) {
-
-		/* Alter privileges. */
-		if (drop_privs) {
-			proc_update_privileges(conf()->uid, conf()->gid);
-		}
-
-		/* Duplicate, it doesn't run from stack address anyway. */
-		char **args = malloc((argc + 1) * sizeof(char*));
-		memset(args, 0, (argc + 1) * sizeof(char*));
-		int ci = 0;
-		for (int i = 0; i < argc; ++i) {
-			if (strlen(argv[i]) > 0) {
-				args[ci++] = strdup(argv[i]);
-			}
-		}
-		args[ci] = 0;
-
-		/* Execute command. */
-		fflush(stdout);
-		fflush(stderr);
-		execvp(args[0], args);
-
-		/* Execute failed. */
-		log_server_error("Failed to run executable '%s'\n", args[0]);
-		for (int i = 0; i < argc; ++i) {
-			free(args[i]);
-		}
-		free(args);
-
-		exit(1);
-		return -1;
-	}
-
-	return chproc;
-}
-
-int cmd_exec(const char *argv[], int argc)
-{
-	int ret = 0;
-	pid_t proc = pid_start(argv, argc, 0);
-	pid_wait(proc, &ret);
-	return ret;
-}
diff --git a/src/knot/ctl/process.h b/src/knot/ctl/process.h
index d5aa191db4b04e92cb9fd80b5b350169dac7ded8..1257417b1b41b6654632719ebc6f15451cdc12a3 100644
--- a/src/knot/ctl/process.h
+++ b/src/knot/ctl/process.h
@@ -94,39 +94,6 @@ int pid_running(pid_t pid);
  */
 int proc_update_privileges(int uid, int gid);
 
-/*!
- * \brief Wait for process to finish.
- *
- * \param proc Process ID.
- * \param rc Destination for return code.
- *
- * \return PID of finished process.
- */
-pid_t pid_wait(pid_t proc, int *rc);
-
-/*!
- * \brief Start command with given parameters.
- *
- * Set drop_privs = 1 to change privileges according to conf().
- *
- * \param argv Parameter list.
- * \param argc Parameter count.
- * \param drop_privs Set to 1 to alter privileges.
- *
- * \return PID of started process.
- */
-pid_t pid_start(const char *argv[], int argc, int drop_privs);
-
-/*!
- * \brief Execute command and wait for finish.
- *
- * \param argv Parameter list.
- * \param argc Parameter count.
- *
- * \return Return code.
- */
-int cmd_exec(const char *argv[], int argc);
-
 #endif // _KNOTD_PROCESS_H_
 
 /*! @} */
diff --git a/src/knot/ctl/remote.c b/src/knot/ctl/remote.c
index bc7698f4bb14b41db36ab87347821c34f1883598..0f273729b5e669b0cd53b6bfff0d248284b8a3d7 100644
--- a/src/knot/ctl/remote.c
+++ b/src/knot/ctl/remote.c
@@ -58,6 +58,8 @@ typedef struct remote_cmd_t {
 } remote_cmd_t;
 
 /* Forward decls. */
+static int remote_c_stop(server_t *s, remote_cmdargs_t* a);
+static int remote_c_restart(server_t *s, remote_cmdargs_t* a);
 static int remote_c_reload(server_t *s, remote_cmdargs_t* a);
 static int remote_c_refresh(server_t *s, remote_cmdargs_t* a);
 static int remote_c_status(server_t *s, remote_cmdargs_t* a);
@@ -66,6 +68,8 @@ static int remote_c_flush(server_t *s, remote_cmdargs_t* a);
 
 /*! \brief Table of remote commands. */
 struct remote_cmd_t remote_cmd_tbl[] = {
+	{ "stop",      &remote_c_stop },
+	{ "restart",   &remote_c_restart },
 	{ "reload",    &remote_c_reload },
 	{ "refresh",   &remote_c_refresh },
 	{ "status",    &remote_c_status },
@@ -159,6 +163,31 @@ static int remote_zone_flush(server_t *s, const knot_zone_t *z)
 	return KNOT_EOK;
 }
 
+/*!
+ * \brief Remote command 'stop' handler.
+ *
+ * QNAME: stop
+ * DATA: NULL
+ */
+static int remote_c_stop(server_t *s, remote_cmdargs_t* a)
+{
+	UNUSED(a);
+	UNUSED(s);
+	return KNOT_CTL_STOP;
+}
+
+/*!
+ * \brief Remote command 'restart' handler.
+ *
+ * QNAME: restart
+ * DATA: NULL
+ */
+static int remote_c_restart(server_t *s, remote_cmdargs_t* a)
+{
+	UNUSED(a);
+	return KNOT_CTL_RESTART;
+}
+
 /*!
  * \brief Remote command 'reload' handler.
  *
@@ -646,7 +675,7 @@ int remote_process(server_t *s, int r, uint8_t* buf, size_t buflen)
 		}
 
 		/* Answer packet. */
-		remote_answer(c, s, pkt, buf, buflen);
+		ret = remote_answer(c, s, pkt, buf, buflen);
 	}
 
 	knot_packet_free(&pkt);
diff --git a/src/knot/main.c b/src/knot/main.c
index c06c6990cd02a60c8a78e017290353c2503a844d..10d6ec3906c73a2b67e278f766326e9c9bde816e 100644
--- a/src/knot/main.c
+++ b/src/knot/main.c
@@ -40,6 +40,7 @@
 
 /* Signal flags. */
 static volatile short sig_req_stop = 0;
+static volatile short sig_req_rst = 0;
 static volatile short sig_req_reload = 0;
 static volatile short sig_req_refresh = 0;
 static volatile short sig_stopping = 0;
@@ -371,6 +372,20 @@ int main(int argc, char **argv)
 			int ret = remote_poll(remote);
 			pthread_sigmask(SIG_BLOCK, &sa.sa_mask, NULL);
 
+			/* Events. */
+			if (ret > 0) {
+				ret = remote_process(server, remote, buf, buflen);
+				switch(ret) {
+				case KNOT_CTL_RESTART:
+					sig_req_rst = 1; /* Fall through */
+				case KNOT_CTL_STOP:
+					sig_req_stop = 1;
+					break;
+				default:
+					break;
+				}
+			}
+
 			/* Interrupts. */
 			if (sig_req_stop) {
 				sig_req_stop = 0;
@@ -392,11 +407,6 @@ int main(int argc, char **argv)
 				}
 
 			}
-
-			/* Events. */
-			if (ret > 0) {
-				remote_process(server, remote, buf, buflen);
-			}
 		}
 		pthread_sigmask(SIG_UNBLOCK, &sa.sa_mask, NULL);
 
@@ -434,6 +444,10 @@ int main(int argc, char **argv)
 		fflush(stderr);
 	}
 
+	/* Restart hook. */
+	if (sig_req_rst)
+		return execvp(argv[0], argv);
+
 	return res;
 }