diff --git a/README.md b/README.md
index 60ec180c0ca3b60bf1a18cef7a2f5f5858fc70ee..b75987c7f4e9191116fc11fd85c8fa5c4e6a999c 100644
--- a/README.md
+++ b/README.md
@@ -48,9 +48,9 @@ Install dependencies:
 * GBM (optional, for the GBM allocator)
 * libinput (optional, for the libinput backend)
 * xkbcommon
-* udev
+* udev (optional, for the session)
 * pixman
-* [libseat]
+* [libseat] (optional, for the session)
 * [hwdata] (optional, for the DRM backend)
 
 If you choose to enable X11 support:
diff --git a/backend/backend.c b/backend/backend.c
index de25f3475bacd119d78e72f2b6f21860751a1a0e..a0ed834bc830ae0145a725cf0fde73147a8a5c83 100644
--- a/backend/backend.c
+++ b/backend/backend.c
@@ -9,7 +9,6 @@
 #include <wlr/backend/headless.h>
 #include <wlr/backend/interface.h>
 #include <wlr/backend/multi.h>
-#include <wlr/backend/session.h>
 #include <wlr/backend/wayland.h>
 #include <wlr/config.h>
 #include <wlr/render/wlr_renderer.h>
@@ -20,6 +19,10 @@
 #include "util/env.h"
 #include "util/time.h"
 
+#if WLR_HAS_SESSION
+#include <wlr/backend/session.h>
+#endif
+
 #if WLR_HAS_DRM_BACKEND
 #include <wlr/backend/drm.h>
 #include "backend/drm/monitor.h"
@@ -67,6 +70,7 @@ void wlr_backend_destroy(struct wlr_backend *backend) {
 	}
 }
 
+#if WLR_HAS_SESSION
 static struct wlr_session *session_create_and_wait(struct wl_display *disp) {
 	struct wlr_session *session = wlr_session_create(disp);
 
@@ -106,6 +110,7 @@ static struct wlr_session *session_create_and_wait(struct wl_display *disp) {
 
 	return session;
 }
+#endif
 
 clockid_t wlr_backend_get_presentation_clock(struct wlr_backend *backend) {
 	if (backend->impl->get_presentation_clock) {
@@ -247,7 +252,9 @@ static bool attempt_backend_by_name(struct wl_display *display,
 	} else if (strcmp(name, "drm") == 0 || strcmp(name, "libinput") == 0) {
 		// DRM and libinput need a session
 		if (*session_ptr == NULL) {
+#if WLR_HAS_SESSION
 			*session_ptr = session_create_and_wait(display);
+#endif
 			if (*session_ptr == NULL) {
 				wlr_log(WLR_ERROR, "failed to start a session");
 				return false;
@@ -343,7 +350,9 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display,
 #endif
 
 	// Attempt DRM+libinput
+#if WLR_HAS_SESSION
 	session = session_create_and_wait(display);
+#endif
 	if (!session) {
 		wlr_log(WLR_ERROR, "Failed to start a DRM session");
 		goto error;
@@ -388,6 +397,8 @@ success:
 
 error:
 	wlr_backend_destroy(multi);
+#if WLR_HAS_SESSION
 	wlr_session_destroy(session);
+#endif
 	return NULL;
 }
diff --git a/backend/drm/meson.build b/backend/drm/meson.build
index 7bde50c25881e893c720ae4c3ce56421d48313d8..fc4d84d6fbe15d9348307be57ba2c5723b82a819 100644
--- a/backend/drm/meson.build
+++ b/backend/drm/meson.build
@@ -1,3 +1,7 @@
+if not features['session']
+	subdir_done()
+endif
+
 hwdata = dependency('hwdata', required: false, native: true)
 if hwdata.found()
 	hwdata_dir = hwdata.get_variable(pkgconfig: 'pkgdatadir')
diff --git a/backend/libinput/meson.build b/backend/libinput/meson.build
index 2f3ccab3ea28fa5baad46c2304efea79de7d7bc5..181eb04b30d9238bd525e0844696a27f34762cd4 100644
--- a/backend/libinput/meson.build
+++ b/backend/libinput/meson.build
@@ -10,7 +10,7 @@ libinput = dependency(
 	not_found_message: '\n'.join(msg),
 )
 
-if not libinput.found()
+if not (libinput.found() and features['session'])
 	subdir_done()
 endif
 
diff --git a/backend/meson.build b/backend/meson.build
index b5b6f7a6cc53c9bddff3f6941ab8ca95ef77aa07..ed977d3b6149e783850dc9078313f532bbfff77c 100644
--- a/backend/meson.build
+++ b/backend/meson.build
@@ -1,6 +1,3 @@
-udev = dependency('libudev')
-wlr_deps += udev
-
 wlr_files += files('backend.c')
 
 all_backends = ['drm', 'libinput', 'x11']
@@ -11,6 +8,16 @@ elif 'auto' in backends and get_option('auto_features').disabled()
 	backends = []
 endif
 
+session_required = 'drm' in backends or 'libinput' in backends or get_option('session').enabled()
+if get_option('session').disabled()
+	if session_required
+		error('Session support is required for the DRM or libinput backends')
+	endif
+	session_required = disabler()
+endif
+
+subdir('session')
+
 foreach backend : all_backends
 	if backend in backends or 'auto' in backends
 		subdir(backend)
@@ -20,5 +27,3 @@ endforeach
 subdir('multi')
 subdir('wayland')
 subdir('headless')
-
-subdir('session')
diff --git a/backend/session/meson.build b/backend/session/meson.build
index 9e35ef1ee426d542bfd5597c12bdf50ba85593dd..4c20ee9df779172dfbaf9770eec424bd0d35ec67 100644
--- a/backend/session/meson.build
+++ b/backend/session/meson.build
@@ -1,8 +1,17 @@
+msg = 'Required for session support.'
+udev = dependency('libudev', required: session_required, not_found_message: msg)
 libseat = dependency(
 	'libseat',
 	version: '>=0.2.0',
 	fallback: 'seatd',
 	default_options: ['server=disabled', 'man-pages=disabled', 'examples=disabled'],
+	required: session_required,
+	not_found_message: msg,
 )
+if not (udev.found() and libseat.found())
+	subdir_done()
+endif
+
 wlr_files += files('session.c')
-wlr_deps += libseat
+wlr_deps += [udev, libseat]
+features += { 'session': true }
diff --git a/include/meson.build b/include/meson.build
index 3aa5955e032c341dd8b628326523951238e7c389..69a6d0e0a3410a91ae4792c29fd51e704dbcd760 100644
--- a/include/meson.build
+++ b/include/meson.build
@@ -21,6 +21,9 @@ endif
 if not features.get('vulkan-renderer')
 	exclude_files += 'render/vulkan.h'
 endif
+if not features.get('session')
+	exclude_files += 'backend/session.h'
+endif
 
 install_subdir('wlr',
 	install_dir: get_option('includedir'),
diff --git a/include/wlr/config.h.in b/include/wlr/config.h.in
index 0b612271caaccc3a2258cac6ee9f2c8abcb1684c..6a53d217415c9aa4433bff6872603e5794ca74b6 100644
--- a/include/wlr/config.h.in
+++ b/include/wlr/config.h.in
@@ -12,4 +12,6 @@
 
 #mesondefine WLR_HAS_XWAYLAND
 
+#mesondefine WLR_HAS_SESSION
+
 #endif
diff --git a/meson.build b/meson.build
index a7a69e129befe383a9d235a66c19ff4ed0a07872..fc3f88afdc22cd4d2f56c02e9b4ff3e0098e4d72 100644
--- a/meson.build
+++ b/meson.build
@@ -93,6 +93,7 @@ features = {
 	'gles2-renderer': false,
 	'vulkan-renderer': false,
 	'gbm-allocator': false,
+	'session': false,
 }
 internal_features = {
 	'xcb-errors': false,
diff --git a/meson_options.txt b/meson_options.txt
index 1a439a4d87728ffdba2d578a16ca494ad708077c..6977643ca33c4100347184582b8fb7a7c5d801fa 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -6,3 +6,4 @@ option('renderers', type: 'array', choices: ['auto', 'gles2', 'vulkan'], value:
 option('backends', type: 'array', choices: ['auto', 'drm', 'libinput', 'x11'], value: ['auto'], description: 'Select built-in backends')
 option('allocators', type: 'array', choices: ['auto', 'gbm'], value: ['auto'],
 	description: 'Select built-in allocators')
+option('session', type: 'feature', value: 'auto', description: 'Enable session support')
