Getting started

From nothing to your first service reachable at your own domain. About 20 minutes, most of it DNS propagation.

The big picture: you run a small server on a machine with a public address (a $5 VPS is plenty), and a tiny agent on the machine that has the service — your home server, a NAS, a Raspberry Pi. The agent dials out to the server; visitors come in through your domain.

1. What you need

You do not need: port forwarding on your home router, a static IP at home, or any programming. Why? Connections are always made from home outward, and routers happily allow that direction — even carrier-grade NAT, which rejects every incoming connection by design, cannot get in the way.

2. Point the domain at your server

In your DNS provider's panel create two records with the server's IP address (replace 203.0.113.10):

Type Name Value
A tunnels.example.com 203.0.113.10
A *.tunnels.example.com 203.0.113.10

The first makes the admin panel reachable; the second — the wildcard record — is what makes every tunnel work. Each tunnel is a subdomain like myapp.tunnels.example.com, and the wildcard points them all at your server in one go. Without it, new tunnels would need a new DNS record every time.

If your provider needs a CNAME instead, point both at the server's hostname.

3. Install the server

With Docker (recommended — the image includes everything):

# docker-compose.yml on the public host
services:
  tunnel-me:
    image: ghcr.io/reddec/tunnel-me
    command: >-
      serve
      --domain tunnels.example.com
      --public-url http://tunnels.example.com:8080
      --db /data/tunnel.db
      --auth basic
      --auth.basic.username me
      --auth.basic.password pick-a-long-random-one
    ports:
      - "8080:8080"
    volumes:
      - ./data:/data
    restart: unless-stopped
docker compose up -d

Full flag reference: Server reference.

Protect the panel before going public. The server's default (--auth static) performs no credential check at all. The compose file above already sets --auth basic with a username and password — keep that in. All sign-in options live in Authentication.

The --public-url above is the address that works now. When you add HTTPS in the last step, change it to https://tunnels.example.com — it is baked into the connect commands the panel shows.

HTTPS comes later (recommended: a reverse proxy in front — see Reverse proxy). For now the panel works on http://tunnels.example.com:8080.

4. Create your first tunnel

Open http://tunnels.example.com:8080 and sign in with the credentials you set.

  1. Click New agent, enter a sub-domain — say, myapp. The dialog shows the future address: myapp.tunnels.example.com.
  2. Open the new tunnel and go to TokensNew token. Give it a label like "home server".
  3. Copy the token now — it is displayed exactly once.

5. Connect from home

On the machine with your service (the one where port 3000 answers), paste the one-line command from the tunnel's Overview tab. It looks like this:

curl -fsSL http://tunnels.example.com:8080/boot/connect/<token> | sh -s -- 3000

It downloads the agent binary, installs it to ~/.local/bin, and starts sharing your local port 3000. The 3000 at the end is the upstream — the local service to expose. Share a different machine on your network with 192.168.1.50:8123, or a folder of files with ./my-site (all forms in the agent guide).

The terminal now shows the agent running and the address it is reachable at. Keep it running — for an always-on setup, run it under systemd (agent guide).

6. Verify

Open http://myapp.tunnels.example.com:8080 — you should see your service. Traffic now flows: browser → your server → through the tunnel → your home machine. Nothing about your home network is exposed except this one service.

7. Add HTTPS

Right now everything is plain HTTP on port 8080. The recommended finishing touch: put Caddy/nginx/traefik in front with a wildcard certificate, port 80/443, and done — https://myapp.tunnels.example.com. Walkthrough: Reverse proxy.

Where to next