diff --git a/src/service/manager.rs b/src/service/manager.rs
index af59b4a4..447cd6fe 100644
--- a/src/service/manager.rs
+++ b/src/service/manager.rs
@@ -54,7 +54,7 @@ impl Manager {
 		);
 
 		debug!("Starting service workers...");
-		for service in self.services.service.values() {
+		for (service, ..) in self.services.service.values() {
 			self.start_worker(&mut workers, service).await?;
 		}
 
diff --git a/src/service/service.rs b/src/service/service.rs
index 3b8f4231..ab41753a 100644
--- a/src/service/service.rs
+++ b/src/service/service.rs
@@ -1,11 +1,11 @@
-use std::{collections::BTreeMap, fmt::Write, sync::Arc};
+use std::{any::Any, collections::BTreeMap, fmt::Write, sync::Arc};
 
 use async_trait::async_trait;
 use conduit::{utils::string::split_once_infallible, Result, Server};
 use database::Database;
 
 #[async_trait]
-pub(crate) trait Service: Send + Sync {
+pub(crate) trait Service: Any + Send + Sync {
 	/// Implement the construction of the service instance. Services are
 	/// generally singletons so expect this to only be called once for a
 	/// service type. Note that it may be called again after a server reload,
@@ -40,7 +40,16 @@ pub(crate) struct Args<'a> {
 	pub(crate) _service: &'a Map,
 }
 
-pub(crate) type Map = BTreeMap<String, Arc<dyn Service>>;
+pub(crate) type Map = BTreeMap<String, MapVal>;
+pub(crate) type MapVal = (Arc<dyn Service>, Arc<dyn Any + Send + Sync>);
+
+pub(crate) fn get<T: Any + Send + Sync>(map: &Map, name: &str) -> Option<Arc<T>> {
+	map.get(name).map(|(_, s)| {
+		s.clone()
+			.downcast::<T>()
+			.expect("Service must be correctly downcast.")
+	})
+}
 
 #[inline]
 pub(crate) fn make_name(module_path: &str) -> &str { split_once_infallible(module_path, "::").1 }
diff --git a/src/service/services.rs b/src/service/services.rs
index cc9ec290..0aac10dc 100644
--- a/src/service/services.rs
+++ b/src/service/services.rs
@@ -1,4 +1,4 @@
-use std::{collections::BTreeMap, fmt::Write, sync::Arc};
+use std::{any::Any, collections::BTreeMap, fmt::Write, sync::Arc};
 
 use conduit::{debug, debug_info, info, trace, Result, Server};
 use database::Database;
@@ -7,7 +7,7 @@ use tokio::sync::Mutex;
 use crate::{
 	account_data, admin, appservice, globals, key_backups,
 	manager::Manager,
-	media, presence, pusher, rooms, sending,
+	media, presence, pusher, rooms, sending, service,
 	service::{Args, Map, Service},
 	transaction_ids, uiaa, updates, users,
 };
@@ -44,7 +44,7 @@ impl Services {
 					db: &db,
 					_service: &service,
 				})?;
-				service.insert(built.name().to_owned(), built.clone());
+				service.insert(built.name().to_owned(), (built.clone(), built.clone()));
 				built
 			}};
 		}
@@ -128,7 +128,7 @@ impl Services {
 	}
 
 	pub async fn clear_cache(&self) {
-		for service in self.service.values() {
+		for (service, ..) in self.service.values() {
 			service.clear_cache();
 		}
 
@@ -143,7 +143,7 @@ impl Services {
 
 	pub async fn memory_usage(&self) -> Result<String> {
 		let mut out = String::new();
-		for service in self.service.values() {
+		for (service, ..) in self.service.values() {
 			service.memory_usage(&mut out)?;
 		}
 
@@ -163,9 +163,11 @@ impl Services {
 	fn interrupt(&self) {
 		debug!("Interrupting services...");
 
-		for (name, service) in &self.service {
+		for (name, (service, ..)) in &self.service {
 			trace!("Interrupting {name}");
 			service.interrupt();
 		}
 	}
+
+	pub fn get<T: Any + Send + Sync>(&self, name: &str) -> Option<Arc<T>> { service::get::<T>(&self.service, name) }
 }