Setting up Auto-Suspend on Ubuntu to keep SMB Transfers, Torrents, and SSH Sessions alive
This is part of a small series on turning a headless Ubuntu NUC into a proper always-available server. The other posts cover converting it from Desktop to headless and setting up reliable Wake-on-LAN with a HomeKit switch. This one covers the other half of the power story: making sure the machine actually suspends when idle, but never while something’s actually using it.

Why not just rely on GNOME’s idle timeout
On a headless box, GNOME’s power daemon isn’t even running (see the headless-conversion post), so its idle-suspend settings are irrelevant regardless. Even before converting, GNOME’s idle timer only tracks keyboard/mouse/screen activity, it has no idea an SMB transfer or torrent download is happening in the background. Something else needs to own the suspend decision, and needs to know about network activity, not just local input.
Setting up autosuspend
Rather than write a suspend/wake daemon from scratch, I used autosuspend, which has a plugin system of “checks” that determine whether the machine counts as idle. If any enabled check reports activity, the idle timer resets. A few worth knowing about:
Smb: watches for active Samba connections/file locks.NetworkBandwidth: watches throughput on given interfaces above a threshold.ActiveConnection: watches for established TCP connections to specific ports (handy for a web UI).ExternalCommand: runs any script you write and treats a zero exit code as “active.”Users: watches for logged-in sessions matching a name/terminal/host pattern.
If it’s not already installed:
sudo apt install autosuspend
Edit its config file:
sudo nano /etc/autosuspend.conf
Base config:
[general] interval = 30 idle_time = 1800 suspend_cmd = /usr/bin/systemctl suspend [check.Smb] enabled = true [check.network] class = NetworkBandwidth interfaces = <interface1>, <interface2> threshold = 1048576 direction = both

One lesson here: double check your actual interface names (ip a) against whatever’s in the config. A leftover example value like eth0 instead of your real interface name (e.g. enp3s0) fails silently rather than erroring loudly.
An active SSH session wasn’t counting as activity
I added a Users check to make sure a logged-in SSH session keeps the machine awake. Append this to the same /etc/autosuspend.conf file:
[check.RemoteUsers] class = Users enabled = true name = .* terminal = .* host = [0-9].*
Despite that, the machine suspended while I had active SSH sessions open and was actively typing commands. The Users check reads from who, which in turn reads the /run/utmp file. It turned out that file didn’t exist on this system at all:
who ls -la /run/utmp
who returned nothing, and the file was simply missing, even though w and loginctl list-sessions both correctly showed the active sessions, since they read session data from systemd-logind directly rather than the legacy utmp file. /run is a tmpfs, wiped on every boot, and normally a tmpfiles rule recreates utmp automatically at startup. On this system, no such rule existed, likely a side effect of it starting life as a Desktop install rather than a proper Server image.
Fix: create the file and add a persistent rule so it survives reboots:
sudo touch /run/utmp sudo chgrp utmp /run/utmp sudo chmod 664 /run/utmp echo 'f /run/utmp 0664 root utmp -' | sudo tee /etc/tmpfiles.d/utmp.conf sudo systemd-tmpfiles --create /etc/tmpfiles.d/utmp.conf
Existing SSH sessions won’t retroactively write an entry, so open a fresh session afterward and confirm with who that it now shows up. The /etc/tmpfiles.d/utmp.conf rule ensures the file gets recreated automatically on every future boot, without needing to redo this by hand.
Extending it for a torrent client’s web UI
I also run a headless torrent client on this box with a web UI, and wanted the machine to stay awake both while someone’s actively viewing the UI and while a download is actually transferring. Two different conditions. Append both of these to /etc/autosuspend.conf as well:
[check.WebUI] class = ActiveConnection enabled = true ports = <port-number> [check.TorrentActivity] class = ExternalCommand enabled = true command = /usr/local/bin/torrent-active-check.sh
The script checks the torrent client’s CLI output for a nonzero transfer rate and exits 0 if something’s actively downloading. Create it:
sudo nano /usr/local/bin/torrent-active-check.sh
Paste this in:
#!/bin/bash transmission-remote -n '<user>:<pass>' -l 2>/dev/null | grep -qE ' [1-9][0-9]*\.[0-9]* (KB|MB|GB)/s'
sudo chmod +x /usr/local/bin/torrent-active-check.sh
Both of these checks use class names that are worth double-checking against your installed version of autosuspend, since they’ve changed between releases. If a check fails to load with an AttributeError naming a missing class, list what’s actually available:
python3 -c "import autosuspend.checks.activity as m; print([c for c in dir(m) if not c.startswith('_')])"
Once everything’s added to /etc/autosuspend.conf, enable and restart the service, and watch the log to confirm every check loaded cleanly (no tracebacks underneath the “Configuring check…” lines):
sudo systemctl enable --now autosuspend sudo journalctl -u autosuspend -f
Result
The server now sleeps automatically after 30 minutes genuinely idle, but stays awake through active SMB transfers, torrent downloads, someone viewing the torrent web UI, and any open SSH session. Combined with the WOL setup in the other post, it wakes reliably on demand and I can flip it on or off from a HomeKit switch on my phone.
I later added a check for Jellyfin too, since none of the above catches someone streaming or browsing the media library. See Getting autosuspend to Detect Active Jellyfin Streams and Browsing for that.

If this post has been useful, support me by buying me a latte or two 🙂
