skip to content

In a systemd socket unit file, what do the ListenStream= and ListenDatagram= directives each set up, and how does systemd know which service unit to start when traffic arrives on that socket?

level: juniorimportance: should knowfreq 45%

answer

  1. stream versus datagram, same family
  2. a port, an address, or a path
  3. the socket is what you enable
  4. matching base names, unless overridden
  5. order of directives sets the fd order

basics

~20 s

ListenStream= creates a connection-oriented listening socket (TCP port or AF_UNIX stream path); ListenDatagram= creates a datagram socket such as UDP. The service started is the one with the same base name — myapp.socket activates myapp.service — unless Service= names another.

solid answer

~40 s

`ListenStream=` declares a stream socket: give it a bare port number for TCP on all addresses, an `address:port` to narrow it, or an absolute filesystem path for an `AF_UNIX` stream socket. `ListenDatagram=` declares the datagram equivalent, which in practice means UDP or a datagram `AF_UNIX` socket. A single socket unit may carry several `Listen*=` directives, and they are passed to the service as descriptors 3, 4, 5 and so on in the order they appear. There are further variants such as `ListenSequentialPacket=` and `ListenFIFO=` for the less common cases. As for the target: systemd pairs the socket with the service of the same base name, so `myapp.socket` triggers `myapp.service`; setting `Service=` in the `[Socket]` section overrides that. The unit you enable for boot is the socket, not the service.

code

ini · 12 lines
ini
# /etc/systemd/system/myapp.socket
[Unit]
Description=myapp listening endpoints

[Socket]
ListenStream=127.0.0.1:8080
ListenStream=/run/myapp.sock
SocketMode=0660
SocketGroup=myapp

[Install]
WantedBy=sockets.target

go deeper

for a junior

Know that ListenStream= is the connection-oriented one and ListenDatagram= the UDP-style one, and that a bare number means a port while an absolute path means a Unix socket. Say that you enable the .socket unit.

for a middle

Explain the naming pairing and the Service= override, and that multiple Listen*= lines become descriptors 3, 4, 5 in file order. Mention FileDescriptorName= as the way to stop depending on that order.

for a senior

Show judgement about exposure and permissions: bind to a loopback address or a Unix socket where the service has no business being on the network, and set SocketMode= and SocketGroup= because a Unix socket's mode is its only access control.

for a principal

Treat the socket unit as the published interface contract for the service — the ports and paths declared there are what the rest of the fleet may depend on, so changes to them are contract changes, not implementation details.

## What a socket unit actually declares A systemd socket unit is a description of one or more listening endpoints that systemd will open on the service's behalf. Its `[Socket]` section is mostly a list of `Listen*=` directives, each of which becomes a real socket that systemd binds and listens on. ## ListenStream= `ListenStream=` creates a stream socket — connection-oriented, ordered, reliable. What it binds depends on the value you give: - `ListenStream=8080` — TCP port 8080 on all local addresses. On a dual-stack host this covers IPv4 and IPv6. - `ListenStream=127.0.0.1:8080` — only that address. - `ListenStream=/run/myapp.sock` — an `AF_UNIX` stream socket at that filesystem path. - `ListenStream=@myapp` — a socket in the abstract Unix namespace, which has no filesystem entry. For filesystem sockets the ownership and mode are worth setting explicitly with `SocketUser=`, `SocketGroup=` and `SocketMode=`, because that is the only access control a Unix socket has. ## ListenDatagram= `ListenDatagram=` is the same idea for datagram sockets: `ListenDatagram=5514` gives you UDP port 5514, an absolute path gives you a datagram Unix socket. Datagram sockets have no connections, so there is no accept step — systemd starts the service as soon as a datagram arrives, and the service reads from the inherited descriptor. This also means datagram sockets require the default `Accept=no`; per-connection instantiation makes no sense when there are no connections. ## The other variants The same family covers less common cases: `ListenSequentialPacket=` for `SOCK_SEQPACKET`, `ListenFIFO=` for a named pipe, `ListenSpecial=` for a character device or similar special file, `ListenNetlink=` and `ListenMessageQueue=` for their namesakes. You will rarely write these, but recognising them in a vendor's unit file is useful. ## Several listeners in one unit Nothing stops a unit from declaring more than one endpoint: ```ini [Socket] ListenStream=8080 ListenStream=/run/myapp.sock ``` All of them are passed to the same service, as descriptors 3 and 4 in the order written. Because that order is easy to disturb when someone edits the file, systemd lets you label groups with `FileDescriptorName=`, and the names travel to the service in the `LISTEN_FDNAMES` variable. ## Which service gets started The pairing is by name and nothing else: `myapp.socket` activates `myapp.service`. This is a default, not a hard rule — putting `Service=other.service` in the `[Socket]` section points it somewhere else, which is what you do when several sockets feed the same daemon or when the names cannot match. Once the service is running, `systemctl status myapp.service` reports the relationship on a `TriggeredBy:` line, and `systemctl list-sockets` shows every listening socket unit next to the unit it activates. ## What you enable This is the part that catches people out. To get on-demand behaviour you `systemctl enable --now myapp.socket`, and you leave `myapp.service` disabled. The socket unit carries `WantedBy=sockets.target` in its `[Install]` section, so it comes up very early in boot. If you also enable the service, the service starts at boot on its own and there is nothing left to activate on demand — you have kept the extra unit file and lost the benefit. Conversely, if you enable only the service and forget the socket, the daemon may still work, but it will be binding the port itself with no socket unit involved at all. ## Reading a vendor unit When a distribution ships both `foo.socket` and `foo.service`, the socket unit tells you the whole story of how the service is reachable: which ports and paths, whether it is localhost-only, and whether the daemon is expected to be running all the time or to be spun up on demand. It is usually the first file to read when you are asked "how does anything actually reach this daemon?".

  • You want a socket unit whose name does not match the service it starts. How do you wire that up?
    Put `Service=` in the `[Socket]` section naming the target service unit explicitly. That breaks the name-matching default and is the normal approach when several socket units feed one daemon, or when the socket has to be named after the port it serves rather than after the program. The service itself needs no change; it just has to handle however many descriptors arrive.
  • Does ListenStream=8080 listen on IPv4, IPv6, or both?
    On a dual-stack host a bare port number gives you both: systemd opens an IPv6 socket that also accepts IPv4 traffic, unless `BindIPv6Only=` says otherwise. If you need to be specific, write the address in explicitly — `ListenStream=0.0.0.0:8080` for IPv4 only, or `ListenStream=[::]:8080` for IPv6. Being explicit is the safer habit in unit files that other people will read.

saying these in an interview costs you the question

  • Says ListenStream= is for TCP and cannot take a Unix path
  • Enables the .service instead of the .socket for on-demand start
  • Thinks a socket unit can declare only one listener
  • Assumes the socket and service must share a name with no override
  • Expects Accept=yes to work on a datagram socket

context