You want to reach a service running at home from the internet. You have no static IP, you are not opening a port on your router, and you would rather not hand all your traffic to Cloudflare. Pangolin answers exactly that: a small server on a VPS receives the requests, authenticates them, and sends them down a tunnel your network opened outward. This guide installs Pangolin with Docker Compose, enrols the Newt connector, and publishes a first service.

Key takeaways

  • The Newt connector dials out from your network to the VPS over WebSocket and WireGuard, so nothing at home listens for inbound traffic and no static IP is needed.
  • The stable release at the time of writing is Pangolin 1.22.0, published on 27 August 2026, with Newt 1.16.0 from 19 August.
  • The deployment is three containers: the Pangolin server, Gerbil for the WireGuard tunnels, and Traefik v3.7 as the ingress, with the Badger plugin doing authentication.
  • Traefik asks Let’s Encrypt for the certificates over an HTTP challenge, so you need ports 80 and 443 open on the VPS, plus UDP 51820 and 21820.
  • You gain control and lose convenience: the VPS becomes part of your security boundary, and its bandwidth and its uptime are now yours to worry about.

What Pangolin is, and which way the connection goes

Pangolin is an identity-aware tunneled reverse proxy published by Fossorial under a dual AGPL-3 and commercial licence. The fosrl/pangolin[1] repository opened in September 2024 and, at the end of August 2026, has 22,550 stars and 769 forks. It is written in TypeScript; the connector is Go and is plain AGPL-3.

The piece that changes the equation is the direction of the connection. In a classic reverse-proxy setup you open a port on the router and wait for the world to call. Here it runs the other way: the Newt connector, installed on your network, opens two outbound connections to the server.

One is a WebSocket to the control plane, which is how it receives its configuration. The other is a WireGuard[2] tunnel to Gerbil, which is how the data travels.

Diagram of a public request path in Pangolin: Internet, Traefik, Badger, Gerbil, the Newt connector and finally the service hosted on your own network.

Every Pangolin node runs three named processes. Traefik terminates TLS and routes; Badger is a Traefik plugin that applies authentication before anything gets through; Gerbil maintains the WireGuard peers and acts as a relay when NAT hole punching fails. If you already have Traefik running with Docker Compose, most of the configuration file will look familiar.

One guarantee worth internalising early: installing a connector exposes nothing. The documentation puts it plainly: "Sites are software-defined proxies and deny traffic by default". Until you define a resource and grant somebody access, that tunnel leads nowhere.

What you need before installing Pangolin

Four things, none of them optional.

A Linux server with a public IP. The project asks for 1 vCPU, 2 GB of memory and 8 GB of disk as a floor, and suggests 2 vCPU with 20 GB for normal use. Below 1 GB of memory you will need swap so the install does not choke.

A domain of your own, with access to its DNS zone. You need a wildcard A record pointing at the VPS, because every service you publish becomes a separate subdomain:

Type: A     Name: *     Value: YOUR_VPS_IP     TTL: 300
Type: A     Name: @     Value: YOUR_VPS_IP     TTL: 300

Four ports open on the VPS firewall: 80 and 443 on TCP, UDP 51820 for the site tunnels and UDP 21820 for clients. That last one is only needed if you plan to use private resources with the desktop client.

And a real email address, which is what Let’s Encrypt attaches to your certificates and what you will use for the first admin account.

Deploying Pangolin on the VPS with Docker Compose

There is an automated installer (curl -fsSL https://static.pangolin.net/get-installer.sh | bash) that asks questions and writes the files for you. We are taking the manual path here, which produces the same result with the advantage that you understand what you put there. Four steps:

  1. Create the project directory tree.
  2. Write the three configuration files with your domain in them.
  3. Bring the stack up and wait for the containers to report healthy.
  4. Open the initial setup page with the token Pangolin prints to its log.

The tree is fixed and worth respecting, because the compose mounts depend on it:

mkdir -p config/db config/letsencrypt config/traefik/logs

The docker-compose.yml file starts the three pieces. Notice that Traefik publishes no ports: it shares Gerbil’s network stack, and Gerbil is the one exposing them. That network_mode: service:gerbil line is the one people delete because it looks odd, and without it TLS stops working:

name: pangolin
services:
  pangolin:
    image: docker.io/fosrl/pangolin:latest
    container_name: pangolin
    restart: unless-stopped
    volumes:
      - ./config:/app/config
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3001/api/v1/"]
      interval: "10s"
      timeout: "10s"
      retries: 15

  gerbil:
    image: docker.io/fosrl/gerbil:latest
    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:
      - 51820:51820/udp
      - 21820:21820/udp
      - 443:443
      - 80:80

  traefik:
    image: docker.io/traefik:v3.7
    container_name: traefik
    restart: unless-stopped
    network_mode: service:gerbil
    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

networks:
  default:
    driver: bridge
    name: pangolin

With the files written, starting up is one command and one check:

sudo docker compose up -d
sudo docker compose ps
sudo docker compose logs pangolin | grep -i token

On first boot Pangolin creates config/db/db.sqlite, Gerbil writes its key to config/key, and the server prints a single-use setup token to the console. With that token you visit https://pangolin.yourdomain.com/auth/initial-setup and create the admin account. If the browser complains about the certificate during the first minute, that is expected: Let’s Encrypt is still validating.

Which values you have to change in config.yml

The application file is short, and everything it ships as an example has to be replaced. These are the values you cannot leave as they come:

gerbil:
    start_port: 51820
    base_endpoint: "pangolin.yourdomain.com"

app:
    dashboard_url: "https://pangolin.yourdomain.com"
    log_level: "info"

domains:
    domain1:
        base_domain: "yourdomain.com"

server:
    secret: "generate-one-with-openssl-rand-hex-32"
    cors:
        origins: ["https://pangolin.yourdomain.com"]

flags:
    require_email_verification: false
    disable_signup_without_invite: true
    allow_raw_resources: true

The server secret is generated with openssl rand -hex 32 and reused from nowhere else. If you ever need to rotate it, the internal pangctl rotate-server-secret tool does it without touching the database by hand.

Two networking settings are decided now or never. Gerbil hands out addresses inside 100.89.137.0/20, a CGNAT range chosen so it does not collide with the usual private networks, and gives each site a /30, which is four addresses. If that range overlaps yours, change it before you register the first Gerbil; afterwards you cannot.

The Traefik file needs two more substitutions: your email in the letsencrypt resolver, which uses an HTTP challenge against the web entry point, and the Badger plugin version, pinned at v1.4.0 in the official template.

Installing Newt on the machine in your network

You create the site in the dashboard first, and the dashboard hands you three values: an ID, a secret and the endpoint. With those, installing the connector on the home machine is direct:

curl -fsSL https://static.pangolin.net/get-newt.sh | bash

newt 
  --id 31frd0uzbjvp721 
  --secret h51mmlknrvrwv8s4r1i210azhumt6isgbpyavxodibx1k2d6 
  --endpoint https://pangolin.yourdomain.com

That is fine for a test. To leave it running, the project documents a systemd service reading credentials from /etc/newt/newt.env with 600 permissions, so the secret never appears in the process list or in your shell history.

If you prefer a container, the image is fosrl/newt and the three variables are PANGOLIN_ENDPOINT, NEWT_ID and NEWT_SECRET. The documentation also proposes a cleaner variant: put the configuration in a JSON file and pass it as a Compose secret with CONFIG_FILE=/run/secrets/newt-config.

services:
  newt:
    image: fosrl/newt
    container_name: newt
    restart: unless-stopped
    environment:
      - PANGOLIN_ENDPOINT=https://pangolin.yourdomain.com
      - NEWT_ID=2ix2t8xk22ubpfy
      - NEWT_SECRET=nnisrfsdfc7prqsp9ewo1dvtvci50j5uiqotez00dgap0ii2

Two other site types exist, and it is worth knowing about them mainly to rule them out. A local site exposes services living on the VPS itself, with no tunnel. A basic WireGuard site uses a raw connection with no control channel, requires NAT to reach other machines on the network, and gives up private resources, health checks and Docker socket scanning. Newt is the recommended path and the only one with the full feature set.

Publishing your first service end to end

A public HTTP resource is the case this section covers. You assign it a fully qualified name inside the domain you registered, add one or more targets with their address and port on the remote network, and pick the site it is reached through. Pangolin terminates TLS, applies the access rules, and only then puts the request into the tunnel.

With more than one target on different sites you get round-robin load balancing and automatic failover when a health check fails. With a single one you have what you need to publish your notes server or your metrics dashboard.

The certificate needs no action from you: when the resource is created, Traefik requests one from Let’s Encrypt for the new subdomain and stores it in config/letsencrypt/acme.json. That is why port 80 has to stay open even when you serve nothing unencrypted.

What authentication comes built in

Every public resource is born with Pangolin’s own single sign-on enabled. From there the options stack, and they can be shared across resources through policies:

Method What it is for
Pangolin platform SSO Account with username, password and a second factor inside the dashboard
External identity provider Google, Azure Entra ID, Okta or any generic OIDC provider
Users and roles Access per person or per group, resource by resource
PIN or passcode A simple barrier for something that does not deserve an account
Header auth Machine-to-machine requests with a username and password
Email one-time passcode Domain whitelist, in the style of *@yourcompany.com
Shareable links One-off access with an expiry, revoked by deleting the link
Ranked rules Allow or deny by IP, by country or by path

The email-domain whitelist is what solves the most common household case: giving the family access without creating accounts. Shareable links carry a token that travels as a p_token query parameter or in the P-Access-Token-Id and P-Access-Token headers, and it must be sent on every request, not just the first. One detail that gets missed: link access does not forward the user’s identity headers to the backend application, so it is no good for wiring the session into the app behind it. For that, your own identity provider such as Pocket ID with passkeys fits better.

What to check when a resource will not respond

In order of likelihood:

  1. DNS is not resolving. The wildcard takes time to propagate. Check the specific subdomain, not just the root domain.
  2. The certificate is not issued. Almost always port 80 closed, or the example email left unchanged in the Traefik file.
  3. The site shows as disconnected. Look at the Newt log on your network. If it retries over and over, UDP 51820 is blocked on the VPS.
  4. The tunnel is fine and the resource returns a 502. The target points at an address Newt cannot reach. Remember it resolves from your network, not from the VPS.
  5. Everything works except the desktop client. UDP 21820 is missing, which is what the relay uses when hole punching does not succeed.
  6. Overlapping addresses. If your network uses the 100.64.0.0/10 range, Gerbil’s default block collides and cannot be changed without redoing the registration.

Pangolin against Cloudflare Tunnel and against a WireGuard mesh

All three avoid opening a port. They differ in who sees your traffic and in what it costs you.

Pangolin Cloudflare Tunnel WireGuard mesh
Outbound connection from your network Yes Yes Yes
Infrastructure you maintain One VPS None A coordinating node
Who decrypts the traffic Your VPS Cloudflare Nobody in between
Access from somebody else’s browser Yes Yes No, a client is required
Per-resource identity and rules Built in With Cloudflare Access Out of scope
Upload limit Whatever your VPS allows 100 MB on the Free and Pro plans Whatever your link allows
Monthly cost The VPS Zero Zero or the node

That upload limit is documented by Cloudflare itself: 100 MB on the Free and Pro plans[3], 200 MB on Business and 500 MB or more on Enterprise. If you publish an Immich or a Nextcloud, you will meet it on day one.

Against the mesh, the comparison is different. Headscale or wg-easy give you private access between your own machines, encrypted end to end, with no intermediary decrypting anything. What they do not give you is an address you can email to somebody who will never install a VPN client. Pangolin covers both cases, with public resources through the browser and private resources through a client, which is why its repository now describes itself as a VPN and a reverse proxy at once.

Now the uncomfortable part, and the Pangolin documentation writes it without decoration: "By tunneling out to the VPS, you are effectively including the VPS in your security boundary, so you must secure it as part of your overall network strategy". That server stops being a rented box and becomes part of your network. Its patching, its uptime and its bandwidth bill are your responsibility now. With Cloudflare somebody else carries that; the price is that somebody else sees your requests in the clear.

Who Pangolin is for, and who it is not for

It makes sense if you publish more than one service, want one login screen in front of all of them, and dislike a third party terminating your TLS. It does not if you publish a single service and do not mind Cloudflare seeing it, because you would be paying for a VPS for nothing. Nor if your requirement is that nobody outside can even attempt to get in: a closed mesh such as the free alternative to Tailscale does that better.

It is also worth knowing where the free edition ends. Community Edition is AGPL-3 and, since version 1.22.0, includes browser-based SSH, RDP and VNC resources plus private HTTP and SSH. Enterprise, with node clustering for high availability, ships under the commercial licence: free below 100,000 dollars of annual revenue, paid above it. A single node remains a single point of failure.

Frequently asked questions

Do I need to open any port on my router?

No. The Newt connector starts both connections towards the VPS, one over WebSocket and one over WireGuard. The ports that need opening are on the VPS, not at your house. You do not need a static IP or dynamic DNS either.

Can I run Pangolin without tunneling, as a plain reverse proxy?

Yes. The installer asks whether you want Gerbil, and there is a documented no-tunnel mode where you drop that container and Traefik publishes the ports directly. In that case you lose the connector half and are left with an authenticating reverse proxy.

What happens if the VPS goes down?

Everything you were publishing goes with it. The free edition runs on a single node with no clustering, so the VPS is a single point of failure. High availability across more than one node is an Enterprise feature.

Conclusion

Installing Pangolin is four files, three containers and one connector. What you buy with that work is an entry point you control, with identity built in and automatic certificates, without opening a port at home. What you pay is a VPS that is now part of your perimeter and has to be looked after as such.

If those numbers add up for you, it is the best answer available today for publishing home services without depending on anybody. The Spanish version of this guide is at Cómo instalar Pangolin.

Sources

  1. fosrl/pangolin
  2. WireGuard
  3. 100 MB on the Free and Pro plans
  4. Pangolin, manual Docker Compose deployment
  5. Pangolin, DNS and networking
  6. Pangolin, system architecture
  7. Pangolin, install the Newt connector
  8. Pangolin, public resource authentication
  9. Pangolin, Enterprise Edition
  10. fosrl/newt, published releases