Socket activation lets systemd bind a port or local socket before the service process exists, so clients can connect immediately and the daemon starts only when traffic arrives. That fits small helper services, compatibility daemons, and on-demand tools that should stay idle until a request reaches a known endpoint.
A *.socket unit defines the listening endpoint and starts a matching service when traffic arrives. The example below uses demo-echo.socket on 127.0.0.1:9091 and a demo-echo@.service template, so each client connection starts one short-lived service instance that echoes the received line back.
New daemons are usually better written for Accept=no, where one long-running service receives the listening socket directly. Accept=yes remains a practical way to learn inetd-style activation and per-connection instances with a simple command such as cat. Keep the first test bound to 127.0.0.1 until the service protocol, firewall rules, and authentication requirements are ready.
[Unit] Description=Per-connection echo service for demo-echo.socket [Service] ExecStart=/bin/cat StandardInput=socket StandardOutput=socket StandardError=journal
With Accept=yes, the triggered unit must be a template named after the socket basename, here demo-echo@.service. StandardInput=socket and StandardOutput=socket connect the accepted socket to cat so the test client receives its own line back.
[Unit] Description=Socket unit for demo-echo [Socket] ListenStream=127.0.0.1:9091 Accept=yes [Install] WantedBy=sockets.target
ListenStream= accepts TCP ports, IPv6 addresses, Unix socket paths, and similar stream endpoints. Use Accept=no and a matching non-template service when the daemon handles the inherited listening socket itself.
$ sudo systemd-analyze verify /etc/systemd/system/demo-echo@.service /etc/systemd/system/demo-echo.socket
No output means the unit files parsed cleanly and the name relationship is acceptable to systemd.
$ sudo systemctl daemon-reload
This reloads unit definitions from disk before the new socket can be enabled or started.
$ sudo systemctl enable --now demo-echo.socket Created symlink /etc/systemd/system/sockets.target.wants/demo-echo.socket → /etc/systemd/system/demo-echo.socket.
Enable the *.socket unit, not the template service. Socket activation starts the matching service instances only when a client connects.
$ systemctl show demo-echo.socket -p Listen -p ActiveState -p SubState Listen=127.0.0.1:9091 (Stream) ActiveState=active SubState=listening
Look for ActiveState=active and SubState=listening before sending the first client connection.
$ printf 'socket activation works\n' | nc -N 127.0.0.1 9091 socket activation works
This example uses the OpenBSD nc syntax. Use another TCP client, or the equivalent EOF flag for the installed netcat build, when nc -N is not available.
$ systemctl status --no-pager --full demo-echo.socket
● demo-echo.socket - Socket unit for demo-echo
Loaded: loaded (/etc/systemd/system/demo-echo.socket; enabled; preset: enabled)
Active: active (listening) since Wed 2026-04-22 02:44:41 UTC; 12s ago
Listen: 127.0.0.1:9091 (Stream)
Accepted: 1; Connected: 0;
Tasks: 0 (limit: 28491)
Memory: 4.0K (peak: 264.0K)
CPU: 3ms
##### snipped #####
The socket should stay in active (listening) after the client disconnects, and Accepted: should increase after each completed test connection.
$ sudo journalctl -u demo-echo.socket -n 10 --no-pager Apr 22 02:44:41 host systemd[1]: Listening on demo-echo.socket - Socket unit for demo-echo.
Manager messages confirm whether the socket bound successfully. Service-side errors normally appear under the triggered unit rather than the socket unit, so inspect the matching service journal when the bind works but the request still fails.