The example below creates a TCP server listening on a stream (i.e. SOCK_STREAM
) socket. A similar approach can be followed to
create a UDP server on a datagram (i.e. SOCK_DGRAM
) socket. See man systemd.socket
for details.
Create an simple echo server at ~/tmp/foo/serve.py
.
#!/usr/bin/env python3
from socketserver import TCPServer, StreamRequestHandler
import socket
import logging
class Handler(StreamRequestHandler):
def handle(self):
self.data = self.rfile.readline().strip()
logging.info("From <%s>: %s" % (self.client_address, self.data))
self.wfile.write(self.data.upper() + "\r\n".encode("utf-8"))
class Server(TCPServer):
# The constant would be better initialized by a systemd module
SYSTEMD_FIRST_SOCKET_FD = 3
def __init__(self, server_address, handler_cls):
# Invoke base but omit bind/listen steps (performed by systemd activation!)
TCPServer.__init__(
self, server_address, handler_cls, bind_and_activate=False)
# Override socket
self.socket = socket.fromfd(
self.SYSTEMD_FIRST_SOCKET_FD, self.address_family, self.socket_type)
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO)
HOST, PORT = "localhost", 9999 # not really needed here
server = Server((HOST, PORT), Handler)
server.serve_forever()
Create the service definition unit file at ~/.config/systemd/user/foo.service
(note that this unit refers to foo.socket
):
[Unit]
Description=Foo Service
After=network.target foo.socket
Requires=foo.socket
[Service]
Type=simple
ExecStart=/usr/bin/python3 %h/tmp/foo/serve.py
TimeoutStopSec=5
[Install]
WantedBy=default.target
Create the socket definition unit file at ~/.config/systemd/user/foo.socket
:
[Unit]
Description=Foo Socket
PartOf=foo.service
[Socket]
ListenStream=127.0.0.1:9999
[Install]
WantedBy=sockets.target
systemctl --user daemon-reload
systemctl --user start foo.socket
Send a message to the listening service:
date | netcat 127.0.0.1 9999
Watch the log for the echo:
journalctl -f --user-unit foo.service
Forked by @kylemanna:
- Convert docs to use the systemd user daemon
- Update to work with python3
- More logs
@bharat76 you do not need to start the service manually. The socket itself contains a reference (
PartOf
) to the service, so when Systemd detects a connection to the defined port, it immediately starts the service when it's not running.The only caveat is if you have a service with long bootstrap (e.g. it does not bind to the port right before bootstrapping the app but the port binding is part of the bootstrap process) then the first few connections could be potentially dropped since the Systemd itself is not able to handle the connection it only terminates it.