In modern Linux infrastructure transitions, systems engineers frequently encounter legacy applications (such as aging Java application servers, bespoke C++ daemons, or older database engines) that cannot be easily updated. A common architectural challenge arises when you must migrate these legacy services to a new physical server or a segregated container network, but hundreds of hardcoded clients are still attempting to connect to the original IP address and TCP port. While you could configure complex iptables NAT rules or deploy a heavyweight reverse proxy like Nginx or HAProxy, these solutions introduce significant configuration overhead. For a lightweight, native, and highly performant solution built directly into the init system, administrators should deploy systemd-socket-proxyd.
The Architecture of systemd Socket Proxying
systemd-socket-proxyd is a specialized, micro-binary included by default in all modern systemd installations. It leverages systemd’s powerful socket activation capability.
In a standard architecture, an application daemon starts up and binds directly to a network port (e.g., TCP 8080). With socket activation, systemd itself binds to TCP 8080. It listens for incoming packets. When a packet arrives, systemd dynamically spawns the associated daemon and passes the active network socket to it.
systemd-socket-proxyd takes this a step further. Instead of spawning a local daemon, systemd accepts the incoming connection on the legacy port, immediately spawns the systemd-socket-proxyd micro-binary, and instructs it to seamlessly bridge the byte stream to a completely different IP address and port (the new, migrated backend server). Because it operates entirely at Layer 4 (TCP), it is protocol-agnostic; it can proxy HTTP, SSH, MySQL, or proprietary binary protocols with equal efficiency.
Configuring the Socket Unit
To deploy the proxy, you must create two distinct systemd unit files: a .socket file to define the listening port, and a .service file to define the proxy destination.
First, create the socket unit file in /etc/systemd/system/legacy-app.socket. This dictates where systemd will listen for incoming legacy traffic.
[Unit]
Description=Socket for Legacy Application Proxy
[Socket]
# Listen on TCP port 8080 on all IPv4 and IPv6 interfaces
ListenStream=8080
# Optional: Bind to a specific IP address
# ListenStream=10.0.0.5:8080
[Install]
WantedBy=sockets.target
Configuring the Service Unit
Next, create the corresponding service unit file in /etc/systemd/system/legacy-app.service. This file explicitly calls the systemd-socket-proxyd binary and defines the destination of the migrated server.
[Unit]
Description=Transparent Proxy for Legacy Application
Requires=legacy-app.socket
After=legacy-app.socket
[Service]
# The proxy binary accepts the destination IP and Port as an argument
ExecStart=/lib/systemd/systemd-socket-proxyd 192.168.50.100:9090
PrivateTmp=yes
# Enhance security by dropping privileges
User=nobody
Group=nogroup
In this configuration, any traffic hitting the original server on TCP 8080 will be instantly and transparently proxied to the new server residing at 192.168.50.100 on port 9090.
Activation and Verification
After creating the two unit files, reload the systemd daemon to parse the new configurations:
sudo systemctl daemon-reload
Crucially, you do not start the .service file directly. You must enable and start the .socket file. Systemd will handle the dynamic spawning of the proxy service.
sudo systemctl enable --now legacy-app.socket
To verify that systemd is actively listening on the legacy port, utilize the ss (socket statistics) command:
ss -tlnp | grep 8080
The output will definitively show that systemd (PID 1) is holding the port open, ready to intercept traffic. If you initiate a connection to port 8080, systemd will spawn the proxy binary, and a subsequent systemctl status legacy-app.service will reveal the active proxy bridging the connection, completely abstracting the infrastructure migration from the legacy clients.