Software Engineer's Blog

Turn an Ubuntu Laptop Into an Always-On Headless Server (Lid Closed)

Turn an Ubuntu Laptop Into an Always-On Headless Server (Lid Closed)

An old laptop makes a great home server — it’s a small box with a built-in UPS (the battery) — but Ubuntu keeps trying to put it to sleep. Closing the lid is only one of the ways it nods off. For a machine that has to stay reachable over SSH at 3 a.m. with no monitor attached, you need to shut down every suspend trigger, not just the lid.

There are three of them: the lid switch, the idle timeout, and the sleep targets themselves. A true headless server should ignore all three.

Why the GUI power settings aren’t enough

Changing “Power” in the GNOME settings menu feels like the fix, but it fails for a server in two ways:

  • GNOME settings apply only after a user logs in. If the server reboots after a power blip and sits at the login screen, closing the lid still suspends it — and it silently drops off the network.
  • They only cover the desktop session’s idea of idle, not the system-level lid and sleep behavior.

For an always-on box, you want system-level settings that hold before and without any login.

1. Ignore the lid, unconditionally

Edit /etc/systemd/logind.conf and set the lid to ignore. Because a server lives on a charger, set the external-power case too:

[Login]
HandleLidSwitch=ignore
HandleLidSwitchExternalPower=ignore

Then reload the login manager:

sudo systemctl restart systemd-logind

This is unconditional on purpose — a headless server should never sleep on lid close, docked or not. (If you actually want it to sleep when undocked and only stay awake on an external monitor, that’s a different setup — see the clamshell guide linked at the end.)

2. Stop the idle suspend

Even with the lid handled, a desktop install will suspend the machine after a stretch of inactivity. Turn that off for the on-AC case:

gsettings set org.gnome.settings-daemon.plugins.power sleep-inactive-ac-type 'nothing'

Better yet, if you’re running it truly headless, you don’t need the graphical session at all. Booting to the console frees memory and removes the whole idle-suspend machinery:

sudo systemctl set-default multi-user.target

3. Mask the sleep targets (the definitive lock)

The belt-and-suspenders step: tell systemd the machine may not suspend or hibernate at all, from any source — lid, idle, a stray systemctl suspend, an application request. Masking the four sleep targets makes it impossible:

sudo systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target

After this, the normal systemd suspend and hibernate paths are all blocked — including systemctl suspend, the lid, and idle triggers. It’s the most reliable line for a headless box because it doesn’t matter which component asks; the systemd-managed routes are simply gone.

Verify it holds

Check the lid setting is live:

$ busctl get-property org.freedesktop.login1 /org/freedesktop/login1 \
    org.freedesktop.login1.Manager HandleLidSwitch
s "ignore"

(systemctl show systemd-logind won’t work here — it reports the service unit’s properties, not logind’s lid settings, so it returns nothing. The lid values live on the login1 D-Bus Manager, which is what busctl queries.)

And confirm the targets are masked:

$ systemctl status sleep.target
   Loaded: masked (Reason: Unit sleep.target is masked.)

Then the real test: close the lid, walk away, and hold an SSH session open across it. If the connection survives and uptime keeps climbing, the box is a proper server now. This works the same on Ubuntu 20.04, 22.04, and 24.04 LTS.

Want the opposite behavior — awake only while docked to an external monitor, but still sleeping in your bag? See clamshell mode.