Socket activation is the idea of activating a daemon or service not by manually starting it, but by merely pre-exposing the socket that is used for communication with that service. This has several advantages:
- system boot speeds up as less things need to actually be started at boot time
- system resource usage is reduced as less services actually run1
- you can restart services behind the socket invisible to the client (if the service gracefully takes up the connection on the socket again) without loosing messages2
The two requirements for this are:
- a daemon that manages these sockets and activates the corresponding process once communication happens
- the socket-activated daemon being able to work with a preestablished socket-connection
For the first part, systemd got us covered. The second part must be implemented in the daemon code, but we can regardless activate any socket based service, even if they don’t implement socket activation themselves with a little help. Let’s take a look at how things work in this order.
Systemd socket activation: The principle
This will be a brief rundown on how systemd’s socket activation works (see here for all the details):
To activate a service via its socket, we need to define two units: the service and the socket.
A socket can be a network socket, a unix socket and several other connection types.
In this post, however, we will exemplarily focus on network sockets.
We place the service file of our socket-activatable service (we will come back to what this means in a moment) as
[Unit] Description=Socket-activatable example service [Service] ExecStart=/usr/bin/myservice NonBlocking=True
and we place the corresponding socket unit (in this case a TCP-network-socket) as
[Unit] Description=Socket for example service [Socket] # In this example we only listen on localhost ListenStream=127.0.0.1:1234 NoDelay=true [Install] WantedBy=sockets.target
Note that the service does not require an install section, only the socket does3.
We then enable our socket via
systemctl enable --now myservice.socket.
Now we have an inactive service unit, but an active socket which is set up by systemd.
The moment any activity occurs on this socket, systemd will start
myservice.service and hand over the socket, buffering what is already written to it until the service takes over.
Leveraging socket activation in practice
In practice, we can distinguish three different cases:
1. Socket activation natively supported already
If the service in question already natively supports socket activation, simply activate the corresponding
socket unit (or create one if it doesn’t exist yet).
systemctl list-units | grep socket for
socket-units already available on the system.
2. Fault tolerance and boot time optimization desired only
If our service is intended to be started at boot and socket activation is intended only to provide transparent restarts and boot parallelization, we can simply use systemd’s
man systemd-socket-proxyd or here for details and usage examples.
Make sure you add an
[Install]-section to the service file and enable the service itself as well then as
systemd-socket-proxyd merely decouples the service unit from its socket, but doesn’t start it automatically.
3. On-demand starting and stopping of arbitrary services
If our service does not support socket activation natively and we want it to start not at boot time, but on-demand, we can use the tool socket-activate.
For this, configure
actual.service to listen on localhost on a different port such as
127.0.0.1:12345 and create two units:
[Unit] Description=Socket-activate proxy for example service [Service] ExecStart=/usr/bin/socket-activate -u "actual.service" -a "127.0.0.1:12345" NonBlocking=True
as well as a corresponding
socket-activate-actual.socket as noted above, listening on the actual desired port.
On the first connection to the socket,
socket-activate-actual.service will be started and in turn start
actual.service, proxying all traffic to it.
socket-activate is invoked with an additional
-t <timeout>, then both
socket-activate-actual.service as well as
actual.service are stopped again when no activity is detected for the specified timeout.
Especially as socket-activated services can terminate once their job is done, as they simply get reactivated next time someone connects to their communication socket again. ↩︎
This includes crashes or upgrades of the service, no further data written to the socket will be lost, only the data the service has already read from it before it crashed or was restarted. ↩︎
The service may still have an
[Install]section to be started conventionally or already being started even if no one has yet connected to its socket. In the latter case the socket activation would primarily serve as restart resilience and boot parallelization. ↩︎