Mastering the socat Command on Linux: Port Forwarding, Relays, and SSL Tunnels in Practice

Network tutorial - IT technology blog
Network tutorial - IT technology blog

When the firewall blocks you but work must go on

Over two years ago, I ran into a frustrating situation: an internal server in our datacenter was running a service on port 5432 (PostgreSQL), but the firewall only allowed traffic through port 443. The dev team needed remote access but didn’t want to touch the firewall config — because touching it meant opening a ticket, waiting for approval, and losing an entire day.

The solution I reached for that day was socat. One command, done in 30 seconds. Ever since, I’ve kept this tool handy whenever I need to “bridge” two network endpoints without configuring anything complicated.

socat (short for SOcket CAT) is a command-line tool that creates bidirectional connections between any two addresses. Those endpoints can be TCP ports, UDP ports, Unix sockets, files, or even stdin/stdout. If netcat is a pocket knife, socat is a Swiss Army knife with a screwdriver and bottle opener thrown in.

How socat works

Just remember one pattern:

socat [options] <address1> <address2>

Declare two “addresses” and socat handles the rest — establishing the connection and shuttling data back and forth between the two ends. It supports a wide range of address types:

  • TCP:host:port — connect via TCP to host:port
  • TCP-LISTEN:port — listen for TCP connections on a port
  • UDP:host:port / UDP-LISTEN:port — same but for UDP
  • SSL:host:port — SSL/TLS connection
  • UNIX-CONNECT:/path/to/socket — Unix domain socket
  • EXEC:/path/to/program — run a program and pipe its stdio
  • STDIN / STDOUT / FILE:/path — files and stdio

Quick install before we get started:

# Debian/Ubuntu
sudo apt install socat

# RHEL/CentOS/Rocky
sudo dnf install socat

Hands-on: The most common use cases

1. Simple port forwarding

This is the use case I reach for most often. For example, forward all connections on port 8080 to port 80 on the same machine:

socat TCP-LISTEN:8080,fork TCP:localhost:80

The fork option is critical — without it, socat handles exactly one connection and exits. With fork, each new connection spawns its own child process.

A more realistic scenario: forward a port from your local machine to an internal server that isn’t exposed externally:

# On the jump server (with a public IP), forward port 5432 to an internal DB server
socat TCP-LISTEN:5432,fork,reuseaddr TCP:192.168.1.50:5432

The reuseaddr option lets socat rebind the port immediately after a restart, so you don’t have to wait out the TCP TIME_WAIT timeout (typically 60–120 seconds). For scenarios where you need more visibility into what’s traversing these connections, tcpdump and Wireshark are excellent companions.

2. Building a relay — forwarding across multiple hops

I have offices in Hanoi and Osaka. Some internal services in Hanoi need to be accessible from Osaka, but direct routing is blocked by the firewall. The solution is a VPS in the middle acting as a relay:

# On the intermediary VPS (e.g., Singapore)
# Listen on port 9000, forward to Hanoi server port 22
socat TCP-LISTEN:9000,fork,reuseaddr TCP:hanoi-server.internal:22

From Osaka, SSHing into the Singapore VPS on port 9000 is effectively SSHing into Hanoi:

ssh -p 9000 [email protected]

This type of relay adds no extra encryption — traffic between the VPS and the Hanoi server is still plain TCP. If the application layer is already encrypted (SSH, HTTPS), there’s nothing to worry about. For more structured tunneling over SSH, local, remote, and dynamic port forwarding via SSH is worth exploring.

3. UDP relay — something netcat can’t do easily

UDP relaying is more complex than TCP because there’s no connection state. socat handles it cleanly with its own syntax:

# Forward UDP port 514 (syslog) to a centralized log server
socat UDP-RECVFROM:514,fork UDP-SENDTO:logserver.internal:514

Use UDP-RECVFROM instead of UDP-LISTEN so multiple clients can send at the same time without contending with each other.

4. SSL tunnel — underrated and more useful than you’d think

Got a plain TCP service you want to expose over an encrypted connection, but don’t want to set up nginx or stunnel? socat can do it — you just need a certificate.

Generate a self-signed cert (or use an existing one):

openssl req -newkey rsa:2048 -nodes -keyout server.key -x509 -days 365 -out server.crt
cat server.crt server.key > server.pem

On the server — wrap the plain TCP service with SSL:

# Listen for SSL on port 8443, forward to plain TCP port 8080 on localhost
socat SSL-LISTEN:8443,cert=server.pem,verify=0,fork TCP:localhost:8080

On the client — connect to the SSL endpoint and expose it on a local port:

# Connect to the SSL server, expose locally on port 8080
socat TCP-LISTEN:8080,fork SSL:yourserver.com:8443,verify=0

The verify=0 option skips certificate verification — fine for internal environments. In production, use a valid certificate and drop this option.

5. Quick debugging — simulating a TCP server

When I need to check whether a client can connect, or want to see what it’s actually sending, I use:

# Listen on port 8080, print everything the client sends to the terminal
socat -v TCP-LISTEN:8080,fork -

The -v option prints both the data and connection info in each direction. Far more convenient than Wireshark when you just need to inspect raw requests from an application. To go deeper into diagnosing connectivity problems, the full guide to ip, route, dig, and netcat covers the broader toolkit.

Or simulate a server that returns a fixed response:

# Every connection to port 9999 gets the contents of response.txt
socat TCP-LISTEN:9999,fork OPEN:response.txt,rdonly

6. Running socat as a systemd service

In the office environments I manage, some relays need to run continuously. Rather than leaving a terminal open, set up a systemd service to keep things clean:

# /etc/systemd/system/socat-relay.service
[Unit]
Description=socat port relay 8080 -> internal:80
After=network.target

[Service]
ExecStart=/usr/bin/socat TCP-LISTEN:8080,fork,reuseaddr TCP:192.168.1.10:80
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now socat-relay

Practical notes to keep in mind

  • Security: socat has no built-in authentication. Binding to a public interface means anyone can connect. Pair it with ufw/iptables to whitelist source IPs.
  • The bind option: By default, socat listens on all interfaces. To restrict it to localhost, add bind=127.0.0.1: TCP-LISTEN:8080,bind=127.0.0.1,fork
  • IPv6: Use TCP6-LISTEN and TCP6 instead of TCP when IPv6 support is needed. If you’re working in a dual-stack environment, configuring IPv6 on Linux servers is a solid reference.
  • Connection limits: The max-children=10 option caps the number of concurrent child processes — a useful safeguard against floods when exposing a port publicly.

When to use socat — and when not to

I think of socat as a situational tool — it shows up when you need a quick fix without touching system configuration. It excels at:

  • Temporary port forwarding (testing, debugging, emergency fixes)
  • Simple relays that don’t need complex configuration
  • Quickly wrapping plain TCP in SSL
  • Low-level network communication debugging

But don’t reach for socat when:

  • You need load balancing or health checks → HAProxy/nginx are far better suited
  • You need a production reverse proxy with logging, retries, and timeouts — socat isn’t feature-rich enough
  • You need a VPN tunnel with strong encryption → WireGuard/OpenVPN are the right choices

Thirty seconds typing a socat command is sometimes more effective than two hours configuring something else. It can’t replace HAProxy or nginx — but it was never meant to. Every tool has its job.

Add socat to your toolbox if it isn’t there already. You won’t use it every day — but when you need it, nothing else quite fits.

Share: