Keeping Docker, Git, and CI/CD Alive on Iranian Servers During Internet Disruptions with Reverse SSH Tunneling
(Using SSH Reverse Tunneling + Local Proxy)
When the internet becomes unreliable, the first things that break are usually the “invisible” parts of engineering work: package downloads, Docker pulls, CI/CD pipelines, and even basic access to internal servers.
Recently, I faced a situation where connectivity was so unstable that even reaching domestic servers was unreliable. I could sometimes SSH into the server, but the server itself couldn’t reliably access the public internet. That meant no Docker installation, no dependency downloads, and no normal deployment workflow.
This post documents a workaround that worked for me: tunneling internet access from my own machine to a server via SSH reverse port forwarding, by exposing a local HTTP/SOCKS proxy on the server’s localhost interface.
It’s a pragmatic solution for emergencies — not a perfect long-term network design.
The idea (high level)
Assumptions:
- You have a machine (laptop/desktop) with some working internet access.
- You can at least occasionally connect to your server via SSH (even if only through mobile internet).
- On your local machine, you can run a proxy (HTTP and/or SOCKS5). This could be Clash, V2Ray, Squid, etc.
Goal:
- Make the server able to send outbound traffic “through” your local proxy — so it can:
- install packages (
dnf/yum/apt) - pull images (
docker pull) - access Git repos / registries (when needed)
- install packages (
Step 1: Run a local proxy (HTTP and/or SOCKS5)
On your local machine, make sure you have a proxy listening on your LAN (or localhost). In my case, the proxy was reachable at:
192.168.1.102:2080
Your values may differ depending on your setup.
Step 2: Expose the local proxy on the server using SSH reverse port forwarding
From your local machine, run:
ssh -o ServerAliveInterval=20 -o ServerAliveCountMax=3 \
-R 127.0.0.1:10809:192.168.1.102:2080 \
-R 127.0.0.1:10808:192.168.1.102:2080 \
root@X.X.X.XWhat this does:
- It binds two ports on the server (only on
127.0.0.1):127.0.0.1:10808127.0.0.1:10809
- Any connection to those ports on the server is forwarded back to your local machine’s proxy endpoint (
192.168.1.102:2080).
I used:
10808as an HTTP proxy endpoint10809as a SOCKS5 proxy endpoint
(If your proxy software only supports one protocol, you only need one mapping.)
Security note: binding to 127.0.0.1 is important. It prevents exposing your proxy to the public internet.
Step 3: Test outbound connectivity from the server
For SOCKS5 via curl:
curl --socks5 127.0.0.1:10809 https://cloudflare.comIf you want to run a script through the proxy:
ALL_PROXY=socks5://127.0.0.1:10809 \
curl -fsSL --socks5 127.0.0.1:10809 \
https://example.com/script.sh | shStep 4: Configure Docker to use the proxy (systemd)
On RHEL-based systems (CentOS / AlmaLinux / Rocky), create:
sudo mkdir -p /etc/systemd/system/docker.service.d/
sudo nano /etc/systemd/system/docker.service.d/proxy.confAdd:
[Service]
Environment="ALL_PROXY=socks5://127.0.0.1:10809"
Environment="HTTP_PROXY=http://127.0.0.1:10808"
Environment="HTTPS_PROXY=http://127.0.0.1:10808"Then reload and restart Docker:
sudo systemctl daemon-reload
sudo systemctl restart dockerAt this point, Docker pulls should start working (assuming the tunnel is up).
Step 5: Use the proxy with dnf / yum
Example with dnf:
sudo dnf --setopt=proxy=http://127.0.0.1:10808 install unzipExample with yum:
sudo yum --setopt=proxy=http://127.0.0.1:10808 install unzipReducing dependency on the public internet (recommended)
Once you can stabilize basic server operations, it helps to reduce how often you need external services.
A few useful self-hosted tools:
- Gitea — lightweight Git hosting (and a base for a more resilient workflow)
- Verdaccio — private npm registry cache
- Kellnr — Rust crates registry / proxy
Even partial self-hosting can dramatically improve survival during outages.
Final notes / caveats
- This is a workaround, not a substitute for proper infrastructure.
- Treat it as an emergency bridge for package installs, image pulls, and short-term recovery.
- Keep the SSH tunnel session stable (
ServerAliveIntervalhelps). - Avoid binding remote forwarded ports to
0.0.0.0unless you fully understand the security implications.
Canonical version. Also on Medium.