README
ΒΆ
πΉ Gopher
Public router for private services. Self-hosted reverse tunnel system with automatic HTTPS and subdomain routing. Expose homelab services, research servers, or any private machine via clean URLs β no port forwarding needed.
Example:
photos.yourdomain.com β Immich on home NAS (192.168.1.50:2283)
lab.yourdomain.com β Jupyter on university server (no public IP)
vault.yourdomain.com β Bitwarden on Raspberry Pi (behind NAT)
Built on Caddy and rathole
Gopher would not exist without two excellent open-source projects:
Caddy handles all HTTPS termination and subdomain routing on the VPS. It automatically provisions and renews TLS certificates via Let's Encrypt, with zero configuration required from the user. Gopher generates and manages the Caddyfile for you, but Caddy is doing the actual reverse proxying.
rathole is the tunnel engine. It's a Rust-based, extremely lightweight TCP/UDP tunnel that machines use to punch through NAT and firewalls by maintaining a persistent outbound connection to the VPS. Gopher manages the rathole server config and deploys rathole client configs to each machine β but rathole is what makes the tunnels actually work.
If you find Gopher useful, please consider starring those projects too.
How It Works
Run Gopher on a VPS. It manages Caddy (reverse proxy) and rathole (tunnel server). Private machines establish outbound tunnels. Gopher routes incoming traffic by subdomain.
Internet
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββ
β Public VPS (Gopher runs here) β
β β
β βββββββββββββββββββ βββββββββββββββββββββββ β
β β Caddy (80/443) β β rathole server β β
β β auto-HTTPS ββββΆβ (port 2333) β β
β β subdomainβport β β tunnels listening β β
β βββββββββββββββββββ ββββββββββ¬βββββββββββββ β
βββββββββββββββββββββββββββββββββ-βββββββββββββββββ
β outbound TCP tunnel
ββββββββββββββββ
β
ββββββββββ΄βββββββββββββββββββββββββββββββββββ
β Private network (home/lab/office) β
β β
β ββββββββββββββββ ββββββββββββββββββββ β
β β Immich VM β β Bitwarden VM β β
β β rathole cli β β rathole cli β β
β β :2283 β β :80 β β
β ββββββββββββββββ ββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββ
Traffic flow: photos.yourdomain.com β Caddy (VPS) β rathole tunnel β rathole client β localhost:2283 (Immich)
Key insight: Machines connect outbound to the VPS, bypassing NAT and firewalls. The VPS routes incoming traffic back through those established tunnels.
Installation
Gopher ships as a single self-contained binary for Linux. No runtime dependencies β Caddy and rathole are downloaded automatically during setup.
Requirements
- Linux VPS (Ubuntu 22.04+ or RHEL 8+ recommended), x86_64 or arm64
- A domain with a wildcard DNS A record pointing at your VPS:
*.yourdomain.com β <VPS IP> - Ports 22, 80, 443, and 2333 reachable on the VPS
Download
Latest stable release:
ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')
TAG=$(curl -fsSL https://api.github.com/repos/smalex-z/gopher/releases/latest | grep '"tag_name"' | cut -d'"' -f4)
curl -fsSL "https://github.com/smalex-z/gopher/releases/download/${TAG}/gopher-linux-${ARCH}" -o gopher && chmod +x gopher
Latest pre-release (recommended for now β no stable release yet):
ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')
TAG=$(curl -fsSL https://api.github.com/repos/smalex-z/gopher/releases | grep -m1 '"tag_name"' | cut -d'"' -f4)
curl -fsSL "https://github.com/smalex-z/gopher/releases/download/${TAG}/gopher-linux-${ARCH}" -o gopher && chmod +x gopher
You can verify your download against the checksums file on the releases page.
Run
# Start the web UI (listens on :4321)
./gopher
# Install as a systemd service (runs on boot, survives reboots)
sudo ./gopher install
# Service management
sudo systemctl status gopher
sudo systemctl restart gopher
# Uninstall
sudo ./gopher uninstall
On first start, visit http://<your-vps-ip>:4321 and complete the setup wizard:
- Password β set your admin password
- Local services β install Caddy and rathole on this machine (or skip for rathole-only)
- Firewall β choose how network rules are managed (Gopher-managed is recommended)
- SSH key β generate or upload an SSH key pair used to access bootstrapped machines
Setup Workflow
After the wizard, you're at the main dashboard:
1. Add Machines (Machines tab)
- Click Bootstrap New Machine
- Copy the one-liner and run it on any machine you want to expose
- It installs rathole client and establishes a reverse tunnel back to your VPS
2. Create Tunnels (Tunnels tab)
- Select a machine, enter a subdomain (e.g.
photos) and the local port (e.g.2283) - Click Create Tunnel
https://photos.yourdomain.comis live with automatic TLS in seconds
Gopher supports HTTP, raw TCP, and UDP tunnels.
Use Cases
Homelab:
photos.home.com β Jellyfin media server
vault.home.com β Bitwarden password manager
files.home.com β Nextcloud file sync
monitor.home.com β Grafana dashboards
Research Lab:
jupyter.lab.edu β Jupyter notebook server
vnc1.lab.edu β VNC to lab workstation
data.lab.edu β Dataset browser
Multi-site:
nas.example.com β Home NAS (Maryland)
jupyter.example.com β Lab server (UCLA)
media.example.com β Friend's shared Plex
Comparison
| Gopher | ngrok | Cloudflare Tunnel | Port Forwarding | |
|---|---|---|---|---|
| Cost | VPS (~$3β5/mo) | $8β20/mo | Free* | None |
| Custom domain | β | π° Paid tier | β οΈ CF DNS required | β |
| Permanent URLs | β | β Ephemeral | β | β |
| Self-hosted | β | β | β | N/A |
| Vendor lock-in | β | β | β | β |
| Protocol support | HTTP / TCP / UDP | HTTP / TCP | HTTP only** | All |
| Works behind NAT | β | β | β | β |
| Automatic HTTPS | β | β | β | β |
| Traffic privacy | β You control | β ngrok sees all | β CF sees all | β |
* Free with limitations
** Non-HTTP protocols require Cloudflare Access (paid)
Firewall Setup
OS-level firewall
Gopher can manage iptables rules automatically. During the setup wizard, choose Gopher-managed firewall mode and Gopher will:
- Create a dedicated
GOPHER_TUNNELSiptables chain - Keep ports 22, 80, 443, and 2333 permanently open
- Open the dashboard port (default 4321) β or restrict it to localhost if Caddy is configured
- Automatically open/close tunnel ports as you add or remove tunnels
If you prefer to manage firewall rules yourself, choose Manual mode and open the required ports yourself. Each tunnel you create gets its own port (starting around 20000) β Gopher shows the assigned port when you create one.
Cloud-level firewall / security groups
Most cloud providers have a firewall that sits in front of the VM and blocks traffic before it ever reaches the OS. You need to open ports there as well β Gopher cannot manage these.
Oracle Cloud (OCI) β the most common gotcha, since the default security list blocks almost everything:
- Go to Networking β Virtual Cloud Networks β your VCN β Security Lists
- Edit the Default Security List (or whichever is attached to your subnet)
- Add ingress rules for TCP ports 80, 443, and 2333 from source
0.0.0.0/0 - Port 22 is usually already open
Alternatively, use a Network Security Group attached to your instance instead of the security list.
AWS EC2:
- Go to EC2 β Instances β your instance β Security
- Click the security group link
- Edit Inbound rules β add TCP 80, 443, 2333 from
0.0.0.0/0
Hetzner:
- Go to Firewall in the Cloud Console
- Add inbound rules for TCP 80, 443, 2333
- Apply the firewall to your server
DigitalOcean:
- Go to Networking β Firewalls
- Create or edit a firewall, add TCP 80, 443, 2333 inbound
- Apply it to your droplet
VPS Recommendations
Tested providers:
- Oracle Cloud Free Tier β 4 vCPU ARM, 24 GB RAM, 4 Gbps β best free option for Gopher
- Hetzner β ~β¬4/month β reliable and cheap
- DigitalOcean / Vultr β $6/month droplet works well
- AWS EC2 β t2.micro is free tier eligible but networking is slow (~0.05 Gbps)
Minimum: 1 vCPU, 512 MB RAM handles 10+ tunnels comfortably.
Project Structure
gopher/
βββ cmd/server/
β βββ main.go # Entry point; embeds frontend
β βββ frontend/dist/ # Compiled React app (embedded at build time)
βββ frontend/ # React + TypeScript + Tailwind
β βββ src/
β βββ pages/ # Dashboard, Machines, Tunnels, Server, Setup
β βββ components/ # UI components
βββ internal/
β βββ api/ # Chi router + HTTP handlers
β βββ config/ # Caddyfile + rathole TOML generation
β βββ db/ # SQLite (GORM) models + migrations
β βββ service/ # Business logic (install, tunnels, firewall, β¦)
β βββ ssh/ # SSH client + VPS/machine deploy scripts
βββ scripts/
βββ build.sh # Build frontend then Go binary
βββ dev.sh # Dev mode with hot reload
Tech Stack
Backend: Go 1.21+, Chi router, GORM + glebarez/sqlite (pure-Go, no CGO), golang.org/x/crypto/ssh
Frontend: React 18 + TypeScript, Vite, Tailwind CSS β embedded in the binary via //go:embed
Infrastructure (managed by Gopher): Caddy 2 for HTTPS + routing, rathole for tunneling
Development
# Dev mode (hot reload on both frontend and backend)
./scripts/dev.sh
# Frontend: http://localhost:5173
# Backend: http://localhost:4321
# Production build
./scripts/build.sh
./gopher
Contributing
Issues and PRs welcome. See open issues.
Areas that would help most:
- Testing on different distros and VPS providers
- Bug reports with reproduction steps
License
MIT β see LICENSE
Acknowledgments
Gopher is a thin management layer on top of two great projects: Caddy by Matt Holt and the team, and rathole (originally by rapiz1). Without them there is no Gopher.