Systemd socket activation application support limits for zero-downtime upgrades
0 reputation · 07 Nov 2025, 22:33 UTC
0 reputation · 07 Nov 2025, 22:33 UTC
Systemd socket activation is frequently discussed as a path to zero-downtime application upgrades on Rocky Linux because the listening socket can stay open while the service binary is replaced. The documented requirement is that the application must accept the file descriptor from systemd rather than binding the port itself.
For legacy binaries that do not support inherited file descriptors, administrators must introduce a wrapper or modify the application. The unresolved behavior concerns the transition itself: when the old service process is stopped and a new process begins accepting from the same socket, it is unclear whether systemd queues incoming connections during the gap and whether established connections to the old process are terminated immediately or allowed to drain.
What are the precise support limits for systemd socket activation when a small application is migrated without dropping active connections?
34775 reputation · 08 Nov 2025, 04:03 UTC
Systemd socket activation keeps the listening socket open across a service restart, so new connections are queued in the kernel backlog and accepted by the new process. However, established TCP connections are not transferred to the new process; they remain owned by the old process and close when that process exits. True zero-downtime for in-flight requests requires application-level graceful drain combined with systemd shutdown tuning, not socket activation alone.
LISTEN_FDS/LISTEN_PID. The application must accept the inherited FD and must not call bind() or listen() on the configured port.ListenBacklog= in the socket unit and the kernel somaxconn value). systemd does not drop these queued connections.KillMode=control-group or a short TimeoutStopSec), in-flight requests fail immediately.To avoid dropping active requests during a binary replacement, you need:
SIGTERM by stopping acceptance of new work, finishing in-flight requests, then exiting cleanly.KillMode=mixed (or process) and a TimeoutStopSec long enough for the longest request to complete. This lets the old process drain while the new process starts accepting from the same socket.ListenBacklog= and /proc/sys/net/core/somaxconn are large enough to absorb the connection spike during the restart window.Without these, the gap between stop and start is only the accept latency; established connections are not preserved across the binary replacement.
Binaries that do not support inherited file descriptors cannot use native socket activation. A wrapper (e.g., socat or a small proxy) can accept the FD and forward to the legacy binary, but this adds a network hop, changes connection semantics, and does not solve the established-connection drain problem.
# Confirm socket unit owns the port and passes FDs
systemctl cat myservice.socket
# Look for ListenStream= and no BindToDevice conflicts
# Confirm service reads LISTEN_FDS and does not bind
systemctl cat myservice.service
# Check for Environment=LISTEN_FDS=1 or similar, and no port bind in code
# Check shutdown tuning
systemctl show myservice.service -p KillMode,TimeoutStopSec,Restart
# Observe backlog and connection states during a controlled restart
ss -lntp sport = :8080
watch -n1 'ss -tan state established sport = :8080 | wc -l'
Does your application currently handle SIGTERM for graceful shutdown, or does it exit immediately on signal receipt? This determines whether TimeoutStopSec tuning alone is sufficient or whether code changes are required.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.