> ## Content Index
> Fetch the complete content index at: https://notes.d4rkcyber.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Pangolin
- URL: https://notes.d4rkcyber.com/pangolin-across-multiple-servers/
- Published: 2026-10-29T13:22:59.000Z
- Updated: 2026-10-29T13:22:59.000Z
- Description: How I use Pangolin for access control and reverse proxying across multiple servers, with sanitized Compose and configuration examples.
- Author: David Dami
- Tags: Infrastructure, homelab, reverse-proxy, dns

## How I Use Pangolin Across My Servers

I use Pangolin for access control and reverse proxying across multiple servers. The applications stay on the machines where they run; Pangolin gives me a central place to manage how traffic reaches them.

The Pangolin stack runs on `d4rkcloud` with Gerbil and Traefik. Newt connectors run on the other servers and connect those sites back to Pangolin.

## Services Behind Pangolin

The setup covers a mix of personal, home, and business services:

- **docker-vm:** D4rk Notes, Immich, Home Assistant
- **d4rkcloud-2:** Abmye Farms, Unistrut, DocuSeal

Pangolin handles the route and access policy; the applications remain distributed across the servers.

## The Pangolin Compose Stack

This is the central stack on `d4rkcloud`. The hostname is configurable, and credentials are kept out of the Compose file. The example preserves the running service layout and pins the Pangolin, Gerbil, and Traefik images.

```
name: pangolin

services:
  pangolin:
    image: fosrl/pangolin:1.25.3
    container_name: pangolin
    restart: unless-stopped
    volumes:
      - ./config:/app/config
      - ./assets:/assets
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3001/api/v1/"]
      interval: 10s
      timeout: 10s
      retries: 15
    logging:
      driver: json-file
      options:
        max-size: "20m"
        max-file: "10"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.pangolin.rule=Host(`${PANGOLIN_HOST}`)"
      - "traefik.http.routers.pangolin.entrypoints=websecure"
      - "traefik.http.routers.pangolin.tls=true"
      - "traefik.http.routers.pangolin.tls.certresolver=letsencrypt"
      - "traefik.http.services.pangolin.loadbalancer.server.port=3001"

  gerbil:
    image: docker.io/fosrl/gerbil:1.4.3
    container_name: gerbil
    restart: unless-stopped
    depends_on:
      pangolin:
        condition: service_healthy
    command:
      - --reachableAt=http://gerbil:3004
      - --generateAndSaveKeyTo=/var/config/key
      - --remoteConfig=http://pangolin:3001/api/v1/
    volumes:
      - ./config/:/var/config
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    ports:
      - "51830:51830/udp"
      - "21820:21820/udp"
      - "443:443"
      - "80:80"
    networks:
      - proxy
      - default

  traefik:
    image: docker.io/traefik:v3.6.2
    container_name: traefik
    restart: unless-stopped
    network_mode: service:gerbil
    env_file:
      - .env
    depends_on:
      pangolin:
        condition: service_healthy
    command:
      - --configFile=/etc/traefik/traefik_config.yml
    volumes:
      - ./config/traefik:/etc/traefik:ro
      - ./config/letsencrypt:/letsencrypt
      - ./config/traefik/logs:/var/log/traefik
    logging:
      driver: json-file
      options:
        max-size: "20m"
        max-file: "10"

networks:
  default:
    driver: bridge
    name: pangolin
  proxy:
    name: proxy
    external: true
```

## Central Stack Environment Example

The live `.env` file contains a Cloudflare DNS token used by Traefik’s DNS challenge. Keep the real file local and protected; this example uses placeholders only.

```
# .env.example — Pangolin host
PANGOLIN_HOST=your-pangolin-hostname
CF_DNS_API_TOKEN=replace-with-your-cloudflare-dns-token
```

## Pangolin Configuration Example

The deployed configuration file on this stack is named `config.yml`. This sanitized `config.yaml` example shows the same main settings without copying the live server secret, SMTP password, or real hostnames.

```
gerbil:
  start_port: 51830
  base_endpoint: "your-pangolin-hostname"

app:
  dashboard_url: "https://your-pangolin-hostname"
  log_level: "debug"
  telemetry:
    anonymous_usage: true

domains:
  domain1:
    base_domain: "your-domain.example"

server:
  secret: "REPLACE_WITH_A_GENERATED_SECRET"
  cors:
    origins:
      - "https://your-pangolin-hostname"
    methods:
      - GET
      - POST
      - PUT
      - DELETE
      - PATCH
    allowed_headers:
      - X-CSRF-Token
      - Content-Type
    credentials: false
  maxmind_db_path: "./config/GeoLite2-Country.mmdb"
  integration_port: 3003

flags:
  require_email_verification: false
  disable_signup_without_invite: true
  disable_user_create_org: false
  allow_raw_resources: true
  enable_integration_api: true

email:
  smtp_host: "your-smtp-host"
  smtp_port: 587
  smtp_user: "your-smtp-user"
  smtp_pass: "REPLACE_WITH_SMTP_PASSWORD"
  no_reply: "no-reply@example.invalid"
  smtp_secure: false
  smtp_tls_reject_unauthorized: true
```

Replace the placeholders with values for your own deployment. In particular, generate a unique server secret and set the SMTP password locally. Do not publish a live configuration file.

## Newt Connector on Each Server

Each remote server runs its own Newt connector. The Compose service shape is reusable, but the site ID and secret must be unique for each registered site.

```
services:
  newt:
    image: fosrl/newt
    container_name: newt
    restart: unless-stopped
    environment:
      PANGOLIN_ENDPOINT: ${PANGOLIN_ENDPOINT:?set the Pangolin endpoint}
      NEWT_ID: ${NEWT_ID:?set this site's ID}
      NEWT_SECRET: ${NEWT_SECRET:?set this site's secret}
    volumes:
      - ./data:/root/.config/newt-client
    networks:
      - proxy

networks:
  proxy:
    name: proxy
    external: true
```

Use a separate local environment file for each Newt site:

```
# .env.example — make a separate .env for each server
PANGOLIN_ENDPOINT=https://your-pangolin-hostname
NEWT_ID=replace-with-this-sites-id
NEWT_SECRET=replace-with-this-sites-secret
```

I keep the actual Newt credentials out of the article and out of source control. Each server gets the credentials for its own site.

## How I Think About the Traffic Path

The reverse proxy routes a request to the configured service target. Newt connects the remote site to Pangolin, and the access policy determines who is allowed through. Those are separate layers: routing gets traffic to an application; access control governs who can reach it.

That distinction matters when troubleshooting. A service can be running while its route is wrong, or the route can work while an access policy prevents the expected user from getting in. I check the application, the site connection, the resource target, and the access policy individually.

## Why This Layout Works for Me

I don’t have to move every application onto the Pangolin server or maintain a separate public-entry stack on every machine. The services stay distributed across multiple servers, while access control and reverse proxying are managed centrally.

For my setup, Pangolin ties together access control and reverse proxying across multiple servers. The applications stay where they are; the way I manage access stays in one place.