Make your services wake up only when they’re actually needed

Systemd & Service Management

Make your services wake up only when they’re actually needed

Technical Briefing | 10/27/2026

Most people, when they want a service running, just throw an ExecStart into a systemd unit, enable it, and walk away. That service then starts right up at boot, grabbing its port, claiming memory, and sitting there. And it’ll just wait, potentially for hours or days, until someone actually tries to connect to it. For critical, high-load services, that’s fine, it’s what you want. But for a lot of internal tooling, a dev environment service, or something that only gets hit occasionally? You’re just wasting resources, slowing down boot, and often making graceful restarts a nightmare. I’ve seen countless machines where half the memory is held by services doing absolutely nothing.

Why bother making your services lazy?

The core idea here is simple: don’t start a service until it actually has work to do. systemd socket activation pushes the “listen” part of your service out to systemd itself. The kernel opens the port, systemd watches it, and only when a connection arrives does systemd fire up your service. This isn’t just about saving RAM; it’s about a more resilient service architecture. Imagine a security service that’s rarely used but needs to be available. Or a monitoring agent that only needs to process incoming data on demand.

The two-part dance: A .socket and a .service

So how does this work? You define two unit files. First, a .socket unit. This tells systemd to open a specific network socket (or even a FIFO or a file system socket) and hold it. Then, you have your regular .service unit. The trick is that this service doesn’t bind to the port itself. Instead, systemd passes the pre-opened socket to your service’s stdin (or via file descriptor 3 onwards). Your service just reads from stdin or uses the passed file descriptor, handles the connection, and then can exit. If another connection comes in, systemd can restart the service, potentially multiple times.

/etc/systemd/system/echo-server.socket
[Unit]
Description=Echo Server Socket


[Socket]
ListenStream=8000
Accept=yes


[Install]
WantedBy=sockets.target


/etc/systemd/system/echo-server@.service
[Unit]
Description=Echo Server Instance
After=echo-server.socket


[Service]
ExecStart=/bin/cat
StandardInput=socket
StandardOutput=inherit
StandardError=inherit

That `echo-server.socket` unit tells systemd to listen on TCP port 8000. The `Accept=yes` is key here: it means systemd will spawn a new instance of the associated service *for every incoming connection*. If you omit `Accept=yes`, systemd only starts the service *once* when the first connection arrives, and passes the *listening* socket to it (meaning the service needs to handle `accept()` calls itself, typically a daemon). The `echo-server@.service` unit is an instantiated service template. The `@` means it’s a template, and systemd automatically names the instance `echo-server@<ID>.service` when it starts it. And crucially, `StandardInput=socket` is how the pre-opened *accepted connection* from systemd gets passed to our `/bin/cat` process. `cat` simply reads from its stdin (the network connection) and writes to its stdout (the network connection), effectively echoing data. We enable the socket, not the service directly.

  • Reduced boot times and lower memory footprint for infrequently used services.
  • Enables truly zero-downtime restarts and upgrades for some service types; the old service keeps running on its open connections while new connections are routed to a fresh service instance.
  • Simplifies firewall rules as systemd is the one binding the port, not the service directly.
  • Forces you to consider if your service is truly stateless or if it needs to persist across connections.
  • Not a panacea: services that need continuous background processing or stateful long-lived connections might not benefit directly or might need a different `Accept=` strategy.

You’ll need to enable `echo-server.socket` and then `systemctl daemon-reload` for these changes to take effect. Then, try `telnet localhost 8000` and watch `systemctl status echo-server@` — you’ll see instances pop up and disappear. This pattern isn’t just for netcat; it’s how `systemd-journal-gatewayd` works, or even how you could make a custom API endpoint lazy. It shifts your mental model of a service from “always on” to “on demand,” which is a powerful concept when you’re trying to squeeze more performance and resilience out of your fleet. This isn’t just some academic exercise; I’ve used this to make services much more robust during rolling updates, avoiding those nasty “connection refused” moments.

Linux Admin Automation  |  © www.ngelinux.com  |  10/27/2026

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Newest
Oldest Most Voted