diff --git a/emper/Emper.hpp b/emper/Emper.hpp
index 2beb9c0149d9ed8a067eea15a6622d3d1be9b0e3..c87e99100b7ddcd950f8920f4140285fb8b3dfbc 100644
--- a/emper/Emper.hpp
+++ b/emper/Emper.hpp
@@ -163,4 +163,15 @@ static const bool HAS_FS_PATH =
 #endif
 		;
 
+enum class CompleterSchedParam {
+	unchanged,
+	normal,
+	nice_10,
+	nice_19,
+	idle,
+};
+
+static const CompleterSchedParam COMPLETER_SCHED_PARAM =
+		CompleterSchedParam::EMPER_IO_COMPLETER_SCHED_PARAM;
+
 }	 // namespace emper
diff --git a/emper/io/GlobalIoContext.cpp b/emper/io/GlobalIoContext.cpp
index a561324791c92aabc9635efa7eedec96d96a85d9..a4f01718ba0f073ee41431e30b88597813fd331d 100644
--- a/emper/io/GlobalIoContext.cpp
+++ b/emper/io/GlobalIoContext.cpp
@@ -4,6 +4,7 @@
 
 #include <liburing.h>
 #include <liburing/io_uring.h>
+#include <sched.h>
 #include <unistd.h>
 
 #include <cassert>
@@ -193,6 +194,34 @@ void GlobalIoContext::startCompleter() {
 	if (unlikely(errno)) {
 		DIE_MSG_ERRNO("Creating completer thread failed");
 	}
+
+	int sched_priority = 0, sched_policy;
+
+	switch (emper::COMPLETER_SCHED_PARAM) {
+		case CompleterSchedParam::unchanged:
+			return;
+		case CompleterSchedParam::normal:
+			sched_policy = SCHED_OTHER;
+			break;
+		case CompleterSchedParam::nice_10:
+			sched_policy = SCHED_OTHER;
+			sched_priority = 10;
+			break;
+		case CompleterSchedParam::nice_19:
+			sched_policy = SCHED_OTHER;
+			sched_priority = 19;
+			break;
+		case CompleterSchedParam::idle:
+			sched_policy = SCHED_IDLE;
+			break;
+	}
+
+	const struct sched_param param = {sched_priority};
+	errno = pthread_setschedparam(completer, sched_policy, &param);
+
+	if (unlikely(errno)) {
+		DIE_MSG_ERRNO("Setting scheduler parameter on completer thread failed");
+	}
 }
 
 void GlobalIoContext::initiateTermination() {
diff --git a/iwyu-mappings.imp b/iwyu-mappings.imp
index f082c29c494cf2f276e2aa3e2f5e8959353e008c..34019d64c63bb35e2178ce15519a1b194789670d 100644
--- a/iwyu-mappings.imp
+++ b/iwyu-mappings.imp
@@ -5,6 +5,7 @@
 	{ include: ["<bits/cxxabi_forced.h>", "private", "<ctime>", "public" ] },
 	{ include: ["<bits/this_thread_sleep.h>", "private", "<thread>", "public" ] },
 	{ include: ["<ext/alloc_traits.h>", "private", "<memory>", "public" ] },
+	{ include: ["<bits/types/struct_sched_param.h>", "private", "<sched.h>", "public" ] },
 
 	{ symbol: ["__kernel_timespec", "private", "<liburing.h>", "public" ] },
 	{ symbol: ["std::filesystem", "private", "<filesystem>", "public" ] },
diff --git a/meson.build b/meson.build
index 056dd7c75cec49ad07b3f1ff7f15bd55a6eb0ba4..e31d9d4d80b7c26b27f7727c761bbe286e973fde 100644
--- a/meson.build
+++ b/meson.build
@@ -42,6 +42,7 @@ conf_data.set('EMPER_STATS', get_option('stats'))
 conf_data.set('EMPER_OVERFLOW_QUEUE', get_option('overflow_queue'))
 conf_data.set('EMPER_BLOCKED_CONTEXT_SET', get_option('blocked_context_set'))
 conf_data.set('EMPER_SET_AFFINITY_ON_BLOCK', get_option('set_affinity_on_block'))
+conf_data.set('EMPER_IO_COMPLETER_SCHED_PARAM', get_option('io_completer_sched_param'))
 
 semaphore_impl = get_option('wakeup_semaphore_implementation')
 conf_data.set('EMPER_' + semaphore_impl.to_upper() + '_WAKEUP_SEMAPHORE', true)
diff --git a/meson_options.txt b/meson_options.txt
index 899b30b6ae540d8cc9c1d79eefc357889080524f..1cd5d81a4e618501181c035e1aa16bb62dc183db 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -160,3 +160,10 @@ option(
   value: false,
   description: 'Set the affinity when blocking',
 )
+option(
+  'io_completer_sched_param',
+  type: 'combo',
+  description: 'The scheduling parmaters used for the completer thread',
+  choices: ['unchanged', 'normal', 'nice_10', 'nice_19', 'idle'],
+  value: 'unchanged',
+)