
Best Web Development Company in Jamshedpur
Discover why Sharp Digital is the best web development company in Jamshedpur. Expert services with local understanding and global standards. 5+ years experience.
Read MoreLearn to expose home services using FRP, Docker, and Caddy. No port forwarding needed, perfect for CGNAT environments with multiple services.

You've got a home server running Immich, n8n, or some other self-hosted app, and you want clean domains like `images.yourdomain.com` or `automation.yourdomain.com` instead of ugly ports and IPs. Your ISP is behind CGNAT, port forwarding is unreliable or impossible, and paying for a static IP feels like a tax on self-hosting. The better option is to tunnel everything through a VPS using FRP (Fast Reverse Proxy) and Docker.
You need a VPS with:
150.265.32.125)SSH into the VPS and create a directory for the FRP server and Caddy:
mkdir -p ~/frp-server
cd ~/frp-server
Create the FRP server config frps.toml:
cat > frps.toml << EOF
bindPort = 7000
vhostHTTPPort = 8080
vhostHTTPSPort = 8443
log.to = "console"
EOF
bindPort is where frpc clients connect.vhostHTTPPort is where FRP listens for HTTP virtual-host traffic (per-domain routing).vhostHTTPSPort is reserved if you later do HTTPS-level handling through FRP itself.Now create a docker-compose.yml to run frps and Caddy on the VPS:
cat > docker-compose.yml << EOF
services:
frps:
image: snowdreamtech/frps:latest
restart: unless-stopped
network_mode: host
volumes:
- ./frps.toml:/etc/frp/frps.toml
caddy:
image: caddy:alpine
restart: unless-stopped
network_mode: host
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_config:/config
volumes:
caddy_data:
caddy_config:
EOF
Create an initial Caddyfile for one domain (more will be added later):
cat > Caddyfile << EOF
yourdomain.com {
reverse_proxy localhost:8080
}
EOF
Start the stack:
docker compose up -d
docker logs frps
docker logs caddy
At this point, the VPS is ready to accept FRP client connections on port 7000 and HTTP traffic on port 8080, with Caddy handling HTTPS on 443 and proxying requests down to FRP.
On Machine 1, Immich is running at http://192.168.1.14:2283. You will run frpc on this same machine to forward yourdomain.com to that internal address.
Create a client directory:
mkdir -p ~/frp-client
cd ~/frp-client
Create frpc.toml:
cat > frpc.toml << EOF
serverAddr = "150.265.32.125"
serverPort = 7000
[[proxies]]
name = "immich"
type = "http"
localIP = "192.168.1.14"
localPort = 2283
customDomains = ["yourdomain.com"]
EOF
Key points from FRP's HTTP vhost docs:
type = "http" is what allows FRP to route based on Host header.customDomains must match the domain you will point at the VPS.Create a docker-compose.yml for frpc:
cat > docker-compose.yml << EOF
services:
frpc:
image: snowdreamtech/frpc:latest
container_name: frpc
restart: unless-stopped
network_mode: host
volumes:
- ./frpc.toml:/etc/frp/frpc.toml
EOF
Start the client:
docker compose up -d
docker logs frpc
You should see a log like:
[immich] start proxy success
In your DNS provider (for example, Cloudflare), create an A record:
Type: A
Name: images
IPv4: 150.265.32.125
Proxy: DNS only (no CDN proxy while debugging)
Caddy is already configured for yourdomain.com and will automatically request a Let's Encrypt certificate and terminate HTTPS.
Test in your browser:
https://yourdomain.com
If Immich is reachable locally and frpc/frps are connected, you should see Immich loaded over HTTPS. If not:
docker logs frpc on Machine 1.docker logs frps and docker logs caddy on the VPS for TLS or proxy errors.On the same Machine 1, n8n is available at http://192.168.1.14:5678 and should be exposed as automation.yourdomain.com. FRP supports multiple [[proxies]] entries in one frpc.toml, all mapped to different domains.
Edit ~/frp-client/frpc.toml on Machine 1:
cd ~/frp-client
cat > frpc.toml << EOF
serverAddr = "150.265.32.125"
serverPort = 7000
[[proxies]]
name = "immich"
type = "http"
localIP = "192.168.1.14"
localPort = 2283
customDomains = ["yourdomain.com"]
[[proxies]]
name = "n8n"
type = "http"
localIP = "192.168.1.14"
localPort = 5678
customDomains = ["automation.yourdomain.com"]
hostHeaderRewrite = "automation.yourdomain.com"
EOF
The hostHeaderRewrite directive is taken directly from FRP's header modification docs and ensures that the Host header reaching n8n is exactly what n8n expects.
Restart frpc:
docker compose down
docker compose up -d
docker logs frpc
On the VPS, extend your Caddyfile:
cd ~/frp-server
cat > Caddyfile << EOF
yourdomain.com {
reverse_proxy localhost:8080
}
automation.yourdomain.com {
reverse_proxy localhost:8080 {
header_up Host automation.yourdomain.com
}
}
EOF
docker compose restart caddy
Add DNS for the new subdomain:
Type: A
Name: automation
IPv4: 150.265.32.125
Proxy: DNS only
Then test:
https://automation.yourdomain.com
If you encounter redirect loops with n8n, they almost always come from incorrect Host/Proto headers or misaligned n8n base URL configuration. Using hostHeaderRewrite on frpc and header_up Host in Caddy removes that class of problem.
Now you want to expose a service running on Machine 2, for example http://192.168.1.63:11000, and map it to cloud.yourdomain.com. You do not reuse the frpc on Machine 1 for this; you run a separate frpc on Machine 2, pointing to the same frps.
On Machine 2:
mkdir -p ~/frp-client
cd ~/frp-client
Create frpc.toml:
cat > frpc.toml << EOF
serverAddr = "150.265.32.125"
serverPort = 7000
[[proxies]]
name = "cloud-dublin"
type = "http"
localIP = "192.168.1.63"
localPort = 11000
customDomains = ["cloud.yourdomain.com"]
EOF
Create docker-compose.yml:
cat > docker-compose.yml << EOF
services:
frpc:
image: snowdreamtech/frpc:latest
container_name: frpc
restart: unless-stopped
network_mode: host
volumes:
- ./frpc.toml:/etc/frp/frpc.toml
EOF
Start frpc:
docker compose up -d
docker logs frpc
On the VPS, expand Caddyfile again:
cd ~/frp-server
cat > Caddyfile << EOF
yourdomain.com {
reverse_proxy localhost:8080
}
automation.yourdomain.com {
reverse_proxy localhost:8080 {
header_up Host automation.yourdomain.com
}
}
cloud.yourdomain.com {
reverse_proxy localhost:8080
}
EOF
docker compose restart caddy
Create DNS for cloud.yourdomain.com pointing to the VPS IP. Now:
yourdomain.com → VPS → frps → Machine 1automation.yourdomain.com → VPS → frps → Machine 1cloud.yourdomain.com → VPS → frps → Machine 2Only one list, but a useful one:
frps) running and listening on 7000/8080, no port conflicts.frpc) running on each machine, logs show start proxy success.localIP:localPort.hostHeaderRewrite and proper Host headers are set.Once this pipeline is in place, adding a new service is just: Add a `[[proxies]]` block in `frpc.toml` on the relevant machine, add a domain block in `Caddyfile` on the VPS, add an A record in DNS, and restart frpc and Caddy. No ISP calls, no port forwarding, no CGNAT pain—just clean domains for all your self-hosted tools.
Let's discuss your project and create something amazing together.