systemd Socket Activation
systemd Socket Activation
Overview
Socket activation: systemd (or another supervisor) binds the socket and passes the FD to your process. Benefits: on-demand start, privilege separation (bind :80 as root, run app as user), faster parallel startup.
How FDs arrive
systemd sets:
LISTEN_FDS=1
LISTEN_PID=<pid>
FDs start at 3 (SD_LISTEN_FDS_START).
Minimal accept (stdlib)
func listenersFromSystemd() ([]net.Listener, error) {
pid, _ := strconv.Atoi(os.Getenv("LISTEN_PID"))
if pid != os.Getpid() && os.Getenv("LISTEN_PID") != "" {
return nil, fmt.Errorf("LISTEN_PID mismatch")
}
n, _ := strconv.Atoi(os.Getenv("LISTEN_FDS"))
if n < 1 {
return nil, nil
}
var out []net.Listener
for i := 0; i < n; i++ {
fd := 3 + i
f := os.NewFile(uintptr(fd), "systemd")
ln, err := net.FileListener(f)
f.Close() // FileListener dups
if err != nil {
return nil, err
}
out = append(out, ln)
}
return out, nil
}lns, err := listenersFromSystemd()
if len(lns) == 0 {
// fallback Listen for local dev
ln, _ := net.Listen("tcp", *addr)
lns = []net.Listener{ln}
}
srv := &http.Server{Handler: mux}
_ = srv.Serve(lns[0])Libraries: coreos/go-systemd/activation for production edge cases.
Unit sketch
# myapp.socket
[Socket]
ListenStream=80
# myapp.service
[Service]
ExecStart=/usr/local/bin/myapp
NonBlocking=trueNotify readiness
// optional: sd_notify READY=1 via NOTIFY_SOCKETRules
| Do | Don’t |
|---|---|
| Fallback Listen for dev | Require systemd on laptops |
| Verify LISTEN_PID | Assume FD 3 always valid |
| Shutdown via Server.Shutdown | Ignore inherited FDs on exit |
Try next
- Local:
systemd-socket-activate -l 8080 ./myapp
- Dual mode: env set → use FD; else TCP.
- Pass Unix socket via activation.