diff --git a/src/event/Loop.cxx b/src/event/Loop.cxx
index f2b42af6b..694ec3c65 100644
--- a/src/event/Loop.cxx
+++ b/src/event/Loop.cxx
@@ -13,8 +13,6 @@
 
 #ifdef HAVE_URING
 #include "uring/Manager.hxx"
-#include "util/PrintException.hxx"
-#include <stdio.h>
 #endif
 
 EventLoop::EventLoop(
@@ -53,26 +51,46 @@ EventLoop::~EventLoop() noexcept
 	assert(ready_sockets.empty());
 }
 
+void
+EventLoop::SetVolatile() noexcept
+{
 #ifdef HAVE_URING
+	if (uring)
+		uring->SetVolatile();
+#endif
+}
+
+#ifdef HAVE_URING
+
+void
+EventLoop::EnableUring(unsigned entries, unsigned flags)
+{
+	assert(!uring);
+
+	uring = std::make_unique<Uring::Manager>(*this, entries, flags);
+}
+
+void
+EventLoop::EnableUring(unsigned entries, struct io_uring_params &params)
+{
+	assert(!uring);
+
+	uring = std::make_unique<Uring::Manager>(*this, entries, params);
+}
+
+void
+EventLoop::DisableUring() noexcept
+{
+	uring.reset();
+}
 
 Uring::Queue *
 EventLoop::GetUring() noexcept
 {
-	if (!uring_initialized) {
-		uring_initialized = true;
-		try {
-			uring = std::make_unique<Uring::Manager>(*this, 1024,
-								 IORING_SETUP_SINGLE_ISSUER);
-		} catch (...) {
-			fprintf(stderr, "Failed to initialize io_uring: ");
-			PrintException(std::current_exception());
-		}
-	}
-
 	return uring.get();
 }
 
-#endif
+#endif // HAVE_URING
 
 bool
 EventLoop::AddFD(int fd, unsigned events, SocketEvent &event) noexcept
@@ -280,17 +298,6 @@ EventLoop::Run() noexcept
 	wake_event.Schedule(SocketEvent::READ);
 #endif
 
-#ifdef HAVE_URING
-	AtScopeExit(this) {
-		/* make sure that the Uring::Manager gets destructed
-		   from within the EventThread, or else its
-		   destruction in another thread will cause assertion
-		   failures */
-		uring.reset();
-		uring_initialized = false;
-	};
-#endif
-
 #ifdef HAVE_THREADED_EVENT_LOOP
 	AtScopeExit(this) {
 		wake_event.Cancel();
diff --git a/src/event/Loop.hxx b/src/event/Loop.hxx
index 5163bcb70..eaee88c9a 100644
--- a/src/event/Loop.hxx
+++ b/src/event/Loop.hxx
@@ -27,6 +27,7 @@
 #include "io/uring/Features.h"
 #ifdef HAVE_URING
 #include <memory>
+struct io_uring_params;
 namespace Uring { class Queue; class Manager; }
 #endif
 
@@ -133,10 +134,6 @@ class EventLoop final
 	bool busy = true;
 #endif
 
-#ifdef HAVE_URING
-	bool uring_initialized = false;
-#endif
-
 	ClockCache<std::chrono::steady_clock> steady_clock_cache;
 
 public:
@@ -185,8 +182,27 @@ public:
 		steady_clock_cache.flush();
 	}
 
+	void SetVolatile() noexcept;
+
 #ifdef HAVE_URING
-	[[gnu::pure]]
+	/**
+	 * Try to enable io_uring support.  If this method succeeds,
+	 * GetUring() can be used to obtain a pointer to the queue
+	 * instance.
+	 *
+	 * Throws on error.
+	 */
+	void EnableUring(unsigned entries, unsigned flags);
+	void EnableUring(unsigned entries, struct io_uring_params &params);
+
+	void DisableUring() noexcept;
+
+	/**
+	 * Returns a pointer to the io_uring queue instance or nullptr
+	 * if io_uring support is not available (or was not enabled
+	 * using EnableUring()).
+	 */
+	[[nodiscard]] [[gnu::const]]
 	Uring::Queue *GetUring() noexcept;
 #endif
 
diff --git a/src/event/Thread.cxx b/src/event/Thread.cxx
index d756171c9..70e1feb4f 100644
--- a/src/event/Thread.cxx
+++ b/src/event/Thread.cxx
@@ -9,6 +9,11 @@
 #include "util/Domain.hxx"
 #include "Log.hxx"
 
+#ifdef HAVE_URING
+#include "util/ScopeExit.hxx"
+#include <liburing.h>
+#endif
+
 static constexpr Domain event_domain("event");
 
 void
@@ -51,7 +56,27 @@ EventThread::Run() noexcept
 				"RTIOThread could not get realtime scheduling, continuing anyway: {}",
 				std::current_exception());
 		}
+	} else {
+#ifdef HAVE_URING
+		try {
+			event_loop.EnableUring(1024, IORING_SETUP_SINGLE_ISSUER);
+		} catch (...) {
+			FmtInfo(event_domain,
+				"Failed to initialize io_uring: {}",
+				std::current_exception());
+		}
+#endif
 	}
 
+#ifdef HAVE_URING
+	AtScopeExit(this) {
+		/* make sure that the Uring::Manager gets destructed
+		   from within the EventThread, or else its
+		   destruction in another thread will cause assertion
+		   failures */
+		event_loop.DisableUring();
+	};
+#endif
+
 	event_loop.Run();
 }
diff --git a/src/event/Thread.hxx b/src/event/Thread.hxx
index dda44db8e..0d1c94d0c 100644
--- a/src/event/Thread.hxx
+++ b/src/event/Thread.hxx
@@ -1,8 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 // Copyright The Music Player Daemon Project
 
-#ifndef MPD_EVENT_THREAD_HXX
-#define MPD_EVENT_THREAD_HXX
+#pragma once
 
 #include "Loop.hxx"
 #include "thread/Thread.hxx"
@@ -36,5 +35,3 @@ public:
 private:
 	void Run() noexcept;
 };
-
-#endif /* MAIN_NOTIFY_H */