diff --git a/Cargo.lock b/Cargo.lock
index 238776a1..82a7c32a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -461,6 +461,7 @@ dependencies = [
  "rusqlite",
  "rust-rocksdb",
  "sd-notify",
+ "sentry",
  "serde",
  "serde_html_form",
  "serde_json",
@@ -593,6 +594,16 @@ version = "2.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
 
+[[package]]
+name = "debugid"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d"
+dependencies = [
+ "serde",
+ "uuid",
+]
+
 [[package]]
 name = "der"
 version = "0.7.8"
@@ -684,6 +695,16 @@ version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
 
+[[package]]
+name = "errno"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
+dependencies = [
+ "libc",
+ "windows-sys 0.52.0",
+]
+
 [[package]]
 name = "fallible-iterator"
 version = "0.3.0"
@@ -696,6 +717,12 @@ version = "0.1.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
 
+[[package]]
+name = "fastrand"
+version = "2.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984"
+
 [[package]]
 name = "fdeflate"
 version = "0.3.4"
@@ -725,6 +752,18 @@ dependencies = [
  "version_check",
 ]
 
+[[package]]
+name = "findshlibs"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40b9e59cd0f7e0806cca4be089683ecb6434e602038df21fe6bf6711b2f07f64"
+dependencies = [
+ "cc",
+ "lazy_static",
+ "libc",
+ "winapi",
+]
+
 [[package]]
 name = "flate2"
 version = "1.0.28"
@@ -741,6 +780,21 @@ version = "1.0.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
 
+[[package]]
+name = "foreign-types"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
+dependencies = [
+ "foreign-types-shared",
+]
+
+[[package]]
+name = "foreign-types-shared"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
+
 [[package]]
 name = "form_urlencoded"
 version = "1.2.1"
@@ -822,9 +876,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
 dependencies = [
  "futures-core",
+ "futures-io",
  "futures-macro",
  "futures-sink",
  "futures-task",
+ "memchr",
  "pin-project-lite",
  "pin-utils",
  "slab",
@@ -1118,6 +1174,19 @@ dependencies = [
  "tokio-rustls",
 ]
 
+[[package]]
+name = "hyper-tls"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
+dependencies = [
+ "bytes",
+ "hyper",
+ "native-tls",
+ "tokio",
+ "tokio-native-tls",
+]
+
 [[package]]
 name = "hyperlocal"
 version = "0.8.0"
@@ -1381,6 +1450,12 @@ version = "0.5.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
 
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
+
 [[package]]
 name = "lock_api"
 version = "0.4.11"
@@ -1514,6 +1589,24 @@ dependencies = [
  "windows-sys 0.48.0",
 ]
 
+[[package]]
+name = "native-tls"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e"
+dependencies = [
+ "lazy_static",
+ "libc",
+ "log",
+ "openssl",
+ "openssl-probe",
+ "openssl-sys",
+ "schannel",
+ "security-framework",
+ "security-framework-sys",
+ "tempfile",
+]
+
 [[package]]
 name = "new_debug_unreachable"
 version = "1.0.6"
@@ -1658,12 +1751,50 @@ version = "1.19.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
 
+[[package]]
+name = "openssl"
+version = "0.10.64"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f"
+dependencies = [
+ "bitflags 2.5.0",
+ "cfg-if",
+ "foreign-types",
+ "libc",
+ "once_cell",
+ "openssl-macros",
+ "openssl-sys",
+]
+
+[[package]]
+name = "openssl-macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.55",
+]
+
 [[package]]
 name = "openssl-probe"
 version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
 
+[[package]]
+name = "openssl-sys"
+version = "0.9.102"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
 [[package]]
 name = "opentelemetry"
 version = "0.21.0"
@@ -1745,6 +1876,17 @@ dependencies = [
  "num-traits",
 ]
 
+[[package]]
+name = "os_info"
+version = "3.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae99c7fa6dd38c7cafe1ec085e804f8f555a2f8659b0dbe03f1f9963a9b51092"
+dependencies = [
+ "log",
+ "serde",
+ "windows-sys 0.52.0",
+]
+
 [[package]]
 name = "overload"
 version = "0.1.1"
@@ -2100,10 +2242,12 @@ dependencies = [
  "http-body",
  "hyper",
  "hyper-rustls",
+ "hyper-tls",
  "ipnet",
  "js-sys",
  "log",
  "mime",
+ "native-tls",
  "once_cell",
  "percent-encoding",
  "pin-project-lite",
@@ -2116,6 +2260,7 @@ dependencies = [
  "sync_wrapper",
  "system-configuration",
  "tokio",
+ "tokio-native-tls",
  "tokio-rustls",
  "tokio-socks",
  "tower-service",
@@ -2401,6 +2546,19 @@ dependencies = [
  "semver",
 ]
 
+[[package]]
+name = "rustix"
+version = "0.38.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89"
+dependencies = [
+ "bitflags 2.5.0",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.52.0",
+]
+
 [[package]]
 name = "rustls"
 version = "0.21.10"
@@ -2516,6 +2674,114 @@ version = "1.0.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca"
 
+[[package]]
+name = "sentry"
+version = "0.32.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "766448f12e44d68e675d5789a261515c46ac6ccd240abdd451a9c46c84a49523"
+dependencies = [
+ "httpdate",
+ "native-tls",
+ "reqwest",
+ "sentry-backtrace",
+ "sentry-contexts",
+ "sentry-core",
+ "sentry-debug-images",
+ "sentry-panic",
+ "sentry-tracing",
+ "tokio",
+ "ureq",
+]
+
+[[package]]
+name = "sentry-backtrace"
+version = "0.32.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32701cad8b3c78101e1cd33039303154791b0ff22e7802ed8cc23212ef478b45"
+dependencies = [
+ "backtrace",
+ "once_cell",
+ "regex",
+ "sentry-core",
+]
+
+[[package]]
+name = "sentry-contexts"
+version = "0.32.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17ddd2a91a13805bd8dab4ebf47323426f758c35f7bf24eacc1aded9668f3824"
+dependencies = [
+ "hostname",
+ "libc",
+ "os_info",
+ "rustc_version",
+ "sentry-core",
+ "uname",
+]
+
+[[package]]
+name = "sentry-core"
+version = "0.32.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1189f68d7e7e102ef7171adf75f83a59607fafd1a5eecc9dc06c026ff3bdec4"
+dependencies = [
+ "once_cell",
+ "rand",
+ "sentry-types",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "sentry-debug-images"
+version = "0.32.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b4d0a615e5eeca5699030620c119a094e04c14cf6b486ea1030460a544111a7"
+dependencies = [
+ "findshlibs",
+ "once_cell",
+ "sentry-core",
+]
+
+[[package]]
+name = "sentry-panic"
+version = "0.32.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d1c18d0b5fba195a4950f2f4c31023725c76f00aabb5840b7950479ece21b5ca"
+dependencies = [
+ "sentry-backtrace",
+ "sentry-core",
+]
+
+[[package]]
+name = "sentry-tracing"
+version = "0.32.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3012699a9957d7f97047fd75d116e22d120668327db6e7c59824582e16e791b2"
+dependencies = [
+ "sentry-backtrace",
+ "sentry-core",
+ "tracing-core",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "sentry-types"
+version = "0.32.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7173fd594569091f68a7c37a886e202f4d0c1db1e1fa1d18a051ba695b2e2ec"
+dependencies = [
+ "debugid",
+ "hex",
+ "rand",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "time",
+ "url",
+ "uuid",
+]
+
 [[package]]
 name = "serde"
 version = "1.0.197"
@@ -2835,6 +3101,18 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "tempfile"
+version = "3.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "rustix",
+ "windows-sys 0.52.0",
+]
+
 [[package]]
 name = "tendril"
 version = "0.4.3"
@@ -3004,6 +3282,16 @@ dependencies = [
  "syn 2.0.55",
 ]
 
+[[package]]
+name = "tokio-native-tls"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
+dependencies = [
+ "native-tls",
+ "tokio",
+]
+
 [[package]]
 name = "tokio-rustls"
 version = "0.24.1"
@@ -3253,6 +3541,15 @@ version = "1.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e36a83ea2b3c704935a01b4642946aadd445cea40b10935e3f8bd8052b8193d6"
 
+[[package]]
+name = "uname"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b72f89f0ca32e4db1c04e2a72f5345d59796d4866a1ee0609084569f73683dc8"
+dependencies = [
+ "libc",
+]
+
 [[package]]
 name = "uncased"
 version = "0.9.10"
@@ -3295,6 +3592,19 @@ version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
 
+[[package]]
+name = "ureq"
+version = "2.9.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11f214ce18d8b2cbe84ed3aa6486ed3f5b285cf8d8fbdbce9f3f767a724adc35"
+dependencies = [
+ "base64 0.21.7",
+ "log",
+ "native-tls",
+ "once_cell",
+ "url",
+]
+
 [[package]]
 name = "url"
 version = "2.5.0"
@@ -3304,6 +3614,7 @@ dependencies = [
  "form_urlencoded",
  "idna 0.5.0",
  "percent-encoding",
+ "serde",
 ]
 
 [[package]]
@@ -3325,6 +3636,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
 dependencies = [
  "getrandom",
+ "serde",
 ]
 
 [[package]]
diff --git a/Cargo.toml b/Cargo.toml
index d758bbfe..350d8704 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -169,6 +169,11 @@ version = "0.20.0"
 optional = true
 features = ["rt-tokio"]
 
+# optional sentry metrics for crash/panic reporting
+[dependencies.sentry]
+version = "0.32.2"
+optional = true
+
 # optional jemalloc usage
 [dependencies.tikv-jemallocator]
 version = "0.5.4"
@@ -282,7 +287,7 @@ hyperlocal = { git = "https://github.com/softprops/hyperlocal", rev = "2ee4d1496
 
 
 [features]
-default = ["conduit_bin", "backend_rocksdb", "systemd", "element_hacks"]
+default = ["conduit_bin", "backend_rocksdb", "systemd", "element_hacks", "sentry"]
 conduit_bin = ["axum"]
 backend_sqlite = ["sqlite"]
 backend_rocksdb = ["rocksdb"]
diff --git a/conduwuit-example.toml b/conduwuit-example.toml
index fed77160..577c3353 100644
--- a/conduwuit-example.toml
+++ b/conduwuit-example.toml
@@ -32,6 +32,16 @@
 # Defaults to `matrix.org`
 # trusted_servers = ["matrix.org"]
 
+# Sentry.io crash/panic reporting, performance monitoring/metrics, etc.
+#
+# Defaults to false
+#sentry = false
+
+# Report your Conduwuit server_name in Sentry.io crash reports and metrics
+#
+# Defaults to false
+#sentry_send_server_name = false
+
 
 
 ### Database configuration
diff --git a/src/config/mod.rs b/src/config/mod.rs
index 67aa037a..a07fa5da 100644
--- a/src/config/mod.rs
+++ b/src/config/mod.rs
@@ -232,6 +232,11 @@ pub struct Config {
 	#[serde(default)]
 	pub block_non_admin_invites: bool,
 
+	#[serde(default)]
+	pub sentry: bool,
+	#[serde(default)]
+	pub sentry_send_server_name: bool,
+
 	#[serde(flatten)]
 	#[allow(clippy::zero_sized_map_values)] // this is a catchall, the map shouldn't be zero at runtime
 	pub catchall: BTreeMap<String, IgnoredAny>,
diff --git a/src/main.rs b/src/main.rs
index 149b1157..d42b0892 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -55,30 +55,17 @@ use tracing_subscriber::{prelude::*, EnvFilter};
 #[global_allocator]
 static GLOBAL: Jemalloc = Jemalloc;
 
-#[tokio::main]
-async fn main() {
+fn main() {
 	let args = clap::parse();
 
 	// Initialize config
-	let raw_config = if Env::var("CONDUIT_CONFIG").is_some() {
+	let raw_config = if let Some(config_file_env) = Env::var("CONDUIT_CONFIG") {
 		Figment::new()
-			.merge(
-				Toml::file(Env::var("CONDUIT_CONFIG").expect(
-					"The CONDUIT_CONFIG environment variable was set but appears to be invalid. This should be set to \
-					 the path to a valid TOML file, an empty string (for compatibility), or removed/unset entirely.",
-				))
-				.nested(),
-			)
+			.merge(Toml::file(config_file_env).nested())
 			.merge(Env::prefixed("CONDUIT_").global())
-	} else if args.config.is_some() {
+	} else if let Some(config_file_arg) = args.config {
 		Figment::new()
-			.merge(
-				Toml::file(args.config.expect(
-					"conduwuit config commandline argument was specified, but appears to be invalid. This should be \
-					 set to the path of a valid TOML file.",
-				))
-				.nested(),
-			)
+			.merge(Toml::file(config_file_arg).nested())
 			.merge(Env::prefixed("CONDUIT_").global())
 	} else {
 		Figment::new().merge(Env::prefixed("CONDUIT_").global())
@@ -92,13 +79,18 @@ async fn main() {
 		},
 	};
 
+	// don't start if we're listening on both UNIX sockets and TCP at same time
+	if config.is_dual_listening(&raw_config) {
+		return;
+	};
+
 	if config.allow_jaeger {
 		#[cfg(feature = "perf_measurements")]
 		{
 			opentelemetry::global::set_text_map_propagator(opentelemetry_jaeger::Propagator::new());
 			let tracer = opentelemetry_jaeger::new_agent_pipeline()
 				.with_auto_split_batch(true)
-				.with_service_name("conduit")
+				.with_service_name("conduwuit")
 				.install_batch(opentelemetry_sdk::runtime::Tokio)
 				.unwrap();
 			let telemetry = tracing_opentelemetry::layer().with_tracer(tracer);
@@ -143,6 +135,29 @@ async fn main() {
 		tracing::subscriber::set_global_default(subscriber).unwrap();
 	}
 
+	#[cfg(feature = "sentry")]
+	if config.sentry {
+		info!("Sentry.io crash reporting and telemetry is enabled, initialising guard");
+
+		let _guard = sentry::init((
+			"https://fe2eb4536aa04949e28eff3128d64757@o4506996327251968.ingest.us.sentry.io/4506996334657536",
+			sentry::ClientOptions {
+				release: sentry::release_name!(),
+				server_name: if config.sentry_send_server_name {
+					Some(config.server_name.to_string().into())
+				} else {
+					None
+				},
+				..Default::default()
+			},
+		));
+	}
+
+	if let Err(e) = check_config(&config) {
+		error!("Config check failed: {e}");
+		return;
+	}
+
 	// This is needed for opening lots of file descriptors, which tends to
 	// happen more often when using RocksDB and making lots of federation
 	// connections at startup. The soft limit is usually 1024, and the hard
@@ -153,192 +168,23 @@ async fn main() {
 	#[cfg(unix)]
 	maximize_fd_limit().expect("Unable to increase maximum soft and hard file descriptor limit");
 
-	config.warn_deprecated();
-	config.warn_unknown_key();
+	tokio::runtime::Builder::new_multi_thread()
+		.enable_all()
+		.build()
+		.unwrap()
+		.block_on(async {
+			info!("Loading database");
+			let db_load_time = std::time::Instant::now();
+			if let Err(error) = KeyValueDatabase::load_or_create(config).await {
+				error!(?error, "The database couldn't be loaded or created");
+				return;
+			};
+			info!("Database took {:?} to load, now starting server", db_load_time.elapsed());
 
-	// don't start if we're listening on both UNIX sockets and TCP at same time
-	if config.is_dual_listening(&raw_config) {
-		return;
-	};
-
-	info!("Loading database");
-	let db_load_time = std::time::Instant::now();
-	if let Err(error) = KeyValueDatabase::load_or_create(config).await {
-		error!(?error, "The database couldn't be loaded or created");
-		return;
-	};
-	info!("Database took {:?} to load", db_load_time.elapsed());
-
-	let config = &services().globals.config;
-
-	/* ad-hoc config validation/checks */
-
-	if config.unix_socket_path.is_some() && !cfg!(unix) {
-		error!(
-			"UNIX socket support is only available on *nix platforms. Please remove \"unix_socket_path\" from your \
-			 config."
-		);
-		return;
-	}
-
-	if config.address.is_loopback() && cfg!(unix) {
-		debug!(
-			"Found loopback listening address {}, running checks if we're in a container.",
-			config.address
-		);
-
-		#[cfg(unix)]
-		if Path::new("/proc/vz").exists() /* Guest */ && !Path::new("/proc/bz").exists()
-		/* Host */
-		{
-			error!(
-				"You are detected using OpenVZ with a loopback/localhost listening address of {}. If you are using \
-				 OpenVZ for containers and you use NAT-based networking to communicate with the host and guest, this \
-				 will NOT work. Please change this to \"0.0.0.0\". If this is expected, you can ignore.",
-				config.address
-			);
-		}
-
-		#[cfg(unix)]
-		if Path::new("/.dockerenv").exists() {
-			error!(
-				"You are detected using Docker with a loopback/localhost listening address of {}. If you are using a \
-				 reverse proxy on the host and require communication to conduwuit in the Docker container via \
-				 NAT-based networking, this will NOT work. Please change this to \"0.0.0.0\". If this is expected, \
-				 you can ignore.",
-				config.address
-			);
-		}
-
-		#[cfg(unix)]
-		if Path::new("/run/.containerenv").exists() {
-			error!(
-				"You are detected using Podman with a loopback/localhost listening address of {}. If you are using a \
-				 reverse proxy on the host and require communication to conduwuit in the Podman container via \
-				 NAT-based networking, this will NOT work. Please change this to \"0.0.0.0\". If this is expected, \
-				 you can ignore.",
-				config.address
-			);
-		}
-	}
-
-	// rocksdb does not allow max_log_files to be 0
-	if config.rocksdb_max_log_files == 0 && cfg!(feature = "rocksdb") {
-		error!("When using RocksDB, rocksdb_max_log_files cannot be 0. Please set a value at least 1.");
-		return;
-	}
-
-	// yeah, unless the user built a debug build hopefully for local testing only
-	if config.server_name == "your.server.name" && !cfg!(debug_assertions) {
-		error!("You must specify a valid server name for production usage of conduwuit.");
-		return;
-	}
-
-	if cfg!(debug_assertions) {
-		info!("Note: conduwuit was built without optimisations (i.e. debug build)");
-	}
-
-	// check if the user specified a registration token as `""`
-	if config.registration_token == Some(String::new()) {
-		error!("Registration token was specified but is empty (\"\")");
-		return;
-	}
-
-	if config.max_request_size < 4096 {
-		error!(?config.max_request_size, "Max request size is less than 4KB. Please increase it.");
-	}
-
-	// check if user specified valid IP CIDR ranges on startup
-	for cidr in services().globals.ip_range_denylist() {
-		_ = ipaddress::IPAddress::parse(cidr).map_err(|e| error!("Error parsing specified IP CIDR range: {e}"));
-	}
-
-	if config.allow_registration
-		&& !config.yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse
-		&& config.registration_token.is_none()
-	{
-		error!(
-			"!! You have `allow_registration` enabled without a token configured in your config which means you are \
-			 allowing ANYONE to register on your conduwuit instance without any 2nd-step (e.g. registration token).\n
-        If this is not the intended behaviour, please set a registration token with the `registration_token` config \
-			 option.\n
-        For security and safety reasons, conduwuit will shut down. If you are extra sure this is the desired behaviour \
-			 you want, please set the following config option to true:
-        `yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse`"
-		);
-		return;
-	}
-
-	if config.allow_registration
-		&& config.yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse
-		&& config.registration_token.is_none()
-	{
-		warn!(
-			"Open registration is enabled via setting \
-			 `yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse` and `allow_registration` to \
-			 true without a registration token configured. You are expected to be aware of the risks now.\n
-        If this is not the desired behaviour, please set a registration token."
-		);
-	}
-
-	if config.allow_outgoing_presence && !config.allow_local_presence {
-		error!("Outgoing presence requires allowing local presence. Please enable \"allow_local_presence\".");
-		return;
-	}
-
-	if config.allow_outgoing_presence {
-		warn!(
-			"! Outgoing federated presence is not spec compliant due to relying on PDUs and EDUs combined.\nOutgoing \
-			 presence will not be very reliable due to this and any issues with federated outgoing presence are very \
-			 likely attributed to this issue.\nIncoming presence and local presence are unaffected."
-		);
-	}
-
-	if config
-		.url_preview_domain_contains_allowlist
-		.contains(&"*".to_owned())
-	{
-		warn!(
-			"All URLs are allowed for URL previews via setting \"url_preview_domain_contains_allowlist\" to \"*\". \
-			 This opens up significant attack surface to your server. You are expected to be aware of the risks by \
-			 doing this."
-		);
-	}
-	if config
-		.url_preview_domain_explicit_allowlist
-		.contains(&"*".to_owned())
-	{
-		warn!(
-			"All URLs are allowed for URL previews via setting \"url_preview_domain_explicit_allowlist\" to \"*\". \
-			 This opens up significant attack surface to your server. You are expected to be aware of the risks by \
-			 doing this."
-		);
-	}
-	if config
-		.url_preview_url_contains_allowlist
-		.contains(&"*".to_owned())
-	{
-		warn!(
-			"All URLs are allowed for URL previews via setting \"url_preview_url_contains_allowlist\" to \"*\". This \
-			 opens up significant attack surface to your server. You are expected to be aware of the risks by doing \
-			 this."
-		);
-	}
-
-	/* end ad-hoc config validation/checks */
-
-	info!("Starting server");
-	if let Err(e) = run_server().await {
-		error!("Critical error starting server: {}", e);
-	};
-
-	// if server runs into critical error and shuts down, shut down the tracer
-	// provider if jaegar is used. awaiting run_server() is a blocking call so
-	// putting this after is fine, but not the other options above.
-	#[cfg(feature = "perf_measurements")]
-	if config.allow_jaeger {
-		opentelemetry::global::shutdown_tracer_provider();
-	}
+			if let Err(e) = run_server().await {
+				error!("Critical error starting server: {e}");
+			};
+		});
 }
 
 async fn run_server() -> io::Result<()> {
@@ -962,6 +808,167 @@ fn maximize_fd_limit() -> Result<(), nix::errno::Errno> {
 	Ok(())
 }
 
+fn check_config(config: &Config) -> Result<()> {
+	config.warn_deprecated();
+	config.warn_unknown_key();
+
+	if config.unix_socket_path.is_some() && !cfg!(unix) {
+		return Err(Error::bad_config(
+			"UNIX socket support is only available on *nix platforms. Please remove \"unix_socket_path\" from your \
+			 config.",
+		));
+	}
+
+	if config.address.is_loopback() && cfg!(unix) {
+		debug!(
+			"Found loopback listening address {}, running checks if we're in a container.",
+			config.address
+		);
+
+		#[cfg(unix)]
+		if Path::new("/proc/vz").exists() /* Guest */ && !Path::new("/proc/bz").exists()
+		/* Host */
+		{
+			error!(
+				"You are detected using OpenVZ with a loopback/localhost listening address of {}. If you are using \
+				 OpenVZ for containers and you use NAT-based networking to communicate with the host and guest, this \
+				 will NOT work. Please change this to \"0.0.0.0\". If this is expected, you can ignore.",
+				config.address
+			);
+		}
+
+		#[cfg(unix)]
+		if Path::new("/.dockerenv").exists() {
+			error!(
+				"You are detected using Docker with a loopback/localhost listening address of {}. If you are using a \
+				 reverse proxy on the host and require communication to conduwuit in the Docker container via \
+				 NAT-based networking, this will NOT work. Please change this to \"0.0.0.0\". If this is expected, \
+				 you can ignore.",
+				config.address
+			);
+		}
+
+		#[cfg(unix)]
+		if Path::new("/run/.containerenv").exists() {
+			error!(
+				"You are detected using Podman with a loopback/localhost listening address of {}. If you are using a \
+				 reverse proxy on the host and require communication to conduwuit in the Podman container via \
+				 NAT-based networking, this will NOT work. Please change this to \"0.0.0.0\". If this is expected, \
+				 you can ignore.",
+				config.address
+			);
+		}
+	}
+
+	// rocksdb does not allow max_log_files to be 0
+	if config.rocksdb_max_log_files == 0 && cfg!(feature = "rocksdb") {
+		return Err(Error::bad_config(
+			"When using RocksDB, rocksdb_max_log_files cannot be 0. Please set a value at least 1.",
+		));
+	}
+
+	// yeah, unless the user built a debug build hopefully for local testing only
+	if config.server_name == "your.server.name" && !cfg!(debug_assertions) {
+		return Err(Error::bad_config(
+			"You must specify a valid server name for production usage of conduwuit.",
+		));
+	}
+
+	if cfg!(debug_assertions) {
+		info!("Note: conduwuit was built without optimisations (i.e. debug build)");
+	}
+
+	// check if the user specified a registration token as `""`
+	if config.registration_token == Some(String::new()) {
+		return Err(Error::bad_config("Registration token was specified but is empty (\"\")"));
+	}
+
+	if config.max_request_size < 16384 {
+		return Err(Error::bad_config("Max request size is less than 16KB. Please increase it."));
+	}
+
+	// check if user specified valid IP CIDR ranges on startup
+	for cidr in &config.ip_range_denylist {
+		if let Err(e) = ipaddress::IPAddress::parse(cidr) {
+			error!("Error parsing specified IP CIDR range from string: {e}");
+			return Err(Error::bad_config("Error parsing specified IP CIDR ranges from strings"));
+		}
+	}
+
+	if config.allow_registration
+		&& !config.yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse
+		&& config.registration_token.is_none()
+	{
+		return Err(Error::bad_config(
+			"!! You have `allow_registration` enabled without a token configured in your config which means you are \
+			 allowing ANYONE to register on your conduwuit instance without any 2nd-step (e.g. registration token).\n
+If this is not the intended behaviour, please set a registration token with the `registration_token` config option.\n
+For security and safety reasons, conduwuit will shut down. If you are extra sure this is the desired behaviour you \
+			 want, please set the following config option to true:
+`yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse`",
+		));
+	}
+
+	if config.allow_registration
+		&& config.yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse
+		&& config.registration_token.is_none()
+	{
+		warn!(
+			"Open registration is enabled via setting \
+			 `yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse` and `allow_registration` to \
+			 true without a registration token configured. You are expected to be aware of the risks now.\n
+    If this is not the desired behaviour, please set a registration token."
+		);
+	}
+
+	if config.allow_outgoing_presence && !config.allow_local_presence {
+		return Err(Error::bad_config(
+			"Outgoing presence requires allowing local presence. Please enable \"allow_local_presence\".",
+		));
+	}
+
+	if config.allow_outgoing_presence {
+		warn!(
+			"! Outgoing federated presence is not spec compliant due to relying on PDUs and EDUs combined.\nOutgoing \
+			 presence will not be very reliable due to this and any issues with federated outgoing presence are very \
+			 likely attributed to this issue.\nIncoming presence and local presence are unaffected."
+		);
+	}
+
+	if config
+		.url_preview_domain_contains_allowlist
+		.contains(&"*".to_owned())
+	{
+		warn!(
+			"All URLs are allowed for URL previews via setting \"url_preview_domain_contains_allowlist\" to \"*\". \
+			 This opens up significant attack surface to your server. You are expected to be aware of the risks by \
+			 doing this."
+		);
+	}
+	if config
+		.url_preview_domain_explicit_allowlist
+		.contains(&"*".to_owned())
+	{
+		warn!(
+			"All URLs are allowed for URL previews via setting \"url_preview_domain_explicit_allowlist\" to \"*\". \
+			 This opens up significant attack surface to your server. You are expected to be aware of the risks by \
+			 doing this."
+		);
+	}
+	if config
+		.url_preview_url_contains_allowlist
+		.contains(&"*".to_owned())
+	{
+		warn!(
+			"All URLs are allowed for URL previews via setting \"url_preview_url_contains_allowlist\" to \"*\". This \
+			 opens up significant attack surface to your server. You are expected to be aware of the risks by doing \
+			 this."
+		);
+	}
+
+	Ok(())
+}
+
 #[cfg(test)]
 mod test {
 	use super::*;