Systemd Socket Activation: Can Reload Preserve Existing Connections?
0 reputation · 18 Apr 2021, 10:35 UTC
0 reputation · 18 Apr 2021, 10:35 UTC
The application is a small Unix daemon that relies on a single TCP socket. The objective is to upgrade the binary to a newer version while keeping the service available to existing clients.
Systemd supports socket activation, allowing the service to be started on demand and to inherit the listening socket. Reloading the unit with systemctl reload restarts the executable, but documentation does not guarantee that the old process will hand off the socket to the new one without closing existing connections. Reports suggest that on some systemd releases the socket may be closed immediately, causing a reset for clients, whereas newer releases may allow a graceful hand‑over if the service implements proper signal handling. The behavior also depends on the ExecReload implementation.
Given these uncertainties, the key questions are:
1) Does systemd guarantee that a socket‑activated service can preserve active connections across a reload, or is it up to the service to implement a graceful hand‑over?
2) How does the behavior differ between systemd v237, v240, and v252, and can we rely on a specific release for this feature?
3) What minimal unit configuration or ExecReload script is required to achieve zero‑downtime reloads with socket activation?
35275 reputation · 18 Apr 2021, 21:08 UTC
Systemd guarantees that the listening socket inherited via socket activation remains open across a reload, but it does **not** transfer the accepted connection file descriptors from the old process to the new one. If the old process exits or closes those sockets, clients see a reset (RST). Therefore zero‑downtime reload depends entirely on the service draining or passing those sockets itself.
The behavior hinges on the ExecReload directive and the service’s signal handling. If ExecReload sends a signal (e.g., SIGUSR1) that the binary interprets as “stop accepting new connections and finish existing ones”, the old process can keep the accepted sockets open until they finish, then exit. The new instance, started by systemd, inherits the same listening socket and can continue accepting traffic.
[Unit]
Description=Example socket‑activated daemon
[Socket]
ListenStream=12345
[Service]
Type=notify # or Type=simple with explicit signal handling
ExecStart=/usr/local/bin/mydaemon
ExecReload=/bin/kill -s SIGUSR1 $MAINPID
# The daemon must:
# 1. On SIGUSR1, stop calling accept() and wait for existing connections to close.
# 2. Optionally pass those sockets to a child via UNIX domain socket or fd‑passing.
# 3. Exit only after all accepted sockets are closed.
With this setup, systemctl reload myservice.socket (or systemctl reload myservice.service) will send SIGUSR1, allowing the old instance to drain connections while the listening socket stays open, and the new instance will take over without client‑visible reset.
If you cannot modify the binary to handle SIGUSR1, please confirm whether the daemon already ignores SIGTERM/SIGINT until it finishes existing work, or whether an external helper (e.g., systemd‑socket‑activate with fd‑passing) is available. This detail determines whether the above unit changes are sufficient or if additional wrapper logic is required.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.