Digging In with Pangolin - A FOSS Alternative to Cloudflare Tunnel
Exposing homelab services securely and flexibly is an ongoing challenge, especially when you care about minimal external dependencies and full control over the network. If you've already read my post about tunneling with WireGuard and socat (a.k.a. Standleitung), you know the drill.
So here’s a new approach: Pangolin + Gerbil for tunneling, paired with the Newt client on the homelab side. From Traefik’s perspective, Pangolin acts as a config provider. Gerbil manages the WireGuard tunnels on the VPS, making sure traffic flows securely through to your network. Newt forms the other end of the tunnel, running inside your homelab, so requests can be sent to your actual services.
I continue to use Traefik for ingress and SSL, just as before. And yes, it still avoids exposing a single port on my home router.
If you enjoy animals and networking, you're going to love this one.
⚠️ Disclaimer
While Pangolin, Gerbil, and Newt have friendly names, they are still network-layer tools that can do unwanted stuff or expose services you don't want to expose if misconfigured. Secure your tunnel endpoints and audit your setup regularly.
Make sure...
- your VPS is hardened (see my previous post for baseline tips)
- you understand what each container does
- you’ve mapped out how the traffic flows
- you know how to debug when something doesn’t work (and it will, at some point)
Overall, the official Pangolin documentation is very well-structured. The deployment process and application configuration are fully documented. However, I made a few minor changes and simplified some things on my own. A quick look at the documentation won't hurt. ;)
Requirements
Let’s cover what you need before spinning up any containers:
- VPS with a public IP address
- Public domain pointing to your VPS (e.g. A-Record)
- Home server with one or more services that should be exposed remotely
- Traefik already running on the VPS with a static and dynamic config
- Basic knowledge of how to use Docker Compose and Traefik
Pangolin + Gerbil + Traefik on the VPS
We'll start with the main services on the VPS. Pangolin acts as the management tool with web dashboard and traefik configuration provider. Gerbil manages the tunnels. These two together make sure that external requests can find their way to your homelab, securely and reliably.
💡 As I mentioned before, I reused my existing Traefik setup almost exactly as it was, only adding the service and the existing Traefik network to the Compose file. If you've also deployed other services on your VPS that are part of your existing Traefik network and exposed by it, you can do it like me and add your existing traefik network as external network. This makes it easy to migrate your services to the new Pangolin network step by step later. Please note that Traefik now runs on the Gerbils network (network_mode), which exposes its ports. Make sure you're using your existing configurations and Letsencrypt ACME file so you don't run into a rate limit at startup.
A really good diagram in the official Pangolin documentation explains how the services work together.
Compose
##########
# Pangolin + Gerbil + Traefik Compose
# docker-compose.pangolin.yml
##########
---
services:
pangolin:
image: fosrl/pangolin:1.8.0
restart: unless-stopped
volumes:
- ./pangolin/config:/app/config
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3001/api/v1/"]
interval: "3s"
timeout: "3s"
retries: 15
networks:
- pangolin
gerbil:
image: fosrl/gerbil:1.1.0
restart: unless-stopped
depends_on:
pangolin:
condition: service_healthy
command:
- --reachableAt=http://gerbil:3003
- --generateAndSaveKeyTo=/var/config/key
- --remoteConfig=http://pangolin:3001/api/v1/gerbil/get-config
- --reportBandwidthTo=http://pangolin:3001/api/v1/gerbil/receive-bandwidth
volumes:
- ./gerbil/config:/var/config
cap_add:
- NET_ADMIN
- SYS_MODULE
ports:
- 51820:51820/udp
- 80:80/tcp
- 443:443/tcp
- 443:443/udp
- 5223:5223/tcp
networks:
- pangolin
# As mentioned before, this is necessary
# if you have an existing Traefik network
# with services exposed over it.
- traefik
traefik:
image: traefik:v3.5.0
restart: unless-stopped
command:
- "--configFile=/etc/traefik/traefik.yml"
volumes:
- ./traefik/config:/etc/traefik
network_mode: service:gerbil
depends_on:
pangolin:
condition: service_healthy
socket-proxy:
condition: service_started
socket-proxy:
image: ghcr.io/linuxserver/socket-proxy:3.2.3
restart: unless-stopped
environment:
CONTAINERS: 1
EVENTS: 1
INFO: 1
LOG_LEVEL: warning
NETWORKS: 1
PING: 1
VERSION: 1
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
read_only: true
tmpfs:
- /run
networks:
- pangolin
networks:
pangolin:
# As mentioned before, this is necessary
# if you have an existing Traefik network
# with services exposed over it.
traefik:
external: true
Pangolin Configuration
Before starting the Pangolin container for the first time, you’ll need to manually create the configuration file. It isn’t generated automatically and cannot be built from environment variables. Without this config, Pangolin won’t start properly. The Pangolin documentation provides useful information to help you create your own. Just make sure to adjust settings such as ports
, domains
, smtp
and traefik
configuration to match your setup.
Traefik Configuration
Pangolin provides example Traefik configurations in its documentation, covering both static and dynamic settings. You don’t need to replace your existing setup. Instead you can merge the provided example with your own static configuration especially making sure the providers
, entryPoints
and certificateResolvers
sections are aligned and badger
is part of your plugins-section. The same applies to the dynamic config: just integrate the provided routers and services into your existing dynamic configuration. This approach keeps your setup clean and avoids breaking other services you’re already exposing via Traefik.
In my opinion, the routers provided by Pangolin are unnecessarily complicated and can be summarized as 2:
web-router:
rule: "Host(`pangolin.domain.de`) && !PathPrefix(`/api/v1`) && !PathPrefix(`/ws`)"
service: next-service
entryPoints:
- websecure
api-ws-router:
rule: "Host(`pangolin.domain.de`) && (PathPrefix(`/api/v1`) || PathPrefix(`/ws`))"
service: api-service
entryPoints:
- websecure
💡 This approach allows you to continue managing services that you've already published on the VPS via Traefik. Additionally, you can now publish your Homelab services with Pangolin. Nice!
Newt on the Homelab
On your homelab machine, the Newt client establishes a persistent connection to your VPS and bridges traffic back and forth. You don’t need to open any ports locally. The Newt client will map remote requests to local ports, forwarding the traffic to your actual services.
💡 I’m not providing a Compose file here, because when you create a site in the Pangolin web UI, it automatically provides the necessary commands or file templates for all supported systems and deployment methods. Super handy!
