Home / Alt manpages / systemd-socket-activate(1)

  • systemd-socket-activate(1)
  • User command
  • linux

Test a Socket-Activated Daemon Safely with systemd-socket-activate

You will run a daemon behind a temporary listening socket, send it a real connection, and confirm how systemd-socket-activate passes that socket to the child. This is useful before writing a .socket unit or changing an existing service. Allow about fifteen minutes. You need the systemd package, a test daemon, and an unused local TCP port. The examples are unprivileged and bind only to loopback.

1. Check the installed tool

This guide follows systemd 255, installed here as package version 255.4-1ubuntu8.17. Options and diagnostics can differ on older releases. Confirm the binary and read its short option summary:

$ command -v systemd-socket-activate
/usr/bin/systemd-socket-activate
$ systemd-socket-activate --version
systemd 255 (255.4-1ubuntu8.17)
$ systemd-socket-activate --help
systemd-socket-activate [OPTIONS...]

Listen on sockets and launch child on connection.

The daemon command belongs after the options for systemd-socket-activate. Keep that boundary visible when a child has options of its own. A common distraction is assuming that this command creates or enables a persistent systemd unit. It does not: it starts a temporary listener and launches the requested process for testing.

Checkpoint

You have an installed version and a child command you can run harmlessly. The rest of this guide uses cat, which echoes input and then exits when its connection closes.

2. Start a loopback echo service

Choose a high, unused port. The address 127.0.0.1 prevents other machines from connecting. The -l option chooses the listening address, --inetd maps the accepted connection to the child's standard input and output, and -a starts a separate child for each connection.

$ systemd-socket-activate -l 127.0.0.1:24761 --inetd -a cat
Listening on 127.0.0.1:24761 as 3.

Leave this process running in one terminal. In a second terminal, connect with a client available on your machine. For OpenBSD netcat, the -N option closes the connection after standard input reaches end of file:

$ printf 'socket-activate-ok\n' | nc -N 127.0.0.1 24761
socket-activate-ok

The listener's terminal should report a connection and a child that exits with code 0. Exact process IDs and client port numbers vary:

Connection from 127.0.0.1:48840 to 127.0.0.1:24761
Spawned cat (cat) as PID 1636503.
Execing cat (cat)
Child 1636503 died with code 0

If your netcat uses different close options, use its local help output or connect interactively and press Ctrl-D after typing the test line. Do not expose a test daemon on 0.0.0.0 unless you have deliberately considered who can reach it.

Stop the temporary listener with Ctrl-C when finished. There is no unit file or enablement to undo. If the port is already occupied, choose another port rather than stopping an unrelated service.

3. Understand the two descriptor conventions

Without --inetd, the child inherits its normal standard input and output, and listening sockets are passed using systemd's socket-activation convention. The first socket created by this command is file descriptor 3. The environment tells a socket-aware program how many descriptors to inspect through LISTEN_FDS and which process should receive them through LISTEN_PID.

With --inetd, the accepted connection itself becomes standard input and standard output. That is why plain cat works in the previous example. This mode is convenient for daemons designed for the older inetd style, but it is not interchangeable with a program that calls sd_listen_fds().

Checkpoint

Choose the interface your daemon expects before debugging the daemon. Use --inetd when it reads and writes the connected stream through standard input and output. Omit it when the program expects systemd socket activation and reads descriptor 3 or later.

4. Pass a socket-aware daemon

Replace /path/to/daemon with the absolute path to a program that understands systemd socket activation. Put its arguments after the daemon name:

$ systemd-socket-activate -l 127.0.0.1:24761 /path/to/daemon --daemon-option VALUE

In this form, the daemon's standard input and output remain inherited from the launcher, so its logs normally appear in the launching terminal. The listener hands the socket through the standard activation environment and descriptor layout. A daemon that expects inetd input will appear idle or fail because it is looking in the wrong place; retry the same test with --inetd only if its documentation says that is the protocol it supports.

More than one --listen option can be used. Existing descriptors supplied through LISTEN_FDS are retained in their original positions, while sockets specified with --listen use consecutive descriptors. If the daemon distinguishes descriptors by name, supply matching names:

$ systemd-socket-activate \
    -l 127.0.0.1:24761 \
    --fdname=public \
    /path/to/daemon

The name is made available through the activation metadata used by sd_listen_fds_with_names(). It does not rename a network port, and a name supplied for a non-existent extra descriptor is ignored.

5. Add only the environment the child needs

Use -E or --setenv to add an environment variable to the launched process. An explicit value is easiest to audit:

$ systemd-socket-activate \
    -l 127.0.0.1:24761 \
    --inetd -a \
    --setenv=APP_MODE=test \
    cat

If you write only a variable name, the launcher copies that variable's current value into the child environment:

$ export APP_MODE=test
$ systemd-socket-activate -l 127.0.0.1:24761 --setenv=APP_MODE /path/to/daemon

Quote values containing spaces or shell metacharacters. Do not put passwords, API tokens or production connection strings into a command line, because command lines and shell history can be observable. Prefer a temporary test value and clear it afterwards with unset APP_MODE if it is no longer needed.

6. Diagnose the common failures

A bind failure usually means that the address is already in use, the address is not configured, or the process lacks permission for a restricted port. Check the port without changing services:

$ ss -ltn '( sport = :24761 )'
$ systemd-socket-activate -l 127.0.0.1:24762 --inetd -a cat

Using a different high port normally avoids the need for sudo. Do not grant elevated privileges merely to hide a port conflict. If the child exits immediately, run it directly first, confirm its arguments, then check whether it expects inetd descriptors or the systemd activation protocol.

When a client connects but receives no response, confirm that the listener is in --inetd mode for a standard-input program such as cat. For a socket-aware daemon, omit --inetd and inspect the daemon's own error output. A successful listener start proves only that the socket was created; it does not prove that the daemon understood the descriptor.

Use --datagram or --seqpacket only when the daemon and client require those socket types. They are alternatives, not settings to combine: the manual forbids using both together. Start with the default stream socket unless the protocol specifically requires another type.

Done means

  • You checked the installed systemd-socket-activate version and command boundary.
  • A loopback listener accepted a connection and a harmless child returned successfully.
  • You selected --inetd or systemd descriptor passing to match the daemon.
  • Any environment variables were test-specific and free of credentials.
  • You stopped the temporary listener and changed no persistent unit or service configuration.