socat: Forwarding Unix Sockets Over TCP
Sometimes you need to reach a Unix socket from a host where that socket doesn’t physically exist. SSH tunnels won’t help — they only work with TCP ports. socat solves this: it opens a TCP listener and forwards connections to a Unix socket, and the client just connects over the network.
Installation
The package is available in every major distribution. On Debian/Ubuntu:
On RHEL/CentOS:
Alpine:
Verify:
Basic Forwarding: TCP-LISTEN + UNIX-CONNECT
Server side. Listen on a TCP port and redirect traffic to a Unix socket on connection:
Flags:
TCP-LISTEN:2375— opens port 2375fork— spawns a child process for each connection; without it socat accepts one connection and exits
On the client side, work as usual — for example, curl the Docker API:
If the client is on a remote host, specify the server IP:
Docker listens on the local socket by default. Exposing TCP-LISTEN externally without TLS or firewall is a risk. Restrict the bind to an interface: TCP-LISTEN:2375,bind=127.0.0.1.
Stop the forward — Ctrl+C or kill by PID.
Client Test via STDIO
To quickly verify socket availability or send a manual command, use STDIO on the client side:
After starting, enter raw HTTP requests. Example session:
Exit — Ctrl+D or Ctrl+C. This is handy for debugging APIs without curl and without setting environment variables.
For a TCP connection over the network, the client runs symmetrically:
Abstract vs Filesystem Sockets
Unix sockets come in two types. The difference matters for socat operation.
Filesystem sockets — bound to the filesystem. Path starts with /:
Abstract sockets — live in kernel memory, have no filesystem representation. Path starts with \0 or @ (ASCII zero and at-sign). Docker in rootless mode uses these:
Check socket type:
In socat syntax:
@/path/to/socket— abstract socket/path/to/socket— filesystem socket
Abstract sockets are invisible to processes without namespace access. This is an advantage for isolation but complicates forwarding between containers.
Timeout Flags
By default, socat waits forever. For automation and scripts, you need timeouts.
| Flag | Description |
|---|---|
readtimeout=SECONDS | Read timeout |
writetimeout=SECONDS | Write timeout |
timeout=SECONDS | Timeout for both operations |
Example with a general timeout:
Connection closes after 30 seconds of inactivity.
Separate timeouts for client and server:
In cron scripts or systemd units, set a timeout or the process will hang on network disruption:
For infinite waiting without fork, forever works, but in production combine it with system limits:
reuseaddr lets you quickly restart socat without “Address already in use” errors.
Common Errors
Permission denied accessing the socket
Connection refused
Verify socat is running and listening on the port:
Firewall:
One request — and socat dies
Missing fork. Each socat instance handles one connection and exits. Add the flag:
Abstract socket not found
Ensure the correct prefix. Docker in rootless uses @, but this is ASCII 0:
In socat, write @/docker.sock, not @@/docker.sock.
Systemd Service for Persistent Forwarding
For a permanent forward, wrap it in a systemd unit:
Don’t forget to restrict the bind to an interface if you don’t want the port exposed externally.
In Closing
socat is the Unix way for transparent forwarding of anything to anywhere. For Unix sockets over TCP, two processes and a minute of configuration are enough. Keep timeouts in mind, don’t forget fork, and don’t expose ports without authentication on public networks.