Using systemd Socket Activation to Start Services on Demand
Learn how systemd socket activation starts services only on the first connection, saving resources and simplifying daemon management.
10 Sept 2026, 17:12 UTC

Problem: Services that run all the time waste resources
Many network daemons (e.g., a small metrics collector, a debug echo server, or an internal API) are started at boot and stay idle until a client connects. While they wait, they consume RAM, CPU scheduling slots, and file‑descriptor tables. On systems with many such services, this idle footprint can add up and slow boot or waste energy.
Thesis: systemd socket activation lets the service start only when needed
With socket activation, systemd itself binds the listening socket. When the first connection arrives, systemd spawns the associated service, passes the pre‑bound file descriptor to the process, and the service accepts the connection without calling bind() or listen() again. After the service exits (or is stopped), the socket remains bound, ready for the next client.
How socket activation works
Two unit files are required:
- Socket unit (
*.socket) defines the listening endpoint (IP address, port, or UNIX‑domain path) and references a matching service unit. - Service unit (
*.service) contains theExecStartline. The daemon must be aware of the environment variables$LISTEN_FDS(number of passed descriptors) and$LISTEN_PID(the service’s PID) or be wrapped withsystemd-socket-activate.
When the socket unit is started, systemd creates the socket and sets options such as TCP_NODELAY or SO_REUSEPORT as specified in the unit. No service process runs yet. On the first incoming connection, systemd increments $LISTEN_FDS, sets $LISTEN_PID to the PID of the about‑to‑be‑started service, and executes the service’s ExecStart. The daemon receives the socket on file descriptor 3 (if LISTEN_FDS=1) and can immediately call accept().
Worked example: a socket‑activated echo service
We will create a minimal echo service that uses socat to copy data from standard input to standard output. The service will be activated on TCP port 9999.
1. Create the socket unit
# /etc/systemd/system/echo.socket
[Unit]
Description=Echo socket (TCP port 9999)
[Socket]
ListenStream=9999
# Optional: increase backlog for bursty traffic
Backlog=128
[Install]
WantedBy=sockets.target
Place the file with root privileges (sudo cp echo.socket /etc/systemd/system/) and reload systemd:
sudo systemctl daemon-reload
2. Create the service unit
# /etc/systemd/system/echo.service
[Unit]
Description=Echo service activated from echo.socket
[Service]
# socat -u STDIN STDOUT copies data one‑way; change to -u for bidirectional if needed
ExecStart=/usr/bin/socat -u STDIN STDOUT
# Tell systemd the service is ready after it has processed the first notification
Type=notify
# Allow the service to inherit the passed file descriptor
StandardInput=socket
StandardOutput=socket
Again copy with sudo and reload:
sudo systemctl daemon-reload
3. Enable and start the socket
sudo systemctl enable --now echo.socket
Check that the socket is listening but no service process is running:
sudo systemctl status echo.socket
# Look for "Listening" and "Active: active (listening)"
ss -ltnp | grep 9999
# Should show a LISTEN socket owned by systemd
4. Test the activation
From another terminal (or host) run:
nc localhost 9999
Hello, world
You should see the same line echoed back. After pressing Enter a second time, the connection closes and the service exits. Verify with:
sudo systemctl status echo.service
# Should show "Active: inactive (dead)" after the connection ends
journalctl -u echo.service
# Look for lines about "Accepted connection" and the socat process ID
Trade‑offs and limitations
- First‑request latency: The client experiences the service start‑up time on the initial connection. If sub‑millisecond response is required, keep the service warmed (e.g., with a permanent instance) or use
Accept=yesfor per‑connection spawning. - Privileged ports: The socket unit runs as root to bind ports <1024, then drops privileges to the service user. Ensure the service user cannot alter the socket unit (keep it owned by root).
- Backlog size: The default
SOMAXCONNmay be too small for bursty workloads. Explicitly setBacklog=in the socket unit. - Daemon awareness: Legacy binaries that always call
bind()will fail or create a second listener. Wrap them withsystemd-socket-activateor patch the source to check$LISTEN_FDS. - Logging correlation: Journal entries show the activation event but not which client triggered a particular service instance. Add unique identifiers in your application logs if needed.
Actionable closing
If you have a network service that spends most of its time idle, consider moving it to socket activation:
- Write a socket unit that defines the listening address.
- Modify the service to accept the passed file descriptor (or use
systemd-socket-activate). - Enable the socket unit and test with a simple client like
ncorcurl. - Monitor latency with
systemd-analyze blameand adjustBacklog=orType=notifyas needed.
By letting systemd handle the socket, you reduce idle resource consumption while keeping the service available exactly when it’s needed.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.