Socket Activation

systemd in Depth

Chapter 8 · Socket Activation

systemd1-7 covered time-based activation. This chapter covers a genuinely different trigger — on-demand, connection-based activation, with no real precedent in traditional init or cron.

The Problem — Services That Are Rarely Used, But Must Always Be Ready

Some services are needed only occasionally — an SSH connection arrives once in a while, a rarely-used network service gets a request once a day. Traditionally, "must always be ready to respond" meant "must always be running," consuming memory and resources continuously, even through long idle stretches with zero actual traffic.

What Socket Activation Actually Does

systemd itself opens and listens on the network (or Unix domain) socket on behalf of a service, before that service is even running. Any connection attempt is queued by the kernel/systemd; only then does systemd actually start the real service, handing it the already-open socket. From the client's own point of view this is completely invisible — the connection appears to succeed immediately either way, whether the service was already running or had to be started on-demand just now.

A .socket Unit, Paired With a .service Unit

The same two-unit pattern as systemd1-7's own .timer/.service pairing — here, .socket + .service, sharing a base name.

# myapp.socket [Unit] Description=Socket for myapp [Socket] ListenStream=8080 [Install] WantedBy=sockets.target

myapp.service itself needs no port-binding logic of its own — systemd hands the already-listening socket to the service when it starts it, via a specific, well-known file descriptor (conventionally file descriptor 3). WantedBy=sockets.target is systemd1-5's own targets material, one more dedicated target — directly parallel to systemd1-7's own timers.target.

Enabling and Testing Socket Activation

systemctl enable --now myapp.socket systemctl status myapp.socket # listening systemctl status myapp.service # inactive (dead) — until a real connection arrives

Again, enable and start the socket, not the service directly — the same pattern systemd1-7 already established for timers. The before/after state is genuinely visible: the socket shows active/listening immediately, while the service itself stays inactive until the first real connection triggers it.

Real Efficiency Benefits

Real memory and resource savings for genuinely rarely-used services — no process running at all during idle periods. A real, historical example worth naming: SSH itself was traditionally socket-activatable this way on some systems (sshd.socket alongside sshd.service) — though many modern distros run sshd as an always-on service instead, for latency reasons on a frequently-used service. Socket activation genuinely shines for rarely-used services specifically, not universally. Faster boot, too: a socket-activated service doesn't need to actually finish starting up during the boot sequence itself — only the socket (cheap to open) is needed at boot; the real service startup is deferred until first actually used.

An Honest Limitation

Not every service is a good fit. A service with genuine startup latency — loading a large dataset, warming a cache — means the first connection after any idle period genuinely waits for that startup to finish, a real, noticeable delay an always-running service wouldn't have. Socket activation trades "always consuming resources" for "occasionally slower first response" — a genuine tradeoff, not a pure, unconditional win.

Resource use while idleFirst-request latency
Always-running serviceContinuous, whether used or notNone — already warm
Socket-activated serviceNone while idle — only the socket itselfReal startup delay on the first connection after idle time
See every socket-activated unit at a glance
systemctl list-sockets shows every socket unit and what it's listening on — the socket-activation equivalent of systemd1-7's own systemctl list-timers.
Never also enable the service directly with WantedBy=multi-user.target
A socket-activated service's own unit file generally should not also carry WantedBy=multi-user.target in its own [Install] section — that would start it unconditionally at boot anyway, defeating the entire point of on-demand activation. The socket unit's own WantedBy=sockets.target is what gets enabled — not the service.

Hands-On Exercises

Exercise 1

Write a complete .socket unit (matching this chapter's own myapp.socket structure) for a service listening on port 9000, and the systemctl command needed to enable and start it.

📄 View solution
Exercise 2

Explain why a client connecting to a socket-activated service that isn't currently running experiences no visible difference from connecting to one that's already running, referencing what systemd actually does with the connection.

📄 View solution
Exercise 3

A team considers socket-activating a service that loads a 4GB dataset into memory on startup, taking about 30 seconds. Using this chapter's own honest limitation, explain why this is likely a poor fit for socket activation.

📄 View solution

Chapter 8 Quick Reference

  • Socket activation: systemd listens on a socket before the service runs, starting it only when a real connection arrives
  • .socket + .service — the same two-unit pairing pattern as timers, sharing a base name
  • The service receives the already-open socket via a well-known file descriptor — no port-binding logic of its own needed
  • Enable/start the socket, not the service — WantedBy=sockets.target, parallel to timers.target
  • Real benefits: no idle resource use, faster boot (only the cheap socket needs to be ready at boot)
  • Real tradeoff: the first connection after idle time pays the service's own real startup cost
  • Never also give the service its own WantedBy=multi-user.target — that defeats on-demand activation entirely