Setting Up tunnel.dsouza.io

Self-hosted ngrok-style tunneling with frp, Caddy, and Cloudflare

Architecture

The request flow from browser to your laptop:

Browser Caddy (TLS termination, *.tunnel.dsouza.io) frps (matches Host header to registered tunnel) frpc (persistent outbound connection from laptop) localhost:PORT
Step 1

Cloudflare — Wildcard DNS Record

In the Cloudflare dashboard, go to your dsouza.io zone, then DNS → Records → Add Record.

Type:  A
Name:  *.tunnel
Value: <your-server-ip>
Proxy: DNS only (gray cloud)
TTL:   Auto
Important: Make sure the proxy toggle is set to "DNS only" (gray cloud), not "Proxied" (orange cloud). Caddy needs to handle TLS directly.
Step 2

Cloudflare — API Token for Caddy

Caddy needs a Cloudflare API token to perform DNS-01 challenges for wildcard certificate issuance.

Go to My Profile → API Tokens → Create Token, then use the "Custom token" template:

Permissions:  Zone → DNS → Edit
Zone scope:  Specific zone → dsouza.io

Copy the token and save it somewhere secure — you'll need it for the Caddy config.

Step 3

Rebuild Caddy with the Cloudflare DNS Plugin

Your stock Caddy doesn't include the Cloudflare DNS module. You'll use xcaddy to build a custom binary.

First, install xcaddy (requires Go):

# Install Go if you don't have it
sudo apt install -y golang

# Install xcaddy
go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest

Build Caddy with the Cloudflare plugin:

xcaddy build --with github.com/caddy-dns/cloudflare

This produces a caddy binary in the current directory. Replace your existing install:

# Stop caddy
sudo systemctl stop caddy

# Back up the old binary
sudo cp $(which caddy) /usr/bin/caddy.bak

# Replace with the new one
sudo cp ./caddy /usr/bin/caddy
sudo chmod +x /usr/bin/caddy

# Verify the module is present
caddy list-modules | grep cloudflare
# Should output: dns.providers.cloudflare
Note: If Caddy was installed via apt, future apt upgrade may overwrite your custom binary. Consider pinning the package or using a systemd override to point to a custom path.
Step 4

Configure Caddy for Wildcard TLS + Reverse Proxy

Add the following to your Caddyfile. This handles TLS for *.tunnel.dsouza.io and proxies traffic to frps.

/etc/caddy/Caddyfile (add to your existing config)
*.tunnel.dsouza.io {
    tls {
        dns cloudflare {env.CLOUDFLARE_API_TOKEN}
    }

    reverse_proxy 127.0.0.1:8080
}

Set the API token as an environment variable for the Caddy service:

Create a systemd override
sudo systemctl edit caddy

Add the following:

[Service]
Environment=CLOUDFLARE_API_TOKEN=your-token-here

Then restart Caddy:

sudo systemctl daemon-reload
sudo systemctl start caddy

Caddy will automatically request a wildcard certificate via the DNS-01 challenge. Check the logs to confirm:

journalctl -u caddy --no-pager -f
Step 5

Install and Configure frps on the Server

Download the latest frp release:

# Check https://github.com/fatedier/frp/releases for latest version
FRP_VERSION="0.61.1"
wget https://github.com/fatedier/frp/releases/download/v${FRP_VERSION}/frp_${FRP_VERSION}_linux_amd64.tar.gz
tar xzf frp_${FRP_VERSION}_linux_amd64.tar.gz
cd frp_${FRP_VERSION}_linux_amd64

# Copy server binary
sudo cp frps /usr/local/bin/
sudo chmod +x /usr/local/bin/frps

Create the server config:

/etc/frp/frps.toml
sudo mkdir -p /etc/frp
# Control port — where frpc connects
bindPort = 7000

# HTTP vhost port — where Caddy sends traffic
vhostHTTPPort = 8080

# Subdomain configuration
subDomainHost = "tunnel.dsouza.io"

# Authentication — generate a strong random token
auth.method = "token"
auth.token = "your-strong-random-token-here"
Generate a strong token: openssl rand -hex 32

Create a systemd service:

/etc/systemd/system/frps.service
[Unit]
Description=frp server
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/frps -c /etc/frp/frps.toml
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable frps
sudo systemctl start frps
Firewall: Make sure port 7000 is open for inbound TCP (the frpc control connection). Port 8080 does not need to be open externally — only Caddy talks to it on localhost.
Step 6

Install frpc on Your Laptop

On macOS:

brew install frpc

Or download the binary manually from the frp releases page (grab the darwin_arm64 archive for Apple Silicon, or darwin_amd64 for Intel).

Create a base config file for convenience:

~/.config/frp/frpc.toml
serverAddr = "dsouza.io"
serverPort = 7000

auth.method = "token"
auth.token = "your-strong-random-token-here"
Step 7

Usage — Exposing a Local Service

Say you have a local dev server on port 3000 and want it reachable at myapp.tunnel.dsouza.io.

Create a tunnel config (or add to your base config):

~/.config/frp/myapp.toml
serverAddr = "dsouza.io"
serverPort = 7000

auth.method = "token"
auth.token = "your-strong-random-token-here"

[[proxies]]
name = "myapp"
type = "http"
localPort = 3000
subdomain = "myapp"

Start the tunnel:

frpc -c ~/.config/frp/myapp.toml

That's it. Open https://myapp.tunnel.dsouza.io in a browser and you'll hit your local port 3000, with TLS handled by Caddy.

To expose multiple services, add more [[proxies]] blocks in the same config:

[[proxies]]
name = "myapp"
type = "http"
localPort = 3000
subdomain = "myapp"

[[proxies]]
name = "api"
type = "http"
localPort = 8000
subdomain = "api"

Quick Reference

Once everything is set up, exposing a new service is just:

# 1. Create a config (or add a [[proxies]] block)
# 2. Run frpc
frpc -c ~/.config/frp/myapp.toml

# 3. Visit https://myapp.tunnel.dsouza.io

Useful commands on the server:

# Check frps status
sudo systemctl status frps

# View frps logs
journalctl -u frps --no-pager -f

# Check Caddy logs
journalctl -u caddy --no-pager -f

# Verify wildcard cert
curl -v https://test.tunnel.dsouza.io 2>&1 | grep subject