diff --git a/daemon/main.c b/daemon/main.c
index 12826ac289e61a9142bcc004c1853160dc7d4bf2..43fbbf09c1d362f086c7151cbd46880b0cf8d0a3 100644
--- a/daemon/main.c
+++ b/daemon/main.c
@@ -35,6 +35,26 @@
 #endif
 #include <libknot/error.h>
 
+#if ENABLE_JEMALLOC
+/* Make the jemalloc library needed.
+ *
+ * The problem is with --as-needed for linker which is added by default by meson.
+ * If we don't use any jemalloc-specific calls, linker will decide that
+ * it is not needed and won't link it.  Making it needed seems better than
+ * trying to override the flag which might be useful in some other cases, etc.
+ *
+ * Exporting the function is a very easy way of ensuring that it's not optimized out.
+ */
+#include <jemalloc/jemalloc.h>
+KR_EXPORT void kr_jemalloc_unused(void)
+{
+	malloc_stats_print(NULL, NULL, NULL);
+}
+/* We don't use threads (or rarely in some parts), so multiple arenas don't make sense.
+   https://jemalloc.net/jemalloc.3.html
+ */
+KR_EXPORT const char *malloc_conf = "narenas:1";
+#endif
 
 struct args the_args_value;  /** Static allocation for the_args singleton. */
 
diff --git a/daemon/meson.build b/daemon/meson.build
index 4d9ca578e20ecb2d478c38fb210a3c8f2195dc32..68a2646682a134b9178770c3154f6fbe84c4a0f8 100644
--- a/daemon/meson.build
+++ b/daemon/meson.build
@@ -51,6 +51,7 @@ kresd_deps = [
   libsystemd,
   capng,
   nghttp2,
+  malloc,
 ]
 
 
diff --git a/meson.build b/meson.build
index 6a1b65b2ae350ddb77932cb99c5f0675218af7c0..9bb00a912acb8c57d869cd569facbef11386a4bd 100644
--- a/meson.build
+++ b/meson.build
@@ -115,6 +115,18 @@ xdp = meson.get_compiler('c').has_header('libknot/xdp/xdp.h')
 ### Systemd
 systemd_files = get_option('systemd_files')
 libsystemd = dependency('libsystemd', required: systemd_files == 'enabled')
+
+### Allocator
+# use empty name to disable the dependency, but still compile the dependent kresd
+malloc_name = get_option('malloc') == 'disabled' ? '' : 'jemalloc'
+malloc = meson.get_compiler('c').find_library(
+  malloc_name,
+  required: get_option('malloc') == 'jemalloc',
+  #static: false, #TODO: add when bumping meson to >= 0.51;
+  # static linking would most likely cause issues.
+  # Fortunately it seems unlikely that dynamic wouldn't be found and static would be.
+)
+
 message('---------------------------')
 
 ## Compiler args
@@ -174,6 +186,7 @@ conf_data.set('ENABLE_LIBSYSTEMD', libsystemd.found().to_int())
 conf_data.set('ENABLE_SENDMMSG', sendmmsg.to_int())
 conf_data.set('ENABLE_XDP', xdp.to_int())
 conf_data.set('ENABLE_CAP_NG', capng.found().to_int())
+conf_data.set('ENABLE_JEMALLOC', malloc.found().to_int())
 conf_data.set('ENABLE_DOH2', nghttp2.found().to_int())
 conf_data.set('DBG_ASSERTION_ABORT', get_option('debug').to_int())
 if get_option('debug')
@@ -297,6 +310,7 @@ s_sendmmsg = sendmmsg ? 'enabled': 'disabled'
 s_xdp = xdp ? 'enabled': 'disabled'
 s_openssl = openssl.found() ? 'present': 'missing'
 s_capng = capng.found() ? 'enabled': 'disabled'
+s_malloc = malloc.found() ? 'jemalloc' : 'libc default'
 s_doh2 = nghttp2.found() ? 'enabled': 'disabled'
 message('''
 
@@ -335,6 +349,7 @@ message('''
     XDP (in libknot):   @0@'''.format(s_xdp) + '''
     openssl debug:      @0@'''.format(s_openssl) + '''
     capng:              @0@'''.format(s_capng) + '''
+    malloc:             @0@'''.format(s_malloc) + '''
     doh2:               @0@'''.format(s_doh2) + '''
 
 =======================================================
diff --git a/meson_options.txt b/meson_options.txt
index 0e24dc4c893fd0a505a0df2a67bd56c2730230ad..576d385ac858cecf33ed8f75d8eb4d310f4d78bd 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -151,6 +151,18 @@ option(
   description: 'build dnstap module',
 )
 
+option(
+  'malloc',
+  type: 'combo',
+  choices: [
+    'auto',     # 'jemalloc' if available
+    'disabled', # default provided by libc
+    'jemalloc',
+  ],
+  value: 'auto',
+  description: 'memory allocator to use in kresd',
+)
+
 option(
   'doc',
   type: 'combo',