Self-hosted HTTP tunnel tool — like ngrok, but yours. No quotas, no limits, on your own server.
7
stars
30
commits
Go
primary language
Sep 1, 2026
updated
smuf exposes a local port on a public URL — like ngrok, but yours: no quotas, no limits, on your own server.
localhost:3000 ◄─────► https://a3f1c9.yourdomain.com
One-click deploy smuf-server to Railway:
Once deployed, the server URL and auth token are set via Railway's environment variables (see Configuration below). Then connect with the client from any machine.
Go to Releases and download the binary for your OS.
| Binary | Where it goes |
|---|---|
smuf-server | Your VPS / server |
smuf | Your local machine |
docker compose up -d
You'll need Go 1.21+.
git clone https://github.com/cdrusu/smuf.git && cd smuf
go build -o smuf-server ./cmd/smuf-server
go build -o smuf ./cmd/smuf
First time: run without arguments and the wizard will set everything up:
./smuf-server # on the server
./smuf --setup # on your machine
After that:
./smuf 3000 # exposes localhost:3000
./smuf 3000 4000 5000 # multiple ports at once
./smuf --sub myapp 3000 # fixed URL: myapp.yourdomain.com
./smuf --tcp 22 # pure TCP tunnel (SSH, DB, etc.)
Output:
Tunnel ready!
Local → http://localhost:3000
Public → https://a3f1c9.yourdomain.com
Press Ctrl+C to stop
While the server is running, open in your browser:
http://yourdomain.com:8080/
It is designed in HashiCorp style: dark #0d0e12 background, system-ui font, cards with micro-shadows and blue accent (#1060ff).
Shows all active tunnels with:
Updates every 5 s. The JSON endpoint is at /_smuf/tunnels.
Everything goes through environment variables (or a .env file next to the binary).
smuf-server)| Variable | Default | Description |
|---|---|---|
SMUF_DOMAIN | localhost | Your base domain |
SMUF_AUTH_TOKEN | — | Secret token (recommended in production) |
SMUF_CONTROL_PORT | 7000 | Port for clients to connect |
SMUF_HTTP_PORT | 8080 | Public HTTP port |
SMUF_HTTPS | false | Automatic HTTPS with Let's Encrypt |
SMUF_HTTPS_PORT | 443 | HTTPS port |
SMUF_ACME_EMAIL | — | Email for certificate notices |
SMUF_MAX_CONNS_PER_IP | 5 | Max tunnels per IP |
SMUF_HANDSHAKE_TIMEOUT | 10s | Handshake timeout |
SMUF_TCP_PORT_RANGE | — | Public TCP port range (e.g. 20000-30000) |
smuf)| Variable | Default | Description |
|---|---|---|
SMUF_SERVER | localhost:7000 | Server address |
SMUF_AUTH_TOKEN | — | Token (must match the server) |
SMUF_SUBDOMAIN | — | Fixed subdomain (equivalent to --sub) |
Server .env example:
SMUF_DOMAIN=yourdomain.com
SMUF_AUTH_TOKEN=a-long-secret-token
# SMUF_HTTPS=true
# SMUF_ACME_EMAIL=you@email.com
Client .env example:
SMUF_SERVER=yourdomain.com:7000
SMUF_AUTH_TOKEN=a-long-secret-token
# SMUF_SUBDOMAIN=myapp
Generate a secure token with
openssl rand -hex 32
smuf 3000 ──TCP──► smuf-server :7000
│
"PORT 3000 SUB myapp" → "OK myapp https://myapp.yourdomain.com"
│
yamux (multiplexing)
│
request → myapp.yourdomain.com → yamux stream → localhost:3000
Uses hashicorp/yamux to multiplex multiple HTTP requests over a single TCP connection.
smuf/
├── cmd/
│ ├── smuf/ # Client (your machine)
│ └── smuf-server/ # Server (your VPS)
└── internal/
├── tunnel/ # Registry + BufConn
├── wizard/ # Interactive setup
└── logger/ # Timestamped logging
Found a bug or have an idea? Open an issue or submit a pull request.
30 commits
Go
66.1%
HTML
28.6%
Shell
1.8%
CSS
1.7%
Python
1.5%
Self-hosted HTTP tunnel tool — like ngrok, but yours. No quotas, no limits, on your own server.
7
stars
30
commits
Go
primary language
Sep 1, 2026
updated
smuf exposes a local port on a public URL — like ngrok, but yours: no quotas, no limits, on your own server.
localhost:3000 ◄─────► https://a3f1c9.yourdomain.com
One-click deploy smuf-server to Railway:
Once deployed, the server URL and auth token are set via Railway's environment variables (see Configuration below). Then connect with the client from any machine.
Go to Releases and download the binary for your OS.
| Binary | Where it goes |
|---|---|
smuf-server | Your VPS / server |
smuf | Your local machine |
docker compose up -d
You'll need Go 1.21+.
git clone https://github.com/cdrusu/smuf.git && cd smuf
go build -o smuf-server ./cmd/smuf-server
go build -o smuf ./cmd/smuf
First time: run without arguments and the wizard will set everything up:
./smuf-server # on the server
./smuf --setup # on your machine
After that:
./smuf 3000 # exposes localhost:3000
./smuf 3000 4000 5000 # multiple ports at once
./smuf --sub myapp 3000 # fixed URL: myapp.yourdomain.com
./smuf --tcp 22 # pure TCP tunnel (SSH, DB, etc.)
Output:
Tunnel ready!
Local → http://localhost:3000
Public → https://a3f1c9.yourdomain.com
Press Ctrl+C to stop
While the server is running, open in your browser:
http://yourdomain.com:8080/
It is designed in HashiCorp style: dark #0d0e12 background, system-ui font, cards with micro-shadows and blue accent (#1060ff).
Shows all active tunnels with:
Updates every 5 s. The JSON endpoint is at /_smuf/tunnels.
Everything goes through environment variables (or a .env file next to the binary).
smuf-server)| Variable | Default | Description |
|---|---|---|
SMUF_DOMAIN | localhost | Your base domain |
SMUF_AUTH_TOKEN | — | Secret token (recommended in production) |
SMUF_CONTROL_PORT | 7000 | Port for clients to connect |
SMUF_HTTP_PORT | 8080 | Public HTTP port |
SMUF_HTTPS | false | Automatic HTTPS with Let's Encrypt |
SMUF_HTTPS_PORT | 443 | HTTPS port |
SMUF_ACME_EMAIL | — | Email for certificate notices |
SMUF_MAX_CONNS_PER_IP | 5 | Max tunnels per IP |
SMUF_HANDSHAKE_TIMEOUT | 10s | Handshake timeout |
SMUF_TCP_PORT_RANGE | — | Public TCP port range (e.g. 20000-30000) |
smuf)| Variable | Default | Description |
|---|---|---|
SMUF_SERVER | localhost:7000 | Server address |
SMUF_AUTH_TOKEN | — | Token (must match the server) |
SMUF_SUBDOMAIN | — | Fixed subdomain (equivalent to --sub) |
Server .env example:
SMUF_DOMAIN=yourdomain.com
SMUF_AUTH_TOKEN=a-long-secret-token
# SMUF_HTTPS=true
# SMUF_ACME_EMAIL=you@email.com
Client .env example:
SMUF_SERVER=yourdomain.com:7000
SMUF_AUTH_TOKEN=a-long-secret-token
# SMUF_SUBDOMAIN=myapp
Generate a secure token with
openssl rand -hex 32
smuf 3000 ──TCP──► smuf-server :7000
│
"PORT 3000 SUB myapp" → "OK myapp https://myapp.yourdomain.com"
│
yamux (multiplexing)
│
request → myapp.yourdomain.com → yamux stream → localhost:3000
Uses hashicorp/yamux to multiplex multiple HTTP requests over a single TCP connection.
smuf/
├── cmd/
│ ├── smuf/ # Client (your machine)
│ └── smuf-server/ # Server (your VPS)
└── internal/
├── tunnel/ # Registry + BufConn
├── wizard/ # Interactive setup
└── logger/ # Timestamped logging
Found a bug or have an idea? Open an issue or submit a pull request.
30 commits
Go
66.1%
HTML
28.6%
Shell
1.8%
CSS
1.7%
Python
1.5%