Skip to content

secwest/lean-ssh

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 

Repository files navigation

Lean SSH Sessions — Disable Unnecessary User Services for Non-Graphical Logins

Problem

On Ubuntu systems (22.04+), every user login — including console-only SSH sessions — starts a full systemd --user session. This triggers socket-activated and D-Bus-activated services designed for graphical desktops, resulting in unnecessary processes consuming memory and resources on headless or SSH-only connections.

A typical SSH login can spawn 8–15+ unnecessary processes across audio, file indexing, virtual filesystems, accessibility, desktop portals, and more — none of which serve any purpose over SSH.

Solution

We apply systemd drop-in overrides that add ConditionEnvironment=DISPLAY to the relevant unit files. This condition ensures the services only start when a graphical display is available (i.e., $DISPLAY is set in the environment), which is the case for desktop and remote desktop sessions but not for plain SSH logins.

The script auto-discovers which units are actually installed on the target system and only creates overrides for those — missing units are silently skipped. This makes it safe to run across different Ubuntu versions and desktop configurations.

Why Not Just Mask the Units?

Masking (systemctl --user mask <unit>) would disable the services entirely — including in graphical sessions. This breaks desktop audio, snap theming, and XDG portals for users who also log in via a desktop environment.

ConditionEnvironment=DISPLAY is the right approach because:

  • Graphical sessions (GNOME, KDE, etc.) set $DISPLAY — services start normally.
  • SSH with X11 forwarding (ssh -X) also sets $DISPLAY — services start, which is correct since forwarded X11 apps may need audio and portal access.
  • Plain SSH sessions do not set $DISPLAY — services are cleanly skipped.
  • No state to track — unlike masks, there's no per-user state to manage or reconcile.

Unit Tiers

Services are organized into three tiers based on risk profile:

Default Tier (always safe)

These services have no business running in a headless SSH session. No CLI tools depend on them.

Unit Category Description
pipewire.socket Audio (PipeWire) Multimedia server socket activation
pipewire-pulse.socket Audio (PipeWire) PulseAudio compatibility layer
wireplumber.service Audio (PipeWire) PipeWire session/policy manager
pulseaudio.socket Audio (PulseAudio) Legacy audio server (pre-PipeWire systems)
pulseaudio.service Audio (PulseAudio) Legacy audio server service
snapd.session-agent.socket Snap Snap desktop integration (Ubuntu-specific)
xdg-document-portal.service XDG Portals Sandboxed document access
xdg-permission-store.service XDG Portals Sandboxed app permissions
xdg-desktop-portal.service XDG Portals Main portal dispatcher
xdg-desktop-portal-gtk.service XDG Portals GTK portal backend
xdg-desktop-portal-gnome.service XDG Portals GNOME portal backend
at-spi-dbus-bus.service Accessibility AT-SPI D-Bus bus for assistive tech
tracker-miner-fs-3.service Indexer GNOME Tracker filesystem indexer (CPU/IO heavy)
tracker-xdg-portal-3.service Indexer Tracker XDG portal frontend
speech-dispatcher.service Speech Text-to-speech routing daemon
speech-dispatcherd.service Speech Text-to-speech (alternate name)

Full Tier (--full)

Safe on servers and SSH-only boxes. On shared workstations where users also log in graphically, these are still safe (the $DISPLAY gate preserves graphical sessions), but the surface area is larger.

Unit Category Description
gvfs-daemon.service GVFS Core virtual filesystem daemon + FUSE
gvfs-metadata.service GVFS File metadata tracking for Nautilus
gvfs-udisks2-volume-monitor.service GVFS Local disk volume monitor
gvfs-mtp-volume-monitor.service GVFS MTP device monitor (Android phones)
gvfs-goa-volume-monitor.service GVFS GNOME Online Accounts volume monitor
gvfs-afc-volume-monitor.service GVFS Apple AFC protocol volume monitor
evolution-addressbook-factory.service Evolution Contacts backend for GNOME
evolution-calendar-factory.service Evolution Calendar backend for GNOME
evolution-source-registry.service Evolution Data source registry
goa-daemon.service GNOME Online Cloud account integration daemon

Keyring Tier (--keyring) ⚠️ USE WITH CAUTION

These units require careful consideration before disabling. gnome-keyring-daemon provides the org.freedesktop.secrets D-Bus API and optionally wraps ssh-agent. If CLI tools on the system use libsecret for credential storage (e.g., git credential-libsecret, NetworkManager, GNOME Passwords), gating gnome-keyring on $DISPLAY will break them in SSH sessions.

Only enable this tier if:

  • You have a separate ssh-agent setup (e.g., OpenSSH's native agent)
  • No CLI tools depend on the org.freedesktop.secrets D-Bus API
  • You don't use gnome-keyring for SSH key passphrase caching
Unit Category Description
gnome-keyring-daemon.socket Keyring GNOME Keyring socket activation
gnome-keyring-daemon.service Keyring GNOME Keyring daemon
gcr-ssh-agent.socket Keyring GCR SSH agent (split from keyring in gcr-4)
gcr-ssh-agent.service Keyring GCR SSH agent service

Note on snap-related units: snapd.session-agent.socket is Ubuntu/snap-specific and will not exist on Fedora, Arch, Debian, or other non-snap distributions. The script silently skips it when not found.

How It Works

Override Mechanism

Overrides are placed in /etc/systemd/user/<unit>.d/graphical-only.conf as drop-in files. This approach:

  • Auto-discovers installed units — only creates overrides for services that exist on the target system. Missing units are silently skipped.
  • Does not modify vendor unit files in /usr/lib/systemd/user/, preserving clean upgrade paths.
  • Survives package updates — drop-ins are not overwritten by package managers.
  • Is easily reversiblerevert cleans up all override files and empty directories.

Session Behavior

  • SSH sessions ($DISPLAY not set): Services are skipped. Only the essential session infrastructure runs (systemd --user, sd-pam, dbus-daemon, sshd, shell).
  • Graphical sessions ($DISPLAY is set): All services start normally.
  • SSH with X11 forwarding (ssh -X): $DISPLAY is set, so services will start.

Each override file contains:

[Unit]
ConditionEnvironment=DISPLAY

Usage

Discover What's Installed

Scan the system to see which known units exist and their current override status:

./lean-ssh.sh discover

Apply Overrides

# Default tier — safe for any system
sudo ./lean-ssh.sh apply

# Default + full tier
sudo ./lean-ssh.sh apply --full

# All tiers including keyring (read caveats first!)
sudo ./lean-ssh.sh apply --full --keyring

# Preview without making changes
sudo ./lean-ssh.sh apply --full --dry-run

Check Status

# Status for default tier (no root needed)
./lean-ssh.sh status

# Status for all tiers
./lean-ssh.sh status --full --keyring

Revert

# Remove ALL overrides (regardless of tier)
sudo ./lean-ssh.sh revert

# Remove only keyring tier overrides
sudo ./lean-ssh.sh revert --keyring

Quiet Mode (for Automation)

sudo ./lean-ssh.sh apply --full --quiet

Exit Codes

Code Meaning
0 All units processed successfully
1 No changes made (nothing to apply/revert)
2 Fatal error (bad arguments, missing root, unsupported systemd)

Verification

After applying, log out and back in via SSH, then check running processes:

ps -u $(whoami) -f

You should see only:

systemd --user
(sd-pam)
dbus-daemon --session    # May show as dbus-broker on Ubuntu 24.04+
sshd: user@pts/X
-bash (or your shell)

Note: Ubuntu 24.04 and later may use dbus-broker instead of dbus-daemon for the session bus. Both are normal and expected.

Compatibility

  • Ubuntu 22.04 LTS (Jammy) and later
  • Ubuntu 24.04 LTS (Noble) and later
  • Other systemd-based distributions with PipeWire (Fedora, Arch, Debian 12+) — verify unit file names match before applying, or use discover to check
  • Requires systemd 249+ (for ConditionEnvironment= support)
  • Snap-related units are Ubuntu-specific and silently skipped on other distributions
  • PulseAudio units are included for pre-PipeWire systems; both stacks are handled

Reverting

To fully revert all changes:

sudo ./lean-ssh.sh revert

Without tier flags, revert removes all overrides this script could have created, regardless of which tier was used to apply them.

Or manually:

# For each unit with an override:
sudo rm -f /etc/systemd/user/<unit>.d/graphical-only.conf
sudo rmdir /etc/systemd/user/<unit>.d 2>/dev/null

Changes take effect on next user login. To apply immediately in an active session:

systemctl --user daemon-reload

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages