diff --git a/src/knot/server/xdp-handler.c b/src/knot/server/xdp-handler.c
index db66d33f05f1fd5fe0cac3376af772fe20ec83f4..f285c72d5e3c324bf0f20d5e9a41427af8d9b3bd 100644
--- a/src/knot/server/xdp-handler.c
+++ b/src/knot/server/xdp-handler.c
@@ -16,13 +16,13 @@
 
 #ifdef ENABLE_XDP
 
+#include <assert.h>
 #include <stdlib.h>
 #include <urcu.h>
 
 #include "knot/server/xdp-handler.h"
 #include "knot/common/log.h"
 #include "knot/server/server.h"
-#include "contrib/ucw/mempool.h"
 #include "libknot/error.h"
 #include "libknot/xdp/tcp.h"
 
@@ -34,10 +34,11 @@ typedef struct xdp_handle_ctx {
 	uint32_t msg_udp_count;
 	knot_tcp_table_t *tcp_table;
 
+	bool tcp;
 	size_t tcp_max_conns;
 	size_t tcp_max_inbufs;
-	uint32_t tcp_idle_close;
-	uint32_t tcp_idle_reset;
+	uint32_t tcp_idle_close; // In microseconds.
+	uint32_t tcp_idle_reset; // In microseconds.
 } xdp_handle_ctx_t;
 
 static bool udp_state_active(int state)
@@ -59,22 +60,16 @@ void xdp_handle_reconfigure(xdp_handle_ctx_t *ctx)
 {
 	rcu_read_lock();
 	conf_t *pconf = conf();
+	ctx->tcp            = pconf->cache.xdp_tcp;
 	ctx->tcp_max_conns  = pconf->cache.xdp_tcp_max_clients    / pconf->cache.srv_xdp_threads;
 	ctx->tcp_max_inbufs = pconf->cache.xdp_tcp_inbuf_max_size / pconf->cache.srv_xdp_threads;
-	ctx->tcp_idle_close = pconf->cache.xdp_tcp_idle_close * 1000000; // conf:secs -> tcp:usecs
+	ctx->tcp_idle_close = pconf->cache.xdp_tcp_idle_close * 1000000;
 	ctx->tcp_idle_reset = pconf->cache.xdp_tcp_idle_reset * 1000000;
 	rcu_read_unlock();
 }
 
-void xdp_handle_cleanup(xdp_handle_ctx_t *ctx)
-{
-	ctx->msg_recv_count = 0;
-	ctx->msg_udp_count = 0;
-}
-
 void xdp_handle_free(xdp_handle_ctx_t *ctx)
 {
-	xdp_handle_cleanup(ctx);
 	knot_tcp_table_free(ctx->tcp_table);
 	free(ctx);
 }
@@ -86,13 +81,15 @@ xdp_handle_ctx_t *xdp_handle_init(void)
 		return NULL;
 	}
 
-	xdp_handle_cleanup(ctx);
 	xdp_handle_reconfigure(ctx);
 
-	ctx->tcp_table = knot_tcp_table_new(ctx->tcp_max_conns); // NOTE: it's not necessary that the table size is equal to its max usage!
-	if (ctx->tcp_table == NULL) {
-		xdp_handle_free(ctx);
-		return NULL;
+	if (ctx->tcp) {
+		// NOTE: the table size don't have to equal its max usage!
+		ctx->tcp_table = knot_tcp_table_new(ctx->tcp_max_conns);
+		if (ctx->tcp_table == NULL) {
+			xdp_handle_free(ctx);
+			return NULL;
+		}
 	}
 
 	return ctx;
@@ -100,8 +97,7 @@ xdp_handle_ctx_t *xdp_handle_init(void)
 
 int xdp_handle_recv(xdp_handle_ctx_t *ctx, knot_xdp_socket_t *xdp_sock)
 {
-	xdp_handle_cleanup(ctx);
-	int ret = knot_xdp_recv(xdp_sock, ctx->msg_recv, sizeof(ctx->msg_recv) / sizeof(ctx->msg_recv[0]),
+	int ret = knot_xdp_recv(xdp_sock, ctx->msg_recv, XDP_BATCHLEN,
 	                        &ctx->msg_recv_count, NULL);
 	return ret == KNOT_EOK ? ctx->msg_recv_count : ret;
 }
@@ -131,15 +127,17 @@ static void handle_finish(knot_layer_t *layer)
 int xdp_handle_msgs(xdp_handle_ctx_t *ctx, knot_xdp_socket_t *sock,
                     knot_layer_t *layer, server_t *server, unsigned thread_id)
 {
+	assert(ctx->msg_recv_count > 0);
+
 	knotd_qdata_params_t params = {
 		.socket = knot_xdp_socket_fd(sock),
 		.server = server,
 		.thread_id = thread_id,
 	};
 
-	if (ctx->msg_recv_count > 0) {
-		knot_xdp_send_prepare(sock);
-	}
+	knot_xdp_send_prepare(sock);
+
+	ctx->msg_udp_count = 0;
 
 	// handle UDP messages
 	for (uint32_t i = 0; i < ctx->msg_recv_count; i++) {
@@ -202,36 +200,35 @@ int xdp_handle_msgs(xdp_handle_ctx_t *ctx, knot_xdp_socket_t *sock,
 	return KNOT_EOK;
 }
 
-size_t overweight(size_t weight, size_t max_weight)
-{
-	int64_t w = weight;
-	w -= max_weight;
-	w = MAX(w, 0);
-	return w;
-}
-
 int xdp_handle_send(xdp_handle_ctx_t *ctx, knot_xdp_socket_t *xdp_sock)
 {
 	uint32_t unused = 0;
 
 	int ret = knot_xdp_send(xdp_sock, ctx->msg_send_udp, ctx->msg_udp_count, &unused);
 	if (ret == KNOT_EOK) {
-		if (ctx->tcp_relays.size > 0) {
-			ret = knot_tcp_send(xdp_sock, knot_tcp_relay_dynarray_arr(&ctx->tcp_relays), ctx->tcp_relays.size);
+		if (ctx->tcp) {
+			ret = knot_tcp_send(xdp_sock, knot_tcp_relay_dynarray_arr(&ctx->tcp_relays),
+			                    ctx->tcp_relays.size);
 		} else {
 			ret = knot_xdp_send_finish(xdp_sock);
 		}
 	}
 
-	knot_tcp_relay_free(&ctx->tcp_relays);
-
-	if (ret == KNOT_EOK) {
-		ret = xdp_handle_sweep(ctx, xdp_sock);
+	if (ctx->tcp) {
+		knot_tcp_relay_free(&ctx->tcp_relays);
 	}
 
 	return ret;
 }
 
+static size_t overweight(size_t weight, size_t max_weight)
+{
+	int64_t w = weight;
+	w -= max_weight;
+	w = MAX(w, 0);
+	return w;
+}
+
 int xdp_handle_sweep(xdp_handle_ctx_t *ctx, knot_xdp_socket_t *xdp_sock)
 {
 	uint32_t last_reset = 0, last_close = 0;
diff --git a/src/knot/server/xdp-handler.h b/src/knot/server/xdp-handler.h
index 3eed5c286459adba083ead3114e013d8b8fda6b0..3a21a0f56c7bc9a2e7ad52686f31cfac7f250f28 100644
--- a/src/knot/server/xdp-handler.h
+++ b/src/knot/server/xdp-handler.h
@@ -36,11 +36,6 @@ struct xdp_handle_ctx *xdp_handle_init(void);
  */
 void xdp_handle_free(struct xdp_handle_ctx *ctx);
 
-/*!
- * \brief Reset XDP packet handling context.
- */
-void xdp_handle_cleanup(struct xdp_handle_ctx *ctx);
-
 /*!
  * \brief Receive packets thru XDP socket.
  */