On a Linux host where a web server and an application server run side by side, what do you gain and what do you give up by connecting them over a Unix domain socket instead of a TCP socket on 127.0.0.1?
answer
- a path, not an address and port
- no handshake, no ports
- who may open the file
- the kernel knows the peer's uid
- same host only, forever
basics
~20 sA Unix domain socket skips the TCP/IP stack entirely: no handshake, no ephemeral ports, no TIME_WAIT, lower overhead, and access is controlled by filesystem permissions on the socket file. The cost is that it only works on the same host and leaves a stale file behind after an unclean exit.
solid answer
~50 sA Unix domain socket is an `AF_UNIX` endpoint: the kernel copies data between two processes on the same machine with no IP, no ports and no TCP state machine. You therefore avoid the handshake, checksums, `TIME_WAIT` and ephemeral-port exhaustion, and you typically get measurably lower latency and higher throughput than loopback TCP. Security is the bigger win: the endpoint is a filesystem path, so its mode and ownership — and the permissions of the directory containing it — decide who may connect, and nothing on the network can reach it at all, whereas a loopback listener is reachable by every local user and by anything that gets a foothold in the namespace. The server can also read the peer's uid, gid and pid with `SO_PEERCRED`, and pass open file descriptors over the socket with `SCM_RIGHTS`. The costs are real: same-host only, so it forecloses moving the two components apart, and a crashed server leaves the socket file behind so the next `bind()` fails until it is unlinked.
code
python · 11 linesimport os, socket
path = "/tmp/demo.sock"
if os.path.exists(path):
os.unlink(path) # clear a stale socket file
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
s.bind(path)
os.chmod(path, 0o660) # access control lives in the file mode
s.listen(64)
print("listening on", s.getsockname())go deeper
Know that a Unix domain socket is addressed by a filesystem path rather than an IP address and port, and that it works only between processes on the same machine.
Explain the concrete differences: no TCP handshake, no ephemeral ports or TIME_WAIT, and access controlled by the socket file's ownership and mode plus its directory's permissions.
Argue the security case over the performance case, cite SO_PEERCRED for peer identity and SCM_RIGHTS for descriptor passing, and recognise a stale socket file as an unlink problem rather than a TIME_WAIT one.
Weigh the coupling you are buying: a Unix socket enforces co-location and OS-level authorisation, while loopback TCP preserves the option to split the components later. Decide deliberately, and account for container boundaries and the abstract namespace's weaker access control.
## What AF_UNIX actually is A socket's address family decides what an address means and which code path the kernel takes. `AF_INET` addresses are IP address plus port, and data goes through the whole IP and TCP machinery. `AF_UNIX` addresses are a filesystem pathname, and the kernel simply moves bytes between two processes on the same machine — no addressing, no routing, no segmentation, no acknowledgements. The socket API is otherwise identical: `socket()`, `bind()`, `listen()`, `accept()`, `connect()`, `read()`, `write()`. Unix domain sockets come in the same types as network sockets: `SOCK_STREAM` (a reliable byte stream, the usual choice), `SOCK_DGRAM` (message-oriented, and unlike UDP it is reliable and ordered because there is no network in between), and `SOCK_SEQPACKET` (reliable, ordered, message-preserving). ## What you gain **No TCP overhead.** There is no three-way handshake to open, no window management, no checksums, no retransmission logic and no connection teardown states. Loopback TCP is fast, but it still runs the state machine. For a busy request path between a reverse proxy and an application server, the difference is measurable, though rarely the largest term in a request's latency budget. **No port-space problems.** Nothing draws an ephemeral port, so a high connect rate cannot exhaust `net.ipv4.ip_local_port_range`, and there is no `TIME_WAIT` accumulation. Teams that switch a proxy-to-app link from loopback TCP to a Unix socket often do so precisely to make those two problems disappear. **Filesystem access control.** This is the argument that usually wins. The endpoint is a path such as `/run/php-fpm/www.sock`, with an owner, a group and a mode. Only processes that can traverse the containing directory and open that file can connect — you can express "only the web server's user may talk to the app server" with `chown` and `chmod`, no firewall rules involved. By contrast, a listener on `127.0.0.1:9000` is reachable by *every* local user account and every local process; there is no per-connection identity check built into TCP. Remember that both the directory permissions and the socket file's own mode matter, and that a `umask` that widens the mode at creation time is a real misconfiguration. **Peer identity and descriptor passing.** Because both ends are local processes, the kernel knows who they are. `SO_PEERCRED` lets a server retrieve the connecting process's uid, gid and pid — genuine authentication with no shared secret. `SCM_RIGHTS` ancillary messages let one process hand an open file descriptor to another, which is how socket-activation systems and privilege-separated designs hand off listening sockets. Neither has any TCP equivalent. ## What you give up **Locality.** The two processes must be on the same machine, and on the same filesystem view of that path — the two ends of a container boundary need the socket's directory bind-mounted into both, which is more coupling than a TCP address. The day you want to move the application server to another host, a TCP address is a config change and a Unix socket is a redesign. **Stale socket files.** If the server dies without unlinking its path, the file remains. The next start calls `bind()` on an existing path and fails with `EADDRINUSE`. This looks superficially like the TCP "Address already in use" problem but has nothing to do with `TIME_WAIT` and is not fixed by `SO_REUSEADDR` — the server must `unlink()` the stale path before binding, which well-written daemons and service managers do on startup. **Small operational frictions.** The path length is bounded by the address structure (about 108 bytes on Linux), and ordinary network tooling — connecting with a TCP client, capturing packets, pointing a health checker at a host and port — does not apply in the same way. ## The Linux-specific abstract namespace Linux adds a variant: if the address begins with a null byte, the socket lives in an *abstract* namespace with no filesystem entry at all. It vanishes automatically when the last reference closes, so there is no stale-file problem — but there is also no file to set permissions on, so access is bounded only by the network namespace the process is in. That is a real security consideration, not a detail: abstract sockets trade filesystem access control for convenience. ## Choosing between them Use a Unix domain socket when the two components are deliberately co-located and you want the OS to enforce who may talk to whom — a reverse proxy in front of an application server, a client talking to a local daemon's control socket. Use loopback TCP when you value the option to relocate a component, when the client library or platform cannot address a path, or when the same code must run distributed and co-located without a second code path. The mistake to avoid is treating the choice as purely a performance question: on most workloads the access-control difference matters more than the microseconds.
- A daemon crashed and now refuses to start with "Address already in use" on its Unix socket path. Is this TIME_WAIT?No. Nothing about TCP is involved — the leftover socket file simply still exists on disk, and `bind()` refuses an existing path. `SO_REUSEADDR` does not help and waiting 60 seconds changes nothing. The server must `unlink()` the path before binding, which is why robust daemons do that unconditionally on startup.
- How can a server using a Unix domain socket authenticate the process on the other end?With `SO_PEERCRED`, which returns the connecting process's uid, gid and pid as the kernel knows them — not as the peer claims them. That gives real local authentication with no token exchange, and it is why control sockets for system daemons can safely authorise by uid. TCP offers nothing equivalent, because the kernel has no idea who a remote peer is.
- What changes if the socket is created in Linux's abstract namespace instead of on the filesystem?The address starts with a null byte, no file is created, and the socket disappears when the last reference closes — so stale-path failures vanish. The tradeoff is that there is no file to own or `chmod`, so filesystem permissions no longer restrict who may connect; reachability is bounded only by the network namespace, which is a weaker control.
saying these in an interview costs you the question
- Unix sockets are just faster loopback TCP
- You can reach a Unix socket from another host by IP
- TIME_WAIT is why the socket file blocks a restart
- A loopback TCP listener is private to the application
- Permissions on the socket file do not affect connecting