Skip to content

Dev Tunnels (Self-hosted ngrok) ​

We run a self-hosted tunneling service that exposes your local dev servers to the internet via HTTPS. This replaces ngrok.

How it works ​

localhost:5173 β†’ frpc (your machine) β†’ frps (GCP VM) β†’ https://yourname.dev.review-time.info
  • frps (server) runs on a GCP VM with Caddy for automatic HTTPS (Let's Encrypt wildcard certificate)
  • frpc (client) runs on your machine and creates a tunnel to the server
  • Each dev gets unique subdomains under *.dev.review-time.info

Setup ​

1. Install frpc ​

bash
brew install frpc

2. Create your config ​

Copy the example config from the repo root:

bash
cp frpc.example.toml frpc.toml

Edit frpc.toml:

  • Replace yourname with your first name (e.g. vincent, alex)
  • Adjust localPort to match your dev server

3. Set your auth token ​

Ask a team member for the FRP token, then export it:

bash
export FRP_TOKEN="the-shared-token"

TIP

Add this to your ~/.zshrc so you don't have to export it every time:

bash
echo 'export FRP_TOKEN="the-shared-token"' >> ~/.zshrc

4. Start the tunnel ​

bash
frpc -c frpc.toml

Your local server is now available at your personal tunnel URL.

How to construct your URL ​

Your tunnel URL follows this pattern:

https://{yourname}.dev.review-time.info/

For example, if your name is Vincent, your URL is:

https://vincent.dev.review-time.info/

Multiple proxies ​

You can expose multiple local services at once. Add more [[proxies]] blocks to your frpc.toml:

toml
[[proxies]]
name = "vincent-front"
type = "http"
localPort = 5173
subdomain = "vincent"

[[proxies]]
name = "vincent-api"
type = "http"
localPort = 3000
subdomain = "vincent-api"

[[proxies]]
name = "vincent-mobile"
type = "http"
localPort = 8081
subdomain = "vincent-mobile"

Each subdomain must be unique across the team. Convention: {yourname}-{service}.

Infrastructure details ​

For maintainers who need to manage the server.

Server location ​

  • GCP project: tell-ia-dev
  • VM: frp-tunnel in europe-west1-b
  • IP: 34.62.32.239 (remember to reserve it so it doesn't change)

SSH into the server ​

bash
gcloud compute ssh frp-tunnel --zone=europe-west1-b

Services ​

All services run in /opt/frp/ via Docker Compose:

ServiceRole
frpsFRP server β€” accepts client connections on port 7000, serves HTTP on port 8080
caddyReverse proxy β€” handles HTTPS (port 443) with auto Let's Encrypt wildcard cert, forwards to frps

Common operations ​

bash
cd /opt/frp

# Check status
docker compose ps

# View logs
docker compose logs -f caddy
docker compose logs -f frps

# Restart after config change
docker compose restart frps    # if frps.toml changed
docker compose restart caddy   # if Caddyfile changed

# Full restart
docker compose down && docker compose up -d

Config files on the server ​

FilePurpose
frps.tomlFRP server config (port, auth token, subdomain host)
CaddyfileCaddy reverse proxy + TLS config
.envAWS credentials for Route 53 DNS challenge
Dockerfile.caddyCustom Caddy build with Route 53 DNS plugin
docker-compose.ymlOrchestrates frps + caddy

DNS ​

Two A records in AWS Route 53 (hosted zone review-time.info):

RecordValue
dev.review-time.info34.62.32.239
*.dev.review-time.info34.62.32.239

Changing the auth token ​

  1. Generate a new token: openssl rand -hex 32
  2. Update frps.toml on the server
  3. docker compose restart frps
  4. Share the new token with the team