docs/p2p-tunnels.md
Kubo supports tunneling TCP connections through libp2p streams, similar to SSH
port forwarding (ssh -L). This allows exposing local services to remote peers
and forwarding remote services to local ports.
Unlike traditional SSH tunnels, libp2p-based tunnels do not require:
No public IP or open ports: The server does not need a static IP address or port forwarding configured on the router. Connectivity to peers behind NAT is facilitated by Direct Connection Upgrade through Relay (DCUtR), which enables NAT hole-punching.
No DNS or IP address management: All you need is the server's PeerID and
an agreed-upon protocol name (e.g., /x/ssh). Kubo handles peer discovery
and routing via the Amino DHT.
Simplified firewall rules: Since connections are established through libp2p's existing swarm connections, no additional firewall configuration is needed beyond what Kubo already requires.
This makes p2p tunnels useful for connecting to machines on home networks, behind corporate firewalls, or in environments where traditional port forwarding is not available.
Enable the experimental feature:
$ ipfs config --json Experimental.Libp2pStreamMounting true
Test with netcat (nc) - no services required:
On the server:
$ ipfs p2p listen /x/test /ip4/127.0.0.1/tcp/9999
$ nc -l -p 9999
On the client:
Replace $SERVER_ID with the server's peer ID (get it with ipfs id -f "<id>\n"
on the server).
$ ipfs p2p forward /x/test /ip4/127.0.0.1/tcp/9998 /p2p/$SERVER_ID
$ nc 127.0.0.1 9998
Type in either terminal and the text appears in the other. Use Ctrl+C to exit.
By default, ipfs p2p listen and ipfs p2p forward register the tunnel with
the daemon and return immediately. The tunnel persists until explicitly closed
with ipfs p2p close or the daemon shuts down.
This example exposes a local SSH server (listening on localhost:22) to a
remote peer. The same pattern works for any TCP service.
On the server (the machine running SSH):
Register a p2p listener that forwards incoming connections to the local SSH
server. The protocol name /x/ssh is an arbitrary identifier that both peers
must agree on (the /x/ prefix is required for custom protocols).
$ ipfs p2p listen /x/ssh /ip4/127.0.0.1/tcp/22
On the client:
Create a local port (2222) that tunnels through libp2p to the server's SSH
service.
$ ipfs p2p forward /x/ssh /ip4/127.0.0.1/tcp/2222 /p2p/$SERVER_ID
Now connect to SSH through the tunnel:
$ ssh [email protected] -p 2222
Other services: To tunnel a different service, change the port and protocol
name. For example, to expose a web server on port 8080, use /x/mywebapp and
/ip4/127.0.0.1/tcp/8080.
Use --foreground (-f) to block until interrupted. The tunnel is
automatically removed when the command exits:
$ ipfs p2p listen /x/ssh /ip4/127.0.0.1/tcp/22 --foreground
Listening on /x/ssh, forwarding to /ip4/127.0.0.1/tcp/22, waiting for interrupt...
^C
Received interrupt, removing listener for /x/ssh
The listener/forwarder is automatically removed when:
ipfs p2p close is calledThis mode is useful for systemd services and scripts that need cleanup on exit.
The --foreground flag enables clean integration with systemd. The examples
below show how to run ipfs p2p listen as a user service that starts
automatically when the IPFS daemon is ready.
Ensure IPFS daemon runs as a systemd user service. See misc/README.md for setup instructions and where to place unit files.
Use a .path unit to wait for the daemon's RPC API to be ready before starting
the p2p listener.
ipfs-p2p-tunnel.path:
[Unit]
Description=Monitor for IPFS daemon startup
After=ipfs.service
Requires=ipfs.service
[Path]
PathExists=%h/.ipfs/api
Unit=ipfs-p2p-tunnel.service
[Install]
WantedBy=default.target
The %h specifier expands to the user's home directory. If you use a custom
IPFS_PATH, adjust accordingly.
ipfs-p2p-tunnel.service:
[Unit]
Description=IPFS p2p tunnel
Requires=ipfs.service
[Service]
ExecStart=ipfs p2p listen /x/ssh /ip4/127.0.0.1/tcp/22 -f
Restart=on-failure
RestartSec=10
[Install]
WantedBy=default.target
$ systemctl --user enable ipfs.service
$ systemctl --user enable ipfs-p2p-tunnel.path
$ systemctl --user start ipfs.service
The path unit monitors ~/.ipfs/api and starts ipfs-p2p-tunnel.service
once the file exists.
[!WARNING] This feature provides CLI and HTTP RPC users with the ability to set up port forwarding for localhost and LAN ports. If you enable this and plan to expose CLI or HTTP RPC to other users or machines, secure the RPC API using
API.Authorizationsor custom auth middleware.
When using --foreground, the listener stops if the terminal closes. For
persistent foreground listeners, use a systemd service, nohup, tmux, or
screen. Without --foreground, the listener persists in the daemon regardless
of terminal state.
Verify:
ipfs config Experimental.Libp2pStreamMountingipfs p2p lsipfs swarm connect /p2p/$PEER_IDThere is currently no way to define tunnels in the Kubo JSON config file. Use
--foreground mode with a systemd service for persistent tunnels. Support for
configuring tunnels via JSON config may be added in the future (see kubo#5460 - PRs welcome!).