Skip to content

Add systemd socket activation support#3529

Open
drizzt wants to merge 1 commit intovalkey-io:unstablefrom
drizzt:systemd-socket-activation
Open

Add systemd socket activation support#3529
drizzt wants to merge 1 commit intovalkey-io:unstablefrom
drizzt:systemd-socket-activation

Conversation

@drizzt
Copy link
Copy Markdown

@drizzt drizzt commented Apr 17, 2026

Problem

Restarting valkey-server closes its listening sockets, so clients that
connect during the restart window see ECONNREFUSED instead of a brief
delay. With systemd socket activation, systemd owns the listening fds
and holds them open across service restarts, so in-flight connections
simply wait in the kernel backlog while the new process starts up.

Fix

When started under a systemd .socket unit, valkey inherits the
listening fds via LISTEN_FDS and skips its own bind()/listen()
for those listeners.

Detection is automatic. After config load, sd_listen_fds() is
consulted and each inherited fd is matched to a configured listener by
probing:

  • AF_UNIX fds are matched against server.unixsocket by sun_path.
  • AF_INET/AF_INET6 fds have their local address and port extracted via
    anetFdToString() and matched against server.port, server.tls_port,
    or the cluster bus port (server.cluster_port, falling back to
    server.port + CLUSTER_PORT_INCR), and against server.bindaddr[]
    with the Valkey wildcards * (any IPv4) and ::* (any IPv6).

An fd whose address/port does not match any configured listener is
closed with a warning, so a .socket unit that exposes a broader
address than valkey.conf authorizes will not silently widen exposure.

Listeners with no matching inherited fd fall through to normal
bind()/listen(), so partial activation (e.g. unix-only) works. An
inherited unix socket is never unlinked on shutdown since systemd owns
the path.

Runtime reconfiguration via CONFIG SET port / bind / tls-port is
refused on an inherited listener: closing and re-binding would
relinquish the fd that systemd still tracks and the subsequent bind()
can fail or race. A clear log message asks the operator to adjust the
.socket unit instead.

Build integration

  • Make: no change; USE_SYSTEMD already detects libsystemd and
    defines HAVE_LIBSYSTEMD.
  • CMake: add matching pkg-config detection of libsystemd, enabling
    HAVE_LIBSYSTEMD and sd_notify parity with the Make build.
    USE_SYSTEMD=yes is honored as a hard requirement (fatal if
    pkg-config or libsystemd is missing), matching the Make build.

The sample utils/systemd-valkey_server.service is documented with
usage instructions and a companion utils/systemd-valkey_server.socket
unit is provided.

Testing

New tests in tests/unit/socket-activation.tcl use systemd-socket-activate
(systemd >= 258, with --now) to simulate activation and verify:

  • TCP and unix inherited fds are adopted.
  • A mismatched bind address is rejected rather than adopted.
  • The unix socket path is preserved on shutdown.
  • CONFIG SET port is refused on an inherited listener.
  • Self-binding is unaffected when no fds are inherited.

The tests skip cleanly when systemd-socket-activate or libsystemd is
not available.

Allows valkey-server to be restarted without causing ECONNREFUSED for
connecting clients. When started under a systemd socket unit, systemd
owns the listening fds and holds them open across service restarts;
valkey inherits them via LISTEN_FDS and skips its own bind()/listen()
for those listeners.

Detection is automatic. After config load, sd_listen_fds() is consulted
and each inherited fd is matched to a configured listener by probing:

  - AF_UNIX fds are matched against server.unixsocket by sun_path.
  - AF_INET/6 fds have their local address and port extracted via
    anetFdToString() and matched against server.port, server.tls_port,
    or the cluster bus port (server.cluster_port, falling back to
    server.port + CLUSTER_PORT_INCR), and against server.bindaddr[]
    with the Valkey wildcards `*` (any IPv4) and `::*` (any IPv6).

An fd whose address/port does not match any configured listener is
closed with a warning, so a `.socket` unit that exposes a broader
address than valkey.conf authorizes will not silently widen exposure.

Listeners with no matching inherited fd fall through to normal
bind()/listen(), so partial activation (e.g. unix-only) works. An
inherited unix socket is never unlinked on shutdown since systemd owns
the path.

Runtime reconfiguration via CONFIG SET port / bind / tls-port is
refused on an inherited listener: closing and re-binding would
relinquish the fd that systemd still tracks and the subsequent bind()
can fail or race. A clear log message asks the operator to adjust the
.socket unit instead.

Build integration:
  - Make: no change; USE_SYSTEMD already detects libsystemd and defines
    HAVE_LIBSYSTEMD.
  - CMake: add matching pkg-config detection of libsystemd, enabling
    HAVE_LIBSYSTEMD and sd_notify parity with the Make build.
    USE_SYSTEMD=yes is honored as a hard requirement (fatal if
    pkg-config or libsystemd is missing), matching the Make build.

The sample utils/systemd-valkey_server.service is documented with
usage instructions and a companion utils/systemd-valkey_server.socket
unit is provided.

Tests in tests/unit/socket-activation.tcl use systemd-socket-activate
(systemd >= 258, with --now) to simulate activation and verify that
the server adopts TCP and unix inherited fds, that a mismatched bind
address is rejected rather than adopted, that the unix socket path is
preserved on shutdown, that CONFIG SET port is refused on an inherited
listener, and that self-binding is unaffected when no fds are
inherited.  They skip cleanly when the helper or libsystemd is not
available.

Signed-off-by: Timothy Redaelli <tredaelli@redhat.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant