How to create a systemd socket unit

Socket activation lets systemd bind a listening endpoint before the service process exists, which reduces idle resource use and keeps a local port or socket path ready until the first real client arrives. That model fits small helper services, compatibility daemons, and on-demand tools that should start only when traffic reaches a known endpoint.

In systemd, a *.socket unit defines the listening endpoint and triggers a matching service when traffic arrives. Current upstream systemd.socket rules require a matching foo.service when Accept=no, or a matching foo@.service template when Accept=yes spawns one service instance per connection. The example below uses demo-echo.socket on 127.0.0.1:9091 with Accept=yes plus a simple template that echoes client input back through the accepted connection by using StandardInput=socket and StandardOutput=socket.

Current upstream systemd documentation recommends Accept=no for new daemons that can consume socket activation directly, but Accept=yes remains the clearest way to demonstrate inetd-style activation and one-connection-per-instance behavior. Binding the sample socket to 127.0.0.1 keeps the first test local to the host; move it to another address or a Unix socket path only after the service behavior, firewall policy, and authentication requirements are understood.

Steps to create a systemd socket unit:

  1. Create the service template that will handle each accepted connection.
    [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. Current systemd.exec behavior allows StandardInput=socket only for socket-activated services and requires Accept=yes or a single configured socket.

  2. Create the socket unit that listens on the chosen address and port.
    [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. The WantedBy=sockets.target install stanza follows current upstream systemd.special guidance for socket units that should be active after boot.

    When the daemon is written to consume the inherited listening socket itself, use Accept=no and a matching non-template demo-echo.service instead.

  3. Verify both unit files before reloading the manager.
    $ 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.

  4. Reload the systemd manager so it notices the new unit files.
    $ sudo systemctl daemon-reload

    This rereads the definitions from disk and rebuilds the dependency tree before the new socket can be enabled or started.

  5. Enable and start the socket unit.
    $ 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.

  6. Confirm that the socket is active and listening.
    $ systemctl status --no-pager --full demo-echo.socket | head -n 6
    ● demo-echo.socket - Socket unit for demo-echo
         Loaded: loaded (/etc/systemd/system/demo-echo.socket; enabled; preset: enabled)
         Active: active (listening) since Mon 2026-04-13 21:20:48 +08; 17ms ago
         Listen: 127.0.0.1:9091 (Stream)
       Accepted: 0; Connected: 0;
          Tasks: 0 (limit: 4543)

    The success state for the socket unit itself is active (listening). A service process may not exist yet because no client has connected.

  7. Connect to the socket and confirm that the templated service handled the request.
    $ printf 'socket activation works\n' | nc -N 127.0.0.1 9091
    socket activation works
    
    $ systemctl status --no-pager --full demo-echo.socket | head -n 7
    ● demo-echo.socket - Socket unit for demo-echo
         Loaded: loaded (/etc/systemd/system/demo-echo.socket; enabled; preset: enabled)
         Active: active (listening) since Mon 2026-04-13 21:20:48 +08; 93ms ago
         Listen: 127.0.0.1:9091 (Stream)
       Accepted: 1; Connected: 0;
          Tasks: 0 (limit: 4543)
         Memory: 4.0K (peak: 264.0K)

    The echoed line proves that systemd accepted the connection and started the matching service instance on demand. On a real application socket, replace the nc test with the protocol-specific client that the service expects.

  8. Inspect the unit journal if the socket never reaches active (listening) or the client connection hangs.
    $ sudo journalctl -u demo-echo.socket -n 10 --no-pager
    Apr 13 21:20:48 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.