4 min read

Digging In with Pangolin - A FOSS Alternative to Cloudflare Tunnel

Expose your homelab without opening ports using the Pangolin stack. A self-hosted, fossorial alternative to Cloudflare Tunnel. Fully integrated with Traefik for secure and flexible ingress.

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!