systemd Socket Activation

Updated

September 8, 2026

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=true

Notify readiness

// optional: sd_notify READY=1 via NOTIFY_SOCKET

Rules

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

  1. Local: systemd-socket-activate -l 8080 ./myapp
  2. Dual mode: env set → use FD; else TCP.
  3. Pass Unix socket via activation.