# How Pangolin Works Source: https://docs.pangolin.net/about/how-pangolin-works Learn about the fundamentals of Pangolin and how they work together to provide simple and secure remote access.
## Basic Steps Join remote networks together using site connectors. Pangolin's lightweight connectors use intelligent routing and NAT traversal to make any network anywhere available. Create resources that represent services or entire network ranges available for remote access. Resources can be public or fully private. Authenticated users access resources through a web browser (public resources) or when connected with a Pangolin client (private resources). The same users, roles, and policies apply across both—users never pick a site; Pangolin routes to the right connector automatically. ## Key Concepts Pangolin relies on several components that work together to provide secure remote access. Each component has a specific role in ensuring that only authenticated users can access the resources they are authorized to use. ### Pangolin Server The Pangolin server is the central coordination component for your network. It stores configuration changes, manages access policies, and coordinates connections between clients and sites. The server handles user authentication and generates access control lists that determine what resources each user can reach. You can use [Pangolin Cloud](https://app.pangolin.net/auth/signup), which is fully managed, or you can self-host your own Pangolin server for complete control over your infrastructure and data. Fastest way to get started with the fully managed control plane. No credit card required. Learn how to deploy your own self-hosted Pangolin server. ### Sites Sites connect remote networks to your Pangolin server. They use Newt connectors to create secure tunnels from remote networks back to Pangolin. Sites let you expose resources on those networks to authorized users. Sites run behind firewalls on remote networks. They maintain outbound connections to the Pangolin server. By default, sites block all traffic until you define resources and grant access. This ensures that just deploying a site does not expose any network resources. The Newt connector handles tunnel creation, NAT traversal, and routing. It makes remote networks available without requiring complex firewall rules or public IP addresses. Newt sites also unlock browser-based SSH, RDP, and VNC resources, private HTTP with edge TLS termination, and intelligent multi-site routing when the same resource is reachable from more than one location. Learn about sites, how they work, and how to install and configure them. ### Resources Resources are the applications, hosts, or network ranges you make available to users. They exist on sites and represent what users can access. Users connect to resources, not to sites directly. There are two types of resources. [Public resources](/manage/resources/understanding-resources#public-resource-types) work through web browsers and act as reverse proxies—or protocol-specific proxies—for backend services. [Private resources](/manage/resources/understanding-resources#private-resource-types) require a client connection and function like a zero-trust VPN. What sets Pangolin apart is the breadth of resource types on one platform: * **Public HTTP/HTTPS** — authenticated reverse proxies with SSO, access rules, and automatic TLS. No client required. * **Public SSH, RDP, and VNC** — full terminal, desktop, or display sessions rendered in the browser. No SSH client or remote desktop software needed. * **Public TCP/UDP** — raw port proxies for protocols that do not need a domain name or authentication layer. * **Private host and CIDR** — route traffic to specific machines or entire subnets over the tunnel, with per-resource port restrictions. * **Private HTTP/HTTPS** — reverse proxy with TLS terminated at your network edge over the tunnel. The app is never exposed on the public internet; only connected clients can reach it. * **Private SSH** — traditional terminal access via `pangolin ssh`, with optional automatic user provisioning from Pangolin identity. You must define resources and assign access before users can reach them. By default, no resources are available on sites. This ensures that only explicitly defined resources can be accessed. Learn about public and private resources and how to create them. ### Clients Clients are software components installed on user devices or machines. They let users and automated systems connect to your Pangolin network and access private resources through a secure tunnel. Users authenticate through the client using their accounts. Machines connect with credentials. Once connected, users can reach all resources their account has access to. The client handles routing decisions and establishes encrypted tunnels to the appropriate sites. Clients are available for Mac, Windows, and Linux. They work transparently with applications, so no application configuration is required. Learn about clients and where to download them for Mac, Windows, and Linux. ### Remote Nodes Remote nodes are self-hosted Pangolin servers that you control while using Pangolin Cloud for management and coordination. You maintain complete control over your infrastructure and data flow, while the cloud handles the control plane, DNS, certificate management, and backups. You can deploy multiple remote nodes for high availability and automatic failover. If your nodes become unavailable, traffic can optionally fail over to cloud infrastructure until you restore service. Learn about remote nodes and how they provide high availability and simplified operations. # Pangolin vs. Proxy vs. VPN Source: https://docs.pangolin.net/about/pangolin-vs-reverse-proxy-vs-vpn What are the similarities and differences between Pangolin and traditional reverse proxies and VPNs?
Pangolin combines the capabilities of both a reverse proxy and a VPN into a single platform. It provides reverse proxy functionality through public resources and VPN functionality through private resources, all with zero-trust access control and distributed architecture. ## What Each Solution Provides **Reverse Proxies** expose web applications to the internet. They handle SSL termination, load balancing, and basic authentication. Users access applications through web browsers using domain names. **VPNs** create encrypted tunnels that give users access to entire private networks. Users install client software and connect to a VPN server. Once connected, they can access any resource on the network they have network-level access to. **Pangolin** provides both capabilities—and several things neither traditional tool does on its own. Public resources work like a reverse proxy, allowing browser-based access to specific applications. Private resources work like a zero-trust VPN, giving users access to specific hosts or network ranges when connected with a client. ## What Pangolin Does Differently | Capability | Traditional reverse proxy | Traditional VPN | Pangolin | | --------------------------------- | ------------------------- | --------------- | ------------------------------------------------------------------ | | Browser access to web apps | Yes | No | Yes — public HTTP/HTTPS | | Browser SSH, RDP, VNC | No | No | Yes — public SSH, RDP, VNC | | Client-only private access | No | Yes | Yes — host, CIDR, SSH, private HTTP | | Per-resource access control | Limited | Network-wide | Yes — users and roles per resource | | No open inbound ports | No | Sometimes | Yes — outbound site tunnels | | TLS at the network edge (private) | No | No | Yes — [private HTTP/HTTPS](/manage/resources/private/private-http) | | Multi-site routing and failover | Uncommon | Uncommon | Yes — automatic site selection | ## Reverse Proxy Capabilities Pangolin's public resources function as reverse proxies—and go further than HTTP alone. **HTTP/HTTPS** resources expose web applications through domain names with automatic SSL certificates. Users access them in a browser with no client installed. Identity-aware access control supports SSO, MFA, and rules based on user identity, roles, geographic location, IP addresses, and URL paths. **SSH, RDP, and VNC** resources render full sessions in the browser. Users get a terminal, Windows desktop, or VNC display without installing SSH clients or remote desktop software—while still passing through Pangolin authentication first. **TCP and UDP** resources bind to a port on the Pangolin server for raw protocol proxying when you need a public pipe without a domain name or auth layer. Unlike traditional reverse proxies, Pangolin does not require public IP addresses or open ports on your network. Sites create outbound tunnels to Pangolin, so your applications remain behind firewalls. ## VPN Capabilities Pangolin's private resources function like a zero-trust VPN—but with tighter scope than a traditional VPN. **Host and CIDR** resources route traffic to specific machines or subnets over the tunnel. Users only reach what you explicitly grant them, with optional per-resource port restrictions—not an entire flat network. **Private HTTP/HTTPS** resources behave like a reverse proxy that only exists on the tunnel. TLS terminates at your [site edge over peer-to-peer transport](/manage/resources/private/private-http)—the application is never reachable from the public internet, only from connected clients with valid access. **Private SSH** resources provide terminal access via `pangolin ssh`, with optional automatic user provisioning from Pangolin identity—no manual key distribution required. Clients work transparently with applications. No application configuration is required. Users connect once and can access all their authorized resources. The client handles routing and establishes encrypted tunnels automatically. ## Why Pangolin Combines Both Many organizations need both reverse proxy and VPN capabilities. You might want to expose a customer portal through a browser while also giving developers SSH access to internal servers and a private HTTPS dashboard that never touches the public internet. With Pangolin, you use one platform for all of these. Public resources handle browser-based access—including SSH, RDP, and VNC when you want sessions without a client. Private resources handle tunnel-only access to hosts, subnets, internal HTTPS apps, and CLI SSH. Both use the same authentication system, access control policies, and infrastructure. This unified approach simplifies management. You configure users, roles, and access policies once. Those policies apply to both public and private resources. You do not need to maintain separate systems for reverse proxy and VPN access. ## Infrastructure and Availability Traditional reverse proxies and VPNs typically run on a single server. If that server fails, all access is lost. They also require public IP addresses and open ports, which adds complexity and security concerns. Pangolin uses a distributed architecture with multiple nodes. If one node fails, traffic automatically routes to another node. Sites create outbound tunnels, so your networks do not need public IP addresses or open ports. When a resource is reachable from multiple site connectors, Pangolin selects the healthiest path based on latency and availability—users connect to the resource, not to a specific site. You can deploy multiple remote nodes for high availability. If your nodes become unavailable, traffic can optionally fail over to cloud infrastructure until you restore service. ## When to Use Each Solution Use a traditional reverse proxy if you only need to expose web applications over HTTP/HTTPS, you have a public IP address, and you do not need advanced access control or high availability. Use a traditional VPN if you need broad network access, you can accept the security risks of flat network visibility, and you do not need application-specific access control. Use Pangolin if you need both reverse proxy and VPN capabilities, browser-based SSH/RDP/VNC, private HTTPS with edge TLS termination, granular per-resource access control, multi-site routing, high availability, or outbound-only connectivity without open ports on your networks. # Contribution Guide Source: https://docs.pangolin.net/development/contributing Set up your local development environment for contributing to Pangolin
This guide describes how to set up your local development environment for contributing to Pangolin. We recommend using Docker Compose for the most consistent development experience across different environments. ## Prerequisites * Node 24 * NPM 11 or similar * Go 1.25 * Git * Docker & Docker Compose * Python (for NPM builds) * Make * G++ For managing multiple versions of Go, you may want to use [gvm](https://github.com/moovweb/gvm). For managing multiple versions of NodeJS, you may want to use [nvm](https://github.com/nvm-sh/nvm). ## Setup Your Repository Below is an example if you're working on the Pangolin repository. [Fork](https://help.github.com/articles/fork-a-repo/) the repository(ies) to your own GitHub account and [clone](https://help.github.com/articles/cloning-a-repository/) to your local device: ```bash theme={"theme":"gruvbox-light-hard"} git clone https://github.com/YOUR_USERNAME/pangolin.git cd pangolin/ ``` Add the remote `upstream`: ```bash theme={"theme":"gruvbox-light-hard"} git remote add upstream https://github.com/fosrl/pangolin.git ``` Create a new branch: ```bash theme={"theme":"gruvbox-light-hard"} git checkout -b BRANCH_NAME dev ``` It is recommended to give your branch a meaningful name, relevant to the feature or fix you are working on. **Good examples**: * `docs-docker` * `feature-new-system` * `fix-title-cards` **Bad examples**: * `bug` * `docs` * `feature` * `fix` * `patch` If you open a pull request, open it against the `dev` branch of the original repository. ## Important Best Practices for PRs * **Keep PRs small and single-purpose**: One feature, fix, or improvement per PR for easier review and testing. * **Prefer improvements over new features**: If you want to propose a net-new feature, contact us by email or on Discord first so we can confirm it fits the roadmap and help scope it. * **Frontend consistency**: * Use existing styles, components, and patterns. * Use Credenza for modals and Zod for form validation. * Keep Tailwind classes minimal; prefer component defaults. * Look for an existing example and mirror that pattern. Extract a small reusable component only when it clearly improves reuse. * **Stick to established patterns**: Avoid introducing new architectures or abstractions without discussing them with us first. * **Auth changes require extra care**: * Pangolin is multi-tenant. Handle user controls at the org level (varies by control) or globally via the server admin panel as appropriate. * Protect all API routes with the correct middleware and verify user permissions and access to referenced entities before performing actions. * **Database changes**: * Keep SQLite and Postgres schemas fully in sync and backward compatible. * Use datatypes supported by both databases. * No need to write versioned migrations; maintainers will handle these during releases. * **Add visuals**: Include screenshots or short videos when applicable to speed up reviews. ## Databases Pangolin supports two database types: SQLite and Postgres. You can switch between them with the provided scripts: Before running these, read local development setup below. ```bash theme={"theme":"gruvbox-light-hard"} npm run set:sqlite # or npm run set:pg ``` After switching, regenerate and apply the schema using the matching scripts for that database. Keep both SQLite and Postgres schemas fully in sync and backward compatible. ## Private Files and Directories Pangolin includes both AGPLv3 code and some proprietary code licensed under the Fossorial Commercial License. Proprietary files include a license header and often live in directories whose names start with `private`. You may edit proprietary files in your PR as long as your PR includes the required CLA. * Frontend: no proprietary code. * Backend: proprietary code exists, primarily under `server/private/`. Subdirectories mirror the structure under `server/`. To keep the AGPLv3 distribution fully compliant, be careful about imports: * AGPLv3 files must never import from the private directory. In TypeScript, the alias `#private/` points to proprietary code and should only be used inside other private files. * If you must expose proprietary behavior to AGPLv3 code, use a dynamic import pattern. Create a file that mirrors the proprietary file’s relative location between `server/private` and `server`, and ensure the exported APIs have exactly matching function signatures. Dynamic import aliases start with `#dynamic`. * At build time, depending on the build flag, `#dynamic` imports are resolved to the appropriate implementation (AGPLv3 or proprietary). Build flags control which distribution you are working on: `oss`, `enterprise`, or `saas`. Enterprise and SaaS include proprietary code; OSS must be 100% AGPLv3 compliant and excludes proprietary code. Use the existing npm scripts to switch: ```bash theme={"theme":"gruvbox-light-hard"} npm run set:oss # or npm run set:enterprise # or npm run set:saas ``` Switching distributions updates TypeScript path aliases so `#dynamic` resolves to the correct locations. The build flag is also used in code to conditionally enable or disable features per distribution. As a rule of thumb, write as much AGPLv3 code as possible. Place only core, distribution-specific functionality in the proprietary layer (Enterprise/SaaS). Database schemas are never proprietary; all distributions share the same schemas. If you have any questions about this setup, email us or reach out on Discord. ## Pangolin Development Setup Choose your preferred development approach. We strongly recommend Docker Compose for the most consistent experience across all platforms. ### Local Development Install package dependencies: ```bash theme={"theme":"gruvbox-light-hard"} npm install ``` Ensure you have a `config/` directory at the root with a `config.yml` inside. Refer to the [Pangolin Configuration docs](/self-host/advanced/config-file) or the `config.example.yml` in the repo for a sample of what to include in that file. You may need to tweak this to run in dev, such as setting the `dashboard_url` to `http://localhost:3002`. Choose to build from the oss/enterprise/saas codebase: ```bash theme={"theme":"gruvbox-light-hard"} npm run set:oss # or npm run set:enterprise # or npm run set:saas ``` Then choose your database: ```bash theme={"theme":"gruvbox-light-hard"} npm run set:sqlite # or npm run set:pg ``` Generate the database schema and push it: ```bash theme={"theme":"gruvbox-light-hard"} npm run db:generate npm run db:push ``` Start the development server using Docker Compose: ```bash theme={"theme":"gruvbox-light-hard"} docker compose up --build ``` Or, start the development server directly: ```bash theme={"theme":"gruvbox-light-hard"} npm run dev ``` ## Exit Nodes When running Pangolin for the first time there will be no exit nodes. This means that there have been no Gerbil "exit nodes" registered in the database, and therefore, you cannot create Newt sites. When Gerbil first starts up and requests its config from Pangolin for the first time it gets registered as an exit node. The easiest way to resolve this is to run Gerbil and have it register in your dev environment. Download the Gerbil binary and run it with localhost: ```bash theme={"theme":"gruvbox-light-hard"} ./gerbil \ --reachableAt=http://localhost:3004 \ --generateAndSaveKeyTo=/var/config/key \ --remoteConfig=http://localhost:3001/api/v1/ ``` Or enter in a dummy exit-node manually to the database: ``` INSERT INTO "exitNodes" ( "name", "address", "endpoint", "publicKey", "listenPort", "reachableAt", "type" ) VALUES ( 'exit-node-1', '10.0.0.1/24', 'gerbil.pangolin.net', 'abc123', 1234, 'gerbil.pangolin.net', 'gerbil' ); ``` ## Windows Development Considerations Windows users with Docker Desktop + WSL2: File change detection may not work properly when project files are stored on the Windows filesystem. **Best performance and compatibility** * **Where to store your project files:** * For best performance, always store your project inside the Linux filesystem of your Docker or Default WSL2 instance, e.g. `/home//pangolin`. * If other WSL instances are used, ensure the Docker Desktop WSL integration is enabled for that distribution. * For further information, see Link Section below. * **Accessing WSL2 files from Windows:** * You can access your WSL2 home directory from Windows using the UNC path: `\\wsl$\\home\\pangolin` (replace `` with your actual WSL distribution, e.g. `Ubuntu-22.04`). * This path works in Windows Explorer, VS Code, and other Windows applications. You can drag & drop files, create shortcuts, or map a network drive for convenience. * **Note:** This UNC path is for Windows tools only. Do not use it for Docker container mounts. * **How to mount WSL2 files in Docker containers:** * Always use the absolute Linux path from inside WSL2 for Docker volumes. This is the only method fully supported and recommended by Docker. * **Correct Docker Compose example:** ```yaml theme={"theme":"gruvbox-light-hard"} services: app: volumes: - /home//pangolin:/app ``` * **Correct docker run example:** ```bash theme={"theme":"gruvbox-light-hard"} docker run -v /home//pangolin:/app my-image ``` * **Never use `\\wsl$` or Windows paths** (e.g. `/mnt/c/...`) for Docker volumes when running with the WSL2 backend. This is not supported and can lead to poor performance or errors. * File watchers and hot reload works natively when your project is inside the WSL2 filesystem and mounted using the Linux path. You may want to use the [VS Code Remote - WSL extension](https://code.visualstudio.com/docs/remote/wsl) or [VS Code Remote - SSH Extension](https://code.visualstudio.com/docs/remote/ssh) to open your project folder directly in VSCode from the WSL/Remote Filesystem for seamless Development. **Reference Links** * [WSL Docker Best Practices](https://docs.docker.com/desktop/features/wsl/best-practices/) * [Use WSL for Development](https://docs.docker.com/desktop/features/wsl/use-wsl/) * [WSL2 Setup](https://docs.docker.com/desktop/features/wsl/) If you need to keep your files on the native Windows filesystem (`C:\Users\...`), enable **Polling Mode** for file watchers. Enable polling mode by adding the following environment variables to your `docker-compose.yml` or `.env` file: For `.env`: ```env theme={"theme":"gruvbox-light-hard"} WATCHPACK_POLLING=true CHOKIDAR_USEPOLLING=true ``` For `docker-compose.yml`: ```yaml theme={"theme":"gruvbox-light-hard"} environment: - WATCHPACK_POLLING=true - CHOKIDAR_USEPOLLING=true ``` This increases CPU usage but ensures file watchers work properly. Polling mode is not required when working directly on the WSL filesystem. ## Component Development ### Gerbil * Go 1.25 ```bash theme={"theme":"gruvbox-light-hard"} make local ``` ### Newt * Go 1.25 ```bash theme={"theme":"gruvbox-light-hard"} make local ``` ### Olm * Go 1.25 ```bash theme={"theme":"gruvbox-light-hard"} make local ``` # Feature Requests & Bug Reports Source: https://docs.pangolin.net/development/feature-requests-and-bug-reports How to submit feature requests and report bugs for Pangolin
We welcome contributions from the community to help improve Pangolin. To ensure your feedback is properly tracked and prioritized, use the repository that matches the component where your issue or request belongs. ## Pick the Right Repository Pangolin is made up of multiple components. To get the fastest response, file your request or bug report in the repository that matches what you're using. ### Server Components Dashboard and core platform. [Discussions](https://github.com/fosrl/pangolin/discussions) and [Issues](https://github.com/fosrl/pangolin/issues). Site and network connector. [Issues](https://github.com/fosrl/newt/issues). WireGuard interface management service. [Issues](https://github.com/fosrl/gerbil/issues). ### User Clients Pangolin desktop application for Windows. [Issues](https://github.com/fosrl/windows/issues). Pangolin mobile application for Android. [Issues](https://github.com/fosrl/android/issues). Pangolin application for macOS, iOS, and iPadOS. [Issues](https://github.com/fosrl/apple/issues). Pangolin CLI client for Linux and macOS environments. [Issues](https://github.com/fosrl/cli/issues). ## Feature Requests We encourage you to submit feature requests in the [GitHub Discussions section](https://github.com/fosrl/pangolin/discussions) of the Pangolin repository. This allows the community to: * **Upvote features** they want to see implemented * **Provide feedback** and suggestions on proposed features * **Discuss implementation details** and alternatives * **Help prioritize** which features to work on next Feature requests with community support (upvotes and positive feedback) are more likely to be prioritized for development. ## Bug Reports Bug reports should be submitted in GitHub Issues for the relevant repository: ### Server Components * **Pangolin**: [fosrl/pangolin/issues](https://github.com/fosrl/pangolin/issues) * **Newt**: [fosrl/newt/issues](https://github.com/fosrl/newt/issues) * **Gerbil**: [fosrl/gerbil/issues](https://github.com/fosrl/gerbil/issues) ### User Clients * **Windows App**: [fosrl/windows/issues](https://github.com/fosrl/windows/issues) * **Android App**: [fosrl/android/issues](https://github.com/fosrl/android/issues) * **Apple Apps**: [fosrl/apple/issues](https://github.com/fosrl/apple/issues) * **Pangolin CLI**: [fosrl/cli/issues](https://github.com/fosrl/cli/issues) * **Olm**: [fosrl/olm/issues](https://github.com/fosrl/olm/issues) Using the correct repo ensures: * **Proper tracking** of bugs through their lifecycle * **Developer visibility** for quick resolution * **Version tracking** and regression testing * **Duplicate detection** and consolidation Please provide as much detail as possible to help developers reproduce and fix the issue quickly. ## Before Submitting * Search existing discussions and issues to avoid duplicates * Provide clear, detailed information * Include steps to reproduce (for bugs) * Test on the latest version of Pangolin * Check if the issue is environment-specific ## Alternative Channels For security vulnerabilities, please email [security@pangolin.net](mailto:security@pangolin.net) instead of posting publicly. For general questions, use [GitHub Discussions](https://github.com/fosrl/pangolin/discussions) with the "Q\&A" category, or come chat with us on [Discord](https://pangolin.net/discord). # System Architecture Source: https://docs.pangolin.net/development/system-architecture A technical overview of how Pangolin's control plane, nodes, connectors, and clients fit together
## Open Source Every layer of Pangolin is open source, from the control plane server through site connectors and end-user clients. Each component has its own repository under [fosrl on GitHub](https://github.com/fosrl); the [component reference](#component-reference) at the bottom of this page lists them. The software that powers [Pangolin Cloud](https://app.pangolin.net) is open source as well. The SaaS control plane runs the same codebase as self-hosted Pangolin, and that code lives in the main [fosrl/pangolin](https://github.com/fosrl/pangolin) repository. Nothing described here is proprietary or hidden behind the managed service. Pangolin is split into a **control plane** (configuration, identity, and orchestration) and a **data plane** (encrypted tunnels, ingress, and relay). Sites and clients both initiate **outbound** connections to a Pangolin node, which keeps remote networks behind firewalls reachable without opening inbound ports on those networks. For a product-level walkthrough of sites, resources, and clients, see [How Pangolin Works](/about/how-pangolin-works). This page focuses on how those pieces are implemented and how traffic moves through the system. Pangolin system architecture diagram ## Control Plane The control plane is the Pangolin server application. It stores organization state in a database, exposes the dashboard and REST API, handles authentication and authorization, and pushes configuration to every node, site connector, and client in real time over WebSocket. Responsibilities include: * **Policy and identity** — users, roles, clients, machines, and access rules that determine which resources each principal can reach * **Resource and site definitions** — targets, destinations, routing preferences, certificates, and health-check configuration * **Orchestration** — coordinates tunnel peers, DNS records, and ingress routes as you create or change resources * **Telemetry** — connection state, relay status, and operational signals surfaced in the dashboard You can run the control plane yourself ([self-hosted](/self-host/quick-install)) or use [Pangolin Cloud](https://app.pangolin.net/auth/signup), where the control plane is fully managed. In both cases, the same orchestration model applies: connectors and nodes pull config from the control plane rather than accepting inbound management connections. ## Nodes A **node** is the networking edge of your Pangolin deployment. It terminates inbound traffic from the public internet, manages WireGuard tunnels, and relays client traffic when a direct peer path is unavailable. On a self-hosted deployment, your Pangolin server runs as a single node by default. [Enterprise clustering](/self-host/advanced/clustering) spreads multiple nodes behind a load balancer with shared database state. On [Pangolin Cloud](/manage/remote-node/understanding-nodes), you can add **remote nodes**: infrastructure you operate that still receives configuration from the cloud control plane. Each node runs several cooperating processes: | Role | Engineering codename | What it does | | ----------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------ | | Ingress / reverse proxy | Traefik | Terminates TLS, routes HTTP(S) and protocol-aware public resources, and forwards authenticated requests into the tunnel fabric | | Tunnel manager | Gerbil | Maintains WireGuard peers for site connectors and clients, performs SNI-based routing, and relays UDP when hole punching fails | | Auth middleware | Badger | Traefik plugin that enforces Pangolin authentication on public resources via forward auth | The control plane writes certificates, Traefik router definitions, and WireGuard peer lists that these processes consume. Nodes do not need direct database access from site connectors; they only need reachability to the control plane and open listener ports on the public side. Required listener ports, firewall rules, and DNS records for a node. Multi-node Enterprise deployments with shared state and failover. Run your own node while using the cloud control plane for management. ## Site Connectors A **site connector** runs on a remote network (server, VM, container, or appliance) and maintains persistent **outbound** connections to a Pangolin node. It is the bridge between your private LAN and the platform. The primary connector implementation is **Newt** (codename). Newt opens: 1. A **WebSocket** to the control plane for configuration, health, and coordination 2. A **WireGuard** tunnel to the node's tunnel manager (Gerbil) for encrypted data traffic Site connectors are deny-by-default proxies. Deploying one does not expose hosts on the network; traffic is forwarded only for [resources](/manage/resources/understanding-resources) you define and grant access to. The connector resolves backend targets on the remote network and delivers packets there. Newt sites support the full feature set: public and private resources, protocol-aware proxies, multi-site routing, health checks, and edge TLS for private HTTP. Other connector types ([local](/manage/sites/understanding-sites#local-site) and [basic WireGuard](/manage/sites/understanding-sites#basic-wireguard-site)) exist for specialized self-hosted cases. Site types, deployment models, and connector capabilities. ## Clients **Clients** are endpoint agents on user devices or machines. They authenticate to the control plane, receive an access control list, and establish WireGuard tunnels so users can reach [private resources](/manage/resources/understanding-resources#private-resource-types). Two client classes share the same tunnel stack: * **User devices** — GUI apps on desktop and mobile; users sign in with their Pangolin identity or SSO * **Machines** — CLI clients for servers and automation; authenticate with an ID and secret ### Shared client stack (Olm) Every Pangolin client shares a common networking core called **Olm** (engineering codename). Olm holds the WireGuard tunnel logic, hole punching and relay negotiation, route installation, and DNS overrides that all clients rely on. Platform apps embed or invoke Olm rather than reimplementing that stack themselves. How Olm is hosted depends on the client: * **macOS and iOS** — Olm runs inside the platform's network extension * **Windows and Android** — Olm runs as the tunnel service behind the native app * **Pangolin CLI** — spawns Olm as a subprocess and manages it over a local API * **Olm CLI** — exposes Olm directly for minimal machine-client deployments Olm is an internal building block, not a product surface. Use the [native clients](/manage/clients/install-client) built for each operating system for the best experience, support, and integration with OS networking APIs. Direct Olm usage is limited to advanced machine-client and automation scenarios; see [Olm (Advanced)](/manage/clients/install-client#olm-advanced) if you need that path. Once connected, the client installs routes for each authorized destination. Pangolin selects the correct site connector automatically; users connect to resources, not to sites directly. See [multi-site routing](/manage/resources/private/multi-site-routing) for how failover works when a resource spans multiple connectors. User devices, machine clients, and how access is granted. Downloads for Mac, Windows, Linux, iOS, iPadOS, and Android. ## Peer-to-Peer and Relay Paths Client-to-site traffic does not always traverse your node. Pangolin negotiates the best available path for each tunnel. ### Direct peer-to-peer (hole punching) By default, the control plane coordinates **NAT hole punching** so a client and site connector can form a direct WireGuard peer connection. Traffic then flows client ↔ site without passing through the node's relay layer. This path typically offers lower latency and less bandwidth use on the node. ### Relay through the node When hole punching fails (restrictive NAT, symmetric NAT, or blocked UDP), the client and site fall back to **relaying** through the node's tunnel manager (Gerbil). Gerbil listens on UDP port 21820 by default, accepts the client's WireGuard packets, and forwards them into the site connector's existing tunnel. The connection stays encrypted end to end; only the network path changes. Site connectors use a separate default port (51820 UDP) for their outbound tunnels to the node. Relay is optional and can be disabled per client if you require direct paths only. Hole punching vs relay, how to check `isRelay` status, and tuning options. ## Traffic Paths Two dominant flows cover most deployments. ### Inbound: public resources Public resources accept traffic from the internet on your node's ingress layer. ``` Internet → Node ingress (Traefik) → Auth (Badger) → Tunnel (Gerbil) → Site connector (Newt) → Backend target ``` HTTP, HTTPS, SSH, RDP, and VNC public resources terminate authentication at the node before traffic enters the tunnel. TCP and UDP public resources bind to node ports and proxy raw traffic without an auth layer. Certificate issuance, router config, and access rules are all driven by the control plane. Protocol types, targets, and how ingress maps to backends. ### Outbound: private resources via client Private resources are reachable only when a client is connected and authorized. ``` Client ↔ (direct peer or relay via Gerbil) ↔ Site connector (Newt) → Destination on remote network ``` The client installs routes for IP, CIDR, or FQDN destinations. The site connector resolves and delivers traffic on the remote LAN. For private HTTP, TLS can terminate at the connector so applications stay off the public internet entirely. How IP, CIDR, FQDN, and alias destinations are resolved and routed. ## Component Reference Pangolin's open-source repositories map to the roles above. Codenames are used throughout the codebase and deployment tooling. | Component | Repository | Role | | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | | Pangolin | [fosrl/pangolin](https://github.com/fosrl/pangolin) | Control plane server | | Newt | [fosrl/newt](https://github.com/fosrl/newt) | Site connector | | Gerbil | [fosrl/gerbil](https://github.com/fosrl/gerbil) | Node tunnel manager and relay | | Badger | [fosrl/badger](https://github.com/fosrl/badger) | Traefik forward-auth middleware | | Olm | [fosrl/olm](https://github.com/fosrl/olm) | Shared client networking stack (tunnels, NAT traversal, DNS) | | Clients | [fosrl/cli](https://github.com/fosrl/cli), [fosrl/windows](https://github.com/fosrl/windows), [fosrl/apple](https://github.com/fosrl/apple), [fosrl/android](https://github.com/fosrl/android) | Endpoint agents | For local development setup and how these repositories interact in a dev environment, see [Contributing](/development/contributing). # Device Approvals Source: https://docs.pangolin.net/manage/access-control/approvals Only allow trusted devices to connect to an organization
Only available in [Pangolin Cloud](https://app.pangolin.net/auth/signup) and [Enterprise Edition](/self-host/enterprise-edition). By default, any client configured with valid credentials can connect to an organization. To enhance security, you can enable device approvals, which require each new device to be manually approved by an administrator before it can connect. When device approvals are enabled, the first time a user connects a new device to the organization, the device will be marked as "Pending Approval." An administrator must then review and approve the device in the management console before it can access organization resources. Device marked pending approval in the Pangolin dashboard All approvals can also be managed from a central page as they stream in to allow admins to approve or deny devices quickly. Approvals page listing pending devices in the Pangolin dashboard ## Enabling Device Approvals Device approvals are enabled on a per-role basis. To enable device approvals for a role, follow these steps: 1. Click on the **Roles** tab. 2. Select the role you want to enable device approvals for. 3. Toggle the **Require Device Approval** option to enable it. 4. Save your changes. Once enabled, any new user connecting with that role will require approval from an administrator before it can access organization resources. You cannot enable device approvals for the "Admin" role. # Change Password Source: https://docs.pangolin.net/manage/access-control/change-password Change or reset your Pangolin account password
### Change Password If you're already logged in, you can change your password by clicking your profile menu (top right) and selecting Change Password. You will be required to confirm your old password and enter a new password. If you want to require password changes at regular intervals for better security, check out the [password rotation documentation](/manage/access-control/password-rotation). ### Reset Password If you forgot your password, you can use the reset password function. On the login page, select Forgot your password?. This will ask for your username or email. A reset code will be sent to that email to complete the reset. If you're self‑hosting Pangolin, you will need an SMTP server configured to send emails. If you don't have one configured, the server will log the reset code to the server logs for you to retrieve and use to reset the password. ### Force Reset Server Admin Password For self‑hosted Pangolin, if you need to force reset your server admin account password server‑side, you can use the internal CLI. [See more here](/self-host/advanced/container-cli-tool). # Users and Roles Source: https://docs.pangolin.net/manage/access-control/create-user Add internal or external users to your organization and manage roles
## Users in Organizations Users can be added to organizations. When a user is added to Pangolin, there is a global user object and an organization‑specific user object that links that user to the organization. This allows a user to exist in one or more organizations. Because the global user exists and a per‑organization user exists, a user invited to an organization may be able to create a new organization. You can disable this functionality via a flag in the config file in self‑hosted Pangolin. [Check out the config file documentation](/self-host/advanced/config-file#feature-flags). When removing a user from an organization, their account still exists. To completely delete their account, visit the server admin panel as the server admin and delete the global user in the users table. Users table in the Pangolin dashboard ### Internal Users An internal user is an identity managed by Pangolin only. When adding the user, you will receive an invite link. The user needs to use this link to either accept the invite, or create an account for the first time and accept the invite. ### External Users An external user is an identity managed by an external identity provider. When creating an external user, you will need to select an existing identity provider added to Pangolin. [Check out the documentation on adding an IDP](/manage/identity-providers/add-an-idp). An identity provider may have auto‑provisioning enabled. This means new users who log in with the IDP are automatically created and you do not need to manually create the user. [Check out the auto‑provisioning documentation](/manage/identity-providers/auto-provisioning). Even if auto‑provisioning is enabled, you can still manually create users. ## Roles Roles are how you group users in an organization. A user can belong to more than one role, for example Member, Admin, Contractor, Operations, or any custom roles you define. You use roles with RBAC on resources so access follows those groups: only Operations might reach production resources, while only Contractors might reach test environments, and so on. On each resource, you define which roles are allowed to access it. A user’s effective access is the union of all resources their roles can reach: they can use any resource that at least one of their assigned roles is permitted to access. You can create as many custom roles as you need in Pangolin. Each role has a name and a description. The name is the display label and also acts as the unique identifier, so two roles cannot share the exact same name. To change which roles a user has, open that user’s settings and select the roles they should belong to. Assigning more than one role to a user is only available in [Pangolin Cloud](https://app.pangolin.net/auth/signup) or self-hosted [Enterprise Edition](/self-host/enterprise-edition). In other editions, only one role per user is supported. # Forwarded Headers Source: https://docs.pangolin.net/manage/access-control/forwarded-headers Learn how Pangolin forwards user identity information to your backend applications through HTTP headers
Pangolin can forward user identity information to your backend applications through custom HTTP headers. This allows your applications to receive user details directly from the request headers, enabling integration with Pangolin's authentication system. Forwarded headers are only available when using authentication methods that provide user identity information. ## Supported Headers Pangolin forwards the following headers to your backend when user identity is available: | Header | Description | Example | | -------------- | ------------------------------- | ---------------------- | | `Remote-User` | Unique username or user ID | `user_123` | | `Remote-Email` | User's email address | `john.doe@example.com` | | `Remote-Name` | User's full name | `John Doe` | | `Remote-Role` | User's role or group membership | `admin` | ## Authentication Methods ### Headers Available These authentication methods provide user identity information and will include the forwarded headers: Full user identity information including username, email, and name. ### Headers Not Available These authentication methods do not provide user identity information: No user identity - only access control. No user identity - only access control. No user identity - only access control. # Shareable Links Source: https://docs.pangolin.net/manage/access-control/links Create Links and use access tokens for browser or programmatic access.
Links are special URLs that grant access to one resource without requiring the recipient to sign in as a Pangolin user. Anyone with a web browser on the internet can access the resource if they have a valid Link. When you create a Link, Pangolin gives you two ways to use it: * **Link**: This is a Pangolin-hosted URL that validates the validity of the Link and then redirects them to the resource. * **Access Token Usage**: Use this only when making direct requests to the resource URL from scripts, tools, or integrations. ## Create a Link From the resource authentication flow, create a Link by: 1. Choosing the target resource. 2. Adding a title if you want the link to be easy to identify later. 3. Setting an expiration, or enabling **Never expire** if the link should stay valid until you revoke it. 4. Copying the generated link or access-token details immediately after creation. Create a Link modal Anyone with the Link or access token can use it. Treat both like credentials. ## Use the Access Token Pangolin can accept a Link access token in either the query string or request headers. If you are sending access to a person, use the copied **Link** shown at the top of the modal. Use **Access Token Usage** only when you are calling the resource URL directly on each request. This is why the two URLs often look different: * The **Link** is usually on your Pangolin domain. * The **Access Token Usage** examples use the resource URL directly. Access token usage examples for a shareable link ### Query Parameter Pangolin accepts the access token in the `p_token` query parameter: ```bash theme={"theme":"gruvbox-light-hard"} curl "https://resource.example.com/?p_token=." ``` The query-string value is the token ID and token joined with a `.`. Some deployments may use a different query parameter name. The query parameter must be sent in every request to the resource, not just the first time. ### Request Headers By default, Pangolin accepts these headers: * `P-Access-Token-Id` * `P-Access-Token` Example: ```bash theme={"theme":"gruvbox-light-hard"} curl \ -H "P-Access-Token-Id: " \ -H "P-Access-Token: " \ "https://resource.example.com/" ``` This is the same token data as the query-string form, split into two headers instead of `.`. Some deployments may use different header names. The headers must be sent in every request to the resource, not just the first time. ## Expiration and Revocation * Expiring links stop working automatically when their lifetime ends. * Non-expiring links remain valid until you delete them. * Deleting the Link revokes both the Link and its access token. ## Important Notes * Links are best for targeted sharing and automation, not broad long-term access. * Link-based access does not carry per-user identity headers to the upstream app. For identity-aware upstream integrations, see [Forwarded Headers](/manage/access-control/forwarded-headers). # Custom Login Page Source: https://docs.pangolin.net/manage/access-control/login-page Configure a custom authentication page URL for your organization
Custom auth pages are only available in [Pangolin Cloud](https://app.pangolin.net/auth/signup). Custom organization authentication pages let you serve the login page at your own domain instead of the default `app.pangolin.net`. This provides better user experience and brand consistency. ## Benefits **For Resource Authentication:** * Users are redirected to your custom domain for login * Familiar domain builds trust and security awareness * Consistent branding throughout the authentication flow **For Identity Provider Integration:** * Centralized login page for your organization * Choose between multiple login methods (Google, Azure, etc.) * Platform SSO: login once, access all Pangolin resources * Direct access to the Pangolin management dashboard Organization login page with multiple login methods ## Configuration 1. Go to **Settings** in your organization sidebar 2. Use the domain picker to select your custom domain 3. Save your changes You need to add a custom domain to your organization first. Free domains (`*.tunneled.to`, `*.hostlocal.app`, etc.) cannot be used for auth pages. [Learn how to add domains](/manage/domains) Domain picker for the auth page in Pangolin settings # Multi-Factor Authentication Source: https://docs.pangolin.net/manage/access-control/mfa Enable and manage two-factor authentication and enforcement for your organization
Pangolin supports two‑factor authentication (2FA) for Pangolin user accounts. ### Enable or Disable 2FA * Click your profile menu (top right) to enable two‑factor authentication. * You will need to confirm your password and code before enabling/disabling 2FA. ### Supported Methods * **Time‑based one‑time code (TOTP)**: Use an authenticator app (e.g., 1Password, Google Authenticator). * **Push via email**: Contact sales to enable. * **Push via Duo**: Contact sales to enable. ### Enforcement Two‑factor enforcement (requiring 2FA at login) is available in [Enterprise Edition](/self-host/enterprise-edition) only. To enable enforcement, go to Organization Settings and toggle 2FA enforcement in the Security section. * Enforcement is configured per organization. * MFA enforcement only applies to internal Pangolin user accounts. This policy does not apply to accounts linked to an external identity provider. * When enforced, users must enable 2FA before accessing the organization or its resources. * Users without 2FA will see a prompt directing them to enable it before proceeding. # Password Rotation Source: https://docs.pangolin.net/manage/access-control/password-rotation Configure password expiration and rotation requirements for your organization
By default, Pangolin does not require passwords to be rotated on a regular basis. However, password rotation can be required on a per‑organization basis. ### Configuration Password expiry and rotation is an [Enterprise Edition](/self-host/enterprise-edition)-only feature. To enable password rotation, go to Organization Settings and select a maximum password age in the Security section. After the configured period expires, users will be prompted to change their password when accessing the organization or its resources. * Password rotation is enforced on a per‑organization basis. * Password rotation only applies to internal Pangolin user accounts. This policy does not apply to accounts linked to an external identity provider. * Users who need to change their password will see a prompt directing them to update it before proceeding. # Rules Source: https://docs.pangolin.net/manage/access-control/rules Configure rules to allow or deny access to resources without authentication
Rules allow you to either "allow" and bypass the Pangolin auth system (no pin, login, password), or "deny" and fully reject the request. After you create a resource you can select the "Rules" tab on the sidebar and enable rules. On public resources, you can also define rules in a [resource policy](/manage/resources/public/resource-policies) and share them across multiple resources. Bypass authentication completely for matching requests. Users can access resources without any login or PIN. Completely reject requests that match the rule. Useful for blocking admin paths or sensitive endpoints. Pass requests that match the rule to the next stage for user to authenticate with SSO, password, or pin. Useful for enforcing auth on specific paths while allowing others. ## Types of Rules Rules are processed from top to bottom in order of their priority. This means you can have multiple rules to bypass auth and to just flat deny users at the end. Right now you can match on the following items: ### Path Path match rules allow URL patterns defined with plain text and wildcards (`*`) that match any characters. Patterns and URLs are split into segments (using `/`), and **each segment is matched individually**. #### Examples: * `blog/posts` Matches the exact path `/blog/posts`. * `blog/*` Matches any path under `/blog` (e.g., `/blog/travel`). * `*/2023/*` Matches paths with `/2023/` as a middle segment (e.g., `/news/2023/summary`). * `article*` Matches **segments** starting with "article" (e.g., `/article-123`). * `*admin*` Matches **segments** containing "admin" (e.g., `/my-admin-panel`). * `personal-*/*` Matches paths where the first segment starts with `personal-` and is followed by any segment (e.g., `/personal-blog/post`). #### Segment-by-Segment Matching * **Normalization:** Both patterns and URLs are split into segments. For example, `/blog/journal/entry` becomes `["blog", "journal", "entry"]`, while `/blog*` becomes `["blog*"]`. * **Validation:** Each pattern segment must correspond to a URL segment, and wildcards match zero or more characters within that segment. A pattern like `/blog*` only matches the first segment, so URLs with extra segments require additional placeholders (e.g., `/blog*/*`). ### Country Country match rules allow you to specify allowed or denied countries for requests based on their IP address. This is useful for geo-restrictions or compliance with regional regulations. We use a IP database to geolocate the IP address but this is not always accurate. Try to keep it updated, but there may be cases where the location is incorrect. Select the "ALL" option to match all countries for allowing or denying access. To use country rules, follow this guide to set up the geolocation database: [Enable Geo-location](/self-host/advanced/enable-geolocation). ### Region Region match rules allow you to specify allowed or denied regions for requests based on their IP address. This is useful for geo-restrictions or compliance with regional regulations. Regions are made up of a list of countries in that region (e.g. "EU" includes France, Germany, etc.) so this is a more broad match than country. To use region rules, follow this guide to set up the geolocation database: [Enable Geo-location](/self-host/advanced/enable-geolocation). ### CIDR CIDR (Classless Inter-Domain Routing) notation specifies IP address ranges using an IP address and a network prefix length. The format is \[IP address]/\[prefix length]. **Examples:** * `192.168.1.0/0` - Matches all 256 IPs from 192.168.1.0 to 192.168.1.255 * `10.0.0.0/8` - Matches any IP starting with 10 (16.7 million addresses) * `2001:db8::/32` - Matches a range of IPv6 addresses * `0.0.0.0/0` - Matches all IPv4 addresses The prefix length (1-32 for IPv4, 1-128 for IPv6) determines how many bits from the left are fixed. Smaller prefix numbers match larger ranges. ### IP Pretty simple: you can match on simply an IP address like your home IP to bypass auth. This is the same as entering a /32 CIDR. ### ASN ASN (Autonomous System Number) match rules allow you to specify allowed or denied ASNs for requests based on their IP address. This is useful for blocking or allowing traffic from specific ISPs or organizations. To use ASN rules, follow this guide to set up the ASN lookup database: [Enable ASN Lookup](/self-host/advanced/enable-asn-lookup). **Examples:** * `23.234.134.32` * `34.45.245.64` * `192.168.1.1` ### Community Contributed Rules Some common bypass paths for common self hosted apps can be found [in the community contributed rules](/self-host/community-guides/rules). # Security Keys Source: https://docs.pangolin.net/manage/access-control/security-keys Use security keys for passwordless login to your Pangolin account
You can log in with security keys, also known as passwordless login. On the login page, there is an option below the login button to Log in with security key. ### Add a Security Key To add a security key, you must first be logged in. Then click your profile menu (top right) and select Add Security Keys. Follow the steps to add your key. Once a security key is added to your account, you can select the Continue with security key option the next time you log in. # Session Length Source: https://docs.pangolin.net/manage/access-control/session-length Configure maximum session length and expiration policies for your organization
By default, Pangolin keeps extending a session indefinitely if a user is actively using it. If a user is not actively using the session, it will expire after 30 days. However, you can require users to log in at regular intervals by enforcing maximum session lengths on a per‑organization basis. ### Configuration Session length enforcement is an [Enterprise Edition](/self-host/enterprise-edition)-only feature. To enable session length enforcement, go to Organization Settings and set a maximum session length in the Security section. After this amount of time, users will be prompted to log back in to acquire a fresh session. * Session length enforcement is configured per organization. * Session length enforcement applies to both internal Pangolin users and users linked to external identity providers. * Users whose session has expired will see a prompt directing them to log in again before proceeding. # Alert Rules Source: https://docs.pangolin.net/manage/alerting/alert-rules Subscribe to Pangolin events on sites, resources, and health checks and deliver email, webhooks, or integrations
Only available in [Pangolin Cloud](https://app.pangolin.net/auth/signup) and [Enterprise Edition](/self-host/enterprise-edition). Alert rules let you react to changes in Pangolin: you pick a source (what to watch), a trigger (which change matters), and one or more actions (what to do). For example, when a site moves from online to offline, email your admins and support; when a [health check](/manage/alerting/health-checks) or resource becomes unhealthy, call a webhook so a tool like Zapier can fan out the event. ## Where to create rules Create and manage rules from the Alert rules page under Alerting for your organization. You can also start a rule from a site or resource detail page: use Create alert rule near the uptime graph to prefill context and keep the flow short. ## Actions When a trigger fires, Pangolin can: * Send email (recipients described below). * Call a webhook with a JSON payload (see [Webhook payloads](#webhook-payloads)). * Open an incident or ticket in PagerDuty, Opsgenie, ServiceNow, or incident.io. You can attach several actions to the same rule (for example email plus a webhook). ### Email Choose users in your Pangolin organization, entire roles, and/or arbitrary email addresses that should receive the message when the condition is met. ### Webhooks Webhook actions issue an HTTP request to your endpoint when the trigger runs. Payloads are JSON and follow the shapes in [Webhook payloads](#webhook-payloads). ## Creating an alert rule Create alert rule wizard in the Pangolin dashboard ### 1. Source Choose what entity the rule watches: | Source type | Meaning | | ------------ | ----------------------------------------------------------- | | Site | One or more [sites](/manage/sites/understanding-sites) | | Resource | One or more resources in the org | | Health check | One or more [health checks](/manage/alerting/health-checks) | For each type, decide whether the rule applies to all of that kind (for example all sites) or only specific sites, resources, or health checks you select. ### 2. Trigger Available triggers depend on the source type. For sites, options include coming online, going offline, or any status change. For resources and health checks, you get healthy, unhealthy, and combined toggle-style triggers that match how those entities change state—the dashboard only lists combinations that apply to what you selected. Pick the condition that should fire the rule (for example site status changes when you care about both online and offline transitions). ### 3. Actions Configure what happens when the trigger runs: add one or more actions (email, webhook, or a vendor integration). Use Add action to stack multiple destinations for the same rule. ## Webhook payloads Webhook bodies are JSON. Every event includes `event`, ISO-8601 `timestamp`, and a `data` object. The event name tells you what changed; `data` always includes `orgId` and entity-specific fields. `{{data}}` is an object and must be treated as such. If you can not support a object, you can also use the flattened fields shown in the examples below. For example `{{orgId}}`, `{{siteId}}`, and `{{siteName}}` are all available on a site alert. ### Site events #### `site_online` A site came back online. ```json theme={"theme":"gruvbox-light-hard"} { "event": "site_online", "timestamp": "2025-06-15T12:34:56.789Z", "data": { "orgId": "org_abc123", "siteId": 42, "siteName": "us-east-prod" } } ``` #### `site_offline` A site went offline. ```json theme={"theme":"gruvbox-light-hard"} { "event": "site_offline", "timestamp": "2025-06-15T12:34:56.789Z", "data": { "orgId": "org_abc123", "siteId": 42, "siteName": "us-east-prod" } } ``` #### `site_toggle` Fires when site connectivity changes, alongside both `site_online` and `site_offline`. Use this when you only care that status flipped, not which direction. `siteId` is always present in `data`. ```json theme={"theme":"gruvbox-light-hard"} { "event": "site_toggle", "timestamp": "2025-06-15T12:34:56.789Z", "data": { "orgId": "org_abc123", "siteId": 42, "siteName": "us-east-prod" } } ``` ### Health check events #### `health_check_healthy` A health check recovered. ```json theme={"theme":"gruvbox-light-hard"} { "event": "health_check_healthy", "timestamp": "2025-06-15T12:34:56.789Z", "data": { "orgId": "org_abc123", "healthCheckName": "API /healthz" } } ``` #### `health_check_unhealthy` A health check is failing. ```json theme={"theme":"gruvbox-light-hard"} { "event": "health_check_unhealthy", "timestamp": "2025-06-15T12:34:56.789Z", "data": { "orgId": "org_abc123", "healthCheckName": "API /healthz" } } ``` #### `health_check_toggle` Fires alongside healthy and unhealthy transitions. `healthCheckId` is included in `data` for this combined event. ```json theme={"theme":"gruvbox-light-hard"} { "event": "health_check_toggle", "timestamp": "2025-06-15T12:34:56.789Z", "data": { "orgId": "org_abc123", "healthCheckId": 7, "healthCheckName": "API /healthz" } } ``` ### Resource events #### `resource_healthy` A resource recovered. ```json theme={"theme":"gruvbox-light-hard"} { "event": "resource_healthy", "timestamp": "2025-06-15T12:34:56.789Z", "data": { "orgId": "org_abc123", "resourceName": "internal-dashboard" } } ``` #### `resource_unhealthy` A resource is unhealthy. ```json theme={"theme":"gruvbox-light-hard"} { "event": "resource_unhealthy", "timestamp": "2025-06-15T12:34:56.789Z", "data": { "orgId": "org_abc123", "resourceName": "internal-dashboard" } } ``` #### `resource_toggle` Fires alongside healthy and unhealthy transitions, or when a resource is enabled or disabled. `resourceId` is included in `data`. ```json theme={"theme":"gruvbox-light-hard"} { "event": "resource_toggle", "timestamp": "2025-06-15T12:34:56.789Z", "data": { "orgId": "org_abc123", "resourceId": 15, "resourceName": "internal-dashboard" } } ``` # Health Checks Source: https://docs.pangolin.net/manage/alerting/health-checks Monitor reachability and response for public resource targets and arbitrary endpoints from your sites
A health check is a periodic probe that decides whether something on your network is up and responding the way you expect. Pangolin runs these checks from your sites so they reflect reachability from the connector’s perspective, not only from wherever an administrator happens to be. ## Health Checks on Public Resource Targets You can attach health checks to individual targets on public resources. When a target fails its health check, Pangolin treats it as unhealthy: it is removed from rotation and load balancing until it passes again, so traffic is not sent to a broken upstream. That behavior is configured per target alongside your proxy settings. For step-by-step setup, states (healthy / unhealthy / unknown), and routing implications, see [Health checks & failover](/manage/resources/public/healthchecks-failover). ## Health Checks in Alerting Under Alerting → Health checks for your organization, you get a single view of health checks tied to public resource targets, so you can see status across resources without opening each resource separately. Create health check form in the Pangolin dashboard ### Arbitrary Health Checks Arbitrary health checks are only available in [Pangolin Cloud](https://app.pangolin.net/auth/signup) and [Enterprise Edition](/self-host/enterprise-edition). Health checks attached to public resource targets are available in all editions. In addition to target-linked checks, you can create standalone health checks that are not attached to any routable resource target. They work the same way at the probe layer, with the same protocols and timing concepts, but only represent reachability for an address your sites can reach (for example an IP or hostname on a remote LAN). You choose which site runs the check so it stays within an addressable range for that connector. These are useful when you care about whether a system is up, even if it is not modeled as a Pangolin resource like a network printer, an IP camera, a legacy server, or anything else that should be watched from the site’s network. Pair them with [Alert rules](/manage/alerting/alert-rules) to send notifications when something goes unhealthy or recovers. ## Check Types There are two kinds of checks: HTTP and TCP. ### HTTP An HTTP health check issues an HTTP or HTTPS request to a URL you specify. You can tune the scheme (`http` or `https`), HTTP method (for example `GET` or `POST`), path, headers, expected status codes, and anything else needed to match how the service exposes a liveness endpoint. Success means the response satisfies your criteria (including status code and optional body rules, depending on configuration). ### TCP A TCP health check does not speak application data: it tries to open a TCP connection to a host and port. If the TCP handshake completes, the check is treated as passing; if nothing answers or the connection is refused or times out, it fails. That is ideal for services that only expose a plain port (databases, cameras, PLCs) or when you only care that the host is reachable on a given port. ## Timing and Thresholds Both HTTP and TCP checks support configuration for how often probes run when things are healthy versus when they are failing, how many successes or failures are required before flipping state, and related tuning (for example healthy interval, unhealthy interval, healthy threshold, unhealthy threshold). Exact field names appear in the dashboard; the intent is to avoid flapping—brief blips should not instantly mark a host down, and recovery should be confirmed before treating it as fully healthy again. ## How the pieces fit together * Target-linked health checks on public resources drive routing: unhealthy targets drop out of the pool until they recover. * Arbitrary checks track reachability for addresses your sites can reach—dashboard visibility and [Alert rules](/manage/alerting/alert-rules)—even when there is no Pangolin resource for that system. # Authentication Logs Source: https://docs.pangolin.net/manage/analytics/access Authentication logs are a record of each authenticated access attempt to a resource
Authentication logs provide detailed information about each access attempt made to your Pangolin resources. These logs help you monitor and analyze user activity each time they attempt to authenticate. Authentication logs are only available in [Pangolin Cloud](https://app.pangolin.net/auth/signup) or self-hosted [Enterprise Edition](/self-host/enterprise-edition). ## What are Authentication Logs? Authentication logs capture authentication events when users or API keys attempt to access a resource. They record whether the authentication was successful or failed, along with contextual information about the attempt. These logs are useful for: * Monitoring authentication patterns and login attempts * Tracking which users are accessing which resources * Identifying failed authentication attempts for security analysis * Understanding geographic distribution of access attempts * Analyzing user agent and device information Authentication logs table in the Pangolin dashboard Make sure to enable authentication logs in the org settings ## Authentication Log Fields Each authentication log entry contains the following fields: | Field | Type | Description | | ------------ | ------- | ----------------------------------------------------------------------- | | `timestamp` | number | Unix timestamp (in seconds) when the access attempt occurred | | `action` | boolean | Whether the access was allowed (`true`) or denied (`false`) | | `type` | string | The type of authentication event (e.g., "login", "password", "pincode") | | `actorType` | string | The type of actor making the access attempt ("user" or "apiKey") | | `actor` | string | The display name of the actor (username or API key name) | | `actorId` | string | The unique identifier for the actor (user ID or API key ID) | | `resourceId` | number | The ID of the resource being accessed (if applicable) | | `ip` | string | The IP address of the client making the access attempt | | `location` | string | The geographic location (country code) based on IP address | | `userAgent` | string | The user agent string of the client browser or application | | `metadata` | string | Additional contextual information in JSON format | ## Log Retention Authentication log retention is controlled by the organization setting. By default, authentication logs are retained for 0 days (disabled). ## Exporting Logs can be exported into CSV format for external analysis and archival. Logs can be exported from the table view in the Pangolin dashboard or via the Pangolin API. When exporting, you can specify date ranges and filters to narrow down the logs you need. # Admin Action Logs Source: https://docs.pangolin.net/manage/analytics/action Admin Action logs are a record of each event taken by users in the organization
Admin Action logs provide an audit trail of administrative actions and configuration changes made within your Pangolin organization. These logs help you track who made what changes and when. Admin Action logs are only available in [Pangolin Cloud](https://app.pangolin.net/auth/signup) or self-hosted [Enterprise Edition](/self-host/enterprise-edition). ## What are Admin Action Logs? Admin Action logs capture administrative events and configuration changes performed by users or API keys in the Pangolin dashboard. They record management operations such as creating resources, modifying settings, managing users, and other organizational changes. These logs are useful for: * Maintaining an audit trail of configuration changes * Tracking administrative actions for compliance * Identifying who made specific changes to your infrastructure * Troubleshooting configuration issues by reviewing recent changes * Meeting security and compliance requirements Admin action logs table in the Pangolin dashboard Make sure to enable access logs in the org settings ## Admin Action Log Fields Each action log entry contains the following fields: | Field | Type | Description | | ----------- | ------ | ----------------------------------------------------------------------------------------------------- | | `timestamp` | number | Unix timestamp (in seconds) when the action was performed | | `action` | string | The specific action that was performed (e.g., "createResource", "updateUser", "deleteTarget") | | `actorType` | string | The type of actor performing the action ("user" or "apiKey") | | `actor` | string | The display name of the actor (username or API key name) | | `actorId` | string | The unique identifier for the actor (user ID or API key ID) | | `metadata` | string | Additional contextual information about the action in JSON format (often contains request parameters) | ## Log Retention Admin Action log retention is controlled by the organization settings. By default, admin action logs are retained for 0 days (disabled). ## Exporting Logs can be exported into CSV format for external analysis and archival. Logs can be exported from the table view in the Pangolin dashboard or via the Pangolin API. When exporting, you can specify date ranges and filters to narrow down the logs you need. # Network Logs Source: https://docs.pangolin.net/manage/analytics/connection Network logs are a record of TCP and UDP sessions between clients and private resources on sites
Network logs record each TCP and UDP session that traverses the tunnel between Pangolin clients and resources on your sites. They apply to private resources reached through the Pangolin client (and related tunnel traffic), not to public resources served only through the reverse proxy. You can see which clients and users opened sessions to which private resources, the source and destination addresses and protocols (TCP and UDP), the start and end times of the sessions, and more. Network logs are only available in [Pangolin Cloud](https://app.pangolin.net/auth/signup) or self-hosted [Enterprise Edition](/self-host/enterprise-edition). ## What are Network Logs? Network logs capture tunnel sessions from clients to private resources. They are useful for: * Observing which clients and users opened sessions to which private resources * Reviewing source and destination addresses and protocols (TCP and UDP) * Measuring traffic volume with transmitted and received byte counts * Auditing session start and end times for troubleshooting and compliance Network logs are synchronized to the cloud every 30–60 seconds. A brief delay before entries appear in the table is expected. Make sure to enable network logging in the org settings ## Network Log Fields Each network log entry contains the following fields: | Field | Type | Description | | ---------------- | -------------- | ----------------------------------------------------------------------------------------------------------- | | `timestamp` | number | Unix timestamp (in seconds) when the session started | | `protocol` | string | Transport protocol for the session (`tcp` or `udp`) | | `siteResourceId` | number \| null | The ID of the [private resource](/manage/resources/understanding-resources) for the session (if applicable) | | `clientId` | number \| null | The Pangolin [client ID](/manage/clients/understanding-clients) for the session | | `clientEndpoint` | string \| null | The client-side endpoint for the session (e.g. `123.123.123.123:12345`) | | `userId` | string \| null | The user ID when the session is tied to an authenticated user | | `sourceAddr` | string | Source address for the session (typically the client-side endpoint) | | `destAddr` | string | Destination address for the session (typically the resource-side endpoint) | | `duration` | number \| null | How long the session lasted (in seconds), when the session has ended | | `bytesTx` | number \| null | Bytes transmitted in the session | | `bytesRx` | number \| null | Bytes received in the session | ## Log Retention Network log retention is controlled by the organization setting. By default, network logs are retained for 0 days (disabled). Network logs can generate significant data volume depending on session churn and traffic. Consider your storage capacity when configuring retention periods. ## Exporting Logs can be exported into CSV format for external analysis and archival. Logs can be exported from the table view in the Pangolin dashboard or via the Pangolin API. When exporting, you can specify date ranges and filters to narrow down the logs you need. # HTTPS Request Logs Source: https://docs.pangolin.net/manage/analytics/request Request logs are a record of each HTTP request to a resource
HTTPS Request logs provide detailed information about every HTTP request made to your Pangolin resources. These logs capture both successful and denied requests along with comprehensive request metadata. ## What are HTTPS Request Logs? HTTPS Request logs capture every HTTPS request that passes through a reverse proxy, including the request details, the decision made (allow or deny), and the reason for that decision. These logs are useful for: * Monitoring traffic patterns and request volumes * Debugging access issues and rule configurations * Analyzing API usage and endpoint popularity * Understanding geographic distribution of requests * Identifying potential security threats or unusual traffic patterns * Troubleshooting connectivity and routing issues HTTPS request logs table in the Pangolin dashboard ## HTTPS Request Log Fields Each HTTPS request log entry contains the following fields: | Field | Type | Description | | -------------------- | ------- | ------------------------------------------------------------------------------ | | `timestamp` | number | Unix timestamp (in seconds) when the request was made | | `action` | boolean | Whether the request was allowed (`true`) or denied (`false`) | | `reason` | number | Numeric code indicating the reason for the decision | | `actorType` | string | The type of actor making the request ("user", "apiKey", or null for anonymous) | | `actor` | string | The display name of the authenticated actor (username or API key name) | | `actorId` | string | The unique identifier for the authenticated actor | | `resourceId` | number | The ID of the resource that received the request | | `ip` | string | The IP address of the client making the request | | `location` | string | The geographic location (country code) based on IP address | | `userAgent` | string | The user agent string of the client browser or application | | `metadata` | string | Additional contextual information in JSON format | | `headers` | string | HTTP request headers in JSON format | | `query` | string | URL query parameters in JSON format | | `originalRequestURL` | string | The full original URL of the request | | `scheme` | string | The protocol scheme (http or https) | | `host` | string | The hostname from the request | | `path` | string | The URL path of the request | | `method` | string | The HTTP method (GET, POST, PUT, DELETE, etc.) | | `tls` | boolean | Whether the connection used TLS/SSL | ## Log Retention HTTPS Request log retention is controlled by the organization setting. By default, HTTPS request logs are retained for 7 days. HTTPS Request logs can generate significant data volume depending on your traffic. Consider your storage capacity when configuring retention periods. ## Exporting Logs can be exported into CSV format for external analysis and archival. Logs can be exported from the table view in the Pangolin dashboard or via the Pangolin API. When exporting, you can specify date ranges and filters to narrow down the logs you need. # Event Streaming Source: https://docs.pangolin.net/manage/analytics/streaming Stream Pangolin log events to external collectors and SIEM tools
Log streaming forwards your organization's audit logs to external data collectors such as Datadog, Splunk, Microsoft Sentinel, Elastic, or any HTTP endpoint you operate. You add a **destination** (how events are delivered), choose which **log types** to include, and Pangolin pushes new events as they are recorded. Event streaming is only available in [Pangolin Cloud](https://app.pangolin.net/auth/signup) or self-hosted [Enterprise Edition](/self-host/enterprise-edition). ## In the dashboard Open **Organization → Logs & Analytics → Streaming** to add destinations and monitor delivery status. Each destination has its own connection settings, optional body customization (where supported), and log-type selection. ## Log types You choose which categories each destination receives. Only log types enabled for your organization can be streamed. Log type selection for a streaming destination ## Destination types Each destination type has its own configuration and payload behavior. Select **Add destination** and pick a delivery method. Add destination dialog in the Pangolin dashboard POST JSON or NDJSON to any URL. Supports custom body templates, authentication, and payload formats for SIEMs and generic webhooks. Upload batched audit logs to S3 or S3-compatible storage. JSON array, NDJSON, or CSV with optional gzip. ### Other destinations Amazon S3 and HTTP webhooks are documented above. For Datadog, Microsoft Sentinel, or other vendor-specific setups, contact [sales@pangolin.net](mailto:sales@pangolin.net). * **No backfill:** New destinations start from the current log cursor. Historical logs already in Pangolin are not replayed. * **Per-log-type cursors:** Each enabled log type on a destination is tracked independently. * **Errors in the UI:** When delivery fails, the destination's last error is shown in the dashboard so you can fix configuration or endpoint issues. # HTTP webhook Source: https://docs.pangolin.net/manage/analytics/streaming/http Forward audit logs to any HTTP endpoint with optional custom body templates
HTTP destinations POST your organization’s audit logs to a URL you control. Use them for generic webhooks, Splunk HEC, Elastic or OpenSearch ingest, Grafana Loki push endpoints, or any receiver that accepts JSON over HTTP. Event streaming is only available in [Pangolin Cloud](https://app.pangolin.net/auth/signup) or self-hosted [Enterprise Edition](/self-host/enterprise-edition). ## Overview An HTTP destination sends **POST** requests to your endpoint. Configure: 1. **Settings:** Name, URL, and authentication. 2. **Headers:** Optional static headers on every request. 3. **Body:** Default JSON shape or a custom body template, plus payload format (how batches are packaged). 4. **Logs:** Which log types are forwarded. Enable **Custom body template** when your receiver expects a different JSON layout than Pangolin’s default. Leave it off to send the standard `{ event, timestamp, data }` object per log record. ## Configure the connection On the **Settings** tab, set a display name, the endpoint URL, and authentication: | Auth type | Behavior | | ------------- | -------------------------------------------------------------- | | None | No `Authorization` header | | Bearer token | `Authorization: Bearer ` | | Basic auth | `Authorization: Basic ` | | Custom header | A single header name and value (for example an API key header) | HTTP destination settings with URL and authentication options All delivery uses **POST**. Requests time out after 30 seconds. ## Authentication and headers On the **Headers** tab, add optional static headers sent with every request, for example a vendor-specific API key or a non-default `Content-Type`. When you do not override it, Pangolin sends `Content-Type: application/json` (or `application/x-ndjson` when using the NDJSON payload format). Headers tab for adding static HTTP headers ## Default payload (template off) When custom body template is disabled, each log event is serialized as: ```json theme={"theme":"gruvbox-light-hard"} { "event": "request", "timestamp": "2025-06-15T12:34:56.789Z", "data": { "timestamp": 1718454896, "action": true, "method": "GET", "path": "/api/health" } } ``` | Field | Meaning | | ----------- | --------------------------------------------------------------------- | | `event` | Log type: `request`, `access`, `action`, or `connection` | | `timestamp` | Event time as ISO-8601 UTC | | `data` | The **complete stored log row** for that record, not a curated subset | The field set inside `data` depends on the log type. The same destination can stream multiple types; batches may contain heterogeneous `data` shapes. See [Log type reference](#log-type-reference) below and the dedicated log docs for full field lists. Some columns are stored as JSON strings in the database (`headers`, `query`, and `metadata` on request logs, for example). In `data`, they appear as **string values**, not nested JSON objects. Parse them on the receiver if you need structured fields. ## Custom body template On the **Body** tab, enable **Custom body template** and provide a JSON template string. Pangolin performs simple placeholder substitution, **not** a full templating language like Handlebars. Body tab with custom body template editor ### Template variables Only these three placeholders are supported: | Variable | Source | How to use in the template | | --------------- | ------------------------------------------------------ | --------------------------------------------- | | `{{event}}` | Log type (`request`, `access`, `action`, `connection`) | Inside JSON **string quotes** | | `{{timestamp}}` | Event time (ISO-8601 UTC) | Inside JSON **string quotes** | | `{{data}}` | Full log row as JSON | **Never wrap in quotes**; inlined as raw JSON | **Canonical example** (equivalent to the default payload): ```json theme={"theme":"gruvbox-light-hard"} { "event": "{{event}}", "timestamp": "{{timestamp}}", "data": {{data}} } ``` **Remapping property names** for a downstream schema: ```json theme={"theme":"gruvbox-light-hard"} { "type": "{{event}}", "ts": "{{timestamp}}", "payload": {{data}} } ``` You may use the same token multiple times and nest placeholders at any depth in your JSON structure. Nested objects and arrays **inside** the substituted `{{data}}` value are preserved from the log row. ### Rules and constraints * **Simple substitution only:** No conditionals, loops, filters, or expressions. * **No field paths:** Placeholders like `{{data.orgId}}`, `{{orgId}}`, or `{{ip}}` do **not** work. To use a single field, read it from the full `data` object on the receiver or transform after ingest. * **Quote `{{data}}` correctly:** `"field": {{data}}` is valid; `"field": "{{data}}"` stringifies the object incorrectly and produces invalid or useless JSON. * **One template per destination:** The same template applies to every log type enabled on that destination. You cannot define different templates per log type on one HTTP destination. * **String escaping:** `{{event}}` and `{{timestamp}}` are JSON-escaped for safe use inside quoted strings. * **Invalid JSON:** Pangolin does not validate templates at save time. If the rendered body is not valid JSON, delivery may still occur but your receiver may reject it. Validate templates with a JSON linter before saving. * **Not available on other destination types:** Body templates apply to HTTP streaming only, not S3 or Datadog destinations. ## Payload format Payload format is separate from the body template. The template defines the shape of **one event**; payload format controls **how many events** are sent per HTTP request. | Format | HTTP body | Content-Type | | ------------------------- | ---------------------------------------- | ---------------------- | | **JSON array** (default) | One POST per batch: `[{…}, {…}, …]` | `application/json` | | **NDJSON** | One JSON object per line, no outer array | `application/x-ndjson` | | **One event per request** | Separate POST for each event | `application/json` | The template is applied once per event, then results are batched into an array, joined as NDJSON lines, or sent individually, depending on the format you select. Choose **NDJSON** for aggregators that expect newline-delimited ingest (Splunk HEC, Elastic/OpenSearch bulk-style HTTP inputs, Loki). Choose **one event per request** when the endpoint cannot accept batches. ## Log type reference The `data` object in each streamed event is the full stored log row. Field sets differ by log type. See the documentation for that log type under **Logs & Analytics** for the complete `data` shape. ## Integration examples ### Generic webhook (default shape, JSON array) Leave custom body template disabled. Select **JSON array** payload format. Point the destination at your webhook URL with bearer or custom-header auth. Each batch POST body looks like: ```json theme={"theme":"gruvbox-light-hard"} [ { "event": "action", "timestamp": "2025-06-15T12:34:56.789Z", "data": { "action": "updateUser", "actor": "admin@example.com" } } ] ``` ### Log aggregator (NDJSON, minimal template) Enable a custom template and select **NDJSON**: ```json theme={"theme":"gruvbox-light-hard"} { "type": "{{event}}", "ts": "{{timestamp}}", "payload": {{data}} } ``` Each line in the POST body is one rendered event. Set any vendor-required headers on the **Headers** tab. ### Vendor schema remapping If a tool expects your log row under a specific key, wrap `{{data}}` without quotes: ```json theme={"theme":"gruvbox-light-hard"} { "source": "pangolin", "sourcetype": "_json", "time": "{{timestamp}}", "event": {{data}} } ``` Adjust property names to match the vendor; field extraction beyond the three template variables happens on the receiver. ## Limitations and troubleshooting * **Field selection:** Cannot pick individual columns in the template. Use full `{{data}}` or transform after delivery. * **Mixed log types:** Enabling multiple log types on one destination produces heterogeneous `data` in the same batch. Enable one type per destination if your pipeline expects a uniform schema. * **Historical logs:** New destinations do not backfill. Only events recorded after the destination is created are streamed. * **Delivery errors:** Check the destination’s **last error** in the dashboard. Common causes: wrong URL, auth failure, TLS issues, or receiver rejecting malformed JSON. * **Quoting `{{data}}`:** `"payload": "{{data}}"` treats the entire row as a string, which is almost always wrong. Use `"payload": {{data}}`. * **Splunk field extraction:** Pangolin does not emit Splunk-style indexed fields in the template. Parse `data` or use a receiver-side pipeline. # Amazon S3 Source: https://docs.pangolin.net/manage/analytics/streaming/s3 Archive audit logs to S3 or S3-compatible object storage
S3 destinations upload batches of your organization's audit logs as objects in a bucket you control. Use them for long-term archival, data lakes (Athena, Glue, BigQuery), or S3-compatible stores such as MinIO and Cloudflare R2. Event streaming is only available in [Pangolin Cloud](https://app.pangolin.net/auth/signup) or self-hosted [Enterprise Edition](/self-host/enterprise-edition). ## Overview An S3 destination writes **one object per batch** via `PutObject`. Each object contains up to 250 events of a **single log type**. There is no custom body template or field mapping; Pangolin serializes every event in a fixed shape and chooses the object key automatically. Configure: 1. **Settings:** Name, credentials, region, bucket, optional prefix and custom endpoint. 2. **Format:** File format (JSON array, NDJSON, or CSV) and optional gzip compression. 3. **Logs:** Which log types are forwarded. ## Settings tab | Field | Required | Description | | --------------------- | -------- | ------------------------------------------------------------ | | Name | Yes | Display label for this destination | | AWS Access Key ID | Yes | Static access key for the S3 client | | AWS Secret Access Key | Yes | Secret for the access key | | AWS Region | Yes | S3 client region (UI default: `us-east-1`) | | Bucket name | Yes | Target bucket | | Key prefix | No | Prepended to every object key; trailing slashes are stripped | | Custom endpoint | No | Base URL for MinIO, R2, etc.; leave blank for AWS S3 | Pangolin uses static access keys only. There is no IAM role, instance profile, or OIDC picker in the UI. S3 destination settings with credentials, region, and bucket Uploads time out after 60 seconds per object. ## Format tab **Gzip compression** (optional): When enabled, the object body is gzip-compressed before upload, `Content-Encoding: gzip` is set, and the object key gets a `.gz` suffix (for example `….json.gz`). Decompress before parsing unless your tool handles gzip automatically. **File format:** | Format | Description | | ------------------------ | ------------------------------------------------------------- | | **JSON array** (default) | One array per object: `[{…}, {…}, …]` | | **NDJSON** | One JSON object per line, no outer array | | **CSV** | RFC-4180 CSV with a header row; see [CSV format](#csv-format) | Format tab with file format and gzip options ## Logs tab Choose which log categories are uploaded. Each enabled type is written to its own key prefix (`request/`, `action/`, etc.). Only log types enabled for your organization can be streamed. Logs tab for selecting streamed log types ## Object key layout Every upload gets a unique key: ``` {prefix}/{logType}/{YYYY}/{MM}/{DD}/{HH-mm-ss-uuid}.{ext}[.gz] ``` | Segment | Meaning | | --------------- | ---------------------------------------------- | | `prefix` | Your optional key prefix; omitted when empty | | `logType` | `request`, `action`, `access`, or `connection` | | `YYYY/MM/DD` | **Upload time (UTC)**, not the event timestamp | | `HH-mm-ss-uuid` | Upload time plus a UUID so keys never collide | | `ext` | `json` (JSON array), `ndjson`, or `csv` | | `.gz` | Present when gzip is enabled | **Without prefix:** ``` request/2026/06/04/14-30-45-a1b2c3d4-e5f6-7890-abcd-ef1234567890.json ``` **With prefix `pangolin/audit` and gzip:** ``` pangolin/audit/action/2026/06/04/14-30-45-a1b2c3d4-e5f6-7890-abcd-ef1234567890.json.gz ``` Enabling multiple log types on one destination produces **separate object streams** under different `logType/` segments. A single object never mixes log types. ## Event record shape Each event in JSON and NDJSON objects uses this fixed structure: ```json theme={"theme":"gruvbox-light-hard"} { "event": "request", "timestamp": "2026-06-04T12:00:00.000Z", "data": { "timestamp": 1717492800, "action": true, "method": "GET", "path": "/api/health" } } ``` | Field | Meaning | | ----------- | --------------------------------------------------------------------- | | `event` | Log type: `request`, `access`, `action`, or `connection` | | `timestamp` | Event time as ISO-8601 UTC (connection logs use session start) | | `data` | The **complete stored log row** for that record, not a curated subset | Some columns are stored as JSON strings in the database (`headers`, `query`, and `metadata` on request logs, for example). In `data`, they appear as **string values**, not nested JSON objects. Parse them in your pipeline if you need structured fields. ## File formats ### JSON array (default) * One S3 object per batch; body is `[{…}, {…}, …]`. * Up to 250 events per object. * `Content-Type: application/json`. ### NDJSON * One S3 object per batch; body is one JSON record per line with no outer array. * Good for Athena, BigQuery load jobs, Spark, and similar line-oriented pipelines. * `Content-Type: application/x-ndjson`. ### CSV format * Header row: `event`, `timestamp`, then **all field names** found in `data` across that batch (union of keys, in insertion order). * Each data row flattens `event`, `timestamp`, and spreads `data` fields into columns. There is **no** nested `data` column. * Missing fields in a given row leave an empty cell. * Object or array values in `data` are written as `JSON.stringify` strings inside the cell. * `Content-Type: text/csv; charset=utf-8`. The column set can grow as new fields appear in later batches. Order is not guaranteed to stay identical across all objects over time. ## Batching and throughput * Objects are written **per batch** (up to \~250 events), not one object per log line. * Pangolin polls for new logs on a regular interval and may write multiple objects during catch-up after a pause. * **No backfill:** New destinations start from the current log cursor. Historical logs already in Pangolin are not uploaded. * **Extended outage:** If the destination is unreachable for about 24 hours, the backlog may be discarded and streaming resumes from the present cursor (same behavior as [HTTP streaming](/manage/analytics/streaming/http)). ## Gzip When gzip is enabled: 1. The serialized body is compressed before upload. 2. The object key includes `.gz` (for example `….ndjson.gz`). 3. S3 stores `Content-Encoding: gzip`. Consumers must decompress before parsing unless the tool auto-detects gzip (many Athena and Spark setups do when `Content-Encoding` is set). NDJSON plus gzip is a common choice for cost-sensitive archival. ## S3-compatible storage Set **Custom endpoint** to your vendor's S3 API URL and provide access key credentials per that vendor's documentation. | Store | Notes | | ----------------- | ------------------------------------------------------------------ | | **AWS S3** | Leave custom endpoint blank; use a bucket in the configured region | | **MinIO** | Set endpoint to your MinIO server URL; use MinIO access keys | | **Cloudflare R2** | Set endpoint to your R2 S3 API URL; use R2 access keys | Pangolin does not expose path-style vs virtual-hosted addressing, ACLs, SSE-KMS, storage class, or multipart tuning. Configure those in the vendor console or bucket policy. ## IAM and bucket policy Grant the access key permission to write under your prefix. A minimal AWS example: ```json theme={"theme":"gruvbox-light-hard"} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["s3:PutObject"], "Resource": "arn:aws:s3:::your-bucket/pangolin/audit/*" }, { "Effect": "Allow", "Action": ["s3:ListBucket"], "Resource": "arn:aws:s3:::your-bucket", "Condition": { "StringLike": { "s3:prefix": ["pangolin/audit/*"] } } } ] } ``` Adjust bucket name and prefix to match your configuration. `ListBucket` is optional but useful when debugging missing objects. Block public access, encryption at rest, lifecycle rules, and object tags are configured in AWS or your vendor console, not in Pangolin. ## Log type reference The `data` object in each streamed event is the full stored log row. Field sets differ by log type. See the documentation for that log type under **Logs & Analytics** for the complete `data` shape. ## Limitations and troubleshooting * **No custom JSON shape:** Fixed event record only. Use an HTTP destination if you need body templates or field remapping. * **No per-event objects:** Always batched (up to \~250 events per object). * **No mixed log types in one object:** Each upload contains a single log type. * **Upload-time partitioning:** Key date folders use upload time (UTC), not the event's `timestamp`. * **CSV columns:** Automatic from batch contents; not user-selectable; column set may change over time. * **Static credentials only:** Rotate keys by updating the destination; credentials are stored encrypted server-side. * **Historical logs:** New destinations do not backfill. * **Delivery errors:** Check the destination's **last error** in the dashboard. Common causes: `AccessDenied`, wrong bucket or region, bad endpoint URL, TLS issues, or expired credentials. * **Missing objects:** Confirm prefix, lifecycle rules, and that the log type is enabled on the **Logs** tab. * **Athena/Glue parse errors:** Verify format (JSON array vs NDJSON), gzip handling, and that the crawler/table schema matches flattened CSV columns if using CSV. # ASN Blocking Source: https://docs.pangolin.net/manage/asnblocking Configure ASN blocking to restrict access based on Autonomous System Numbers
ASN blocking is available in Pangolin Community Edition. It depends on Pangolin's ASN lookup database. Follow [Enable ASN Lookup](/self-host/advanced/enable-asn-lookup) before creating ASN rules. ## Benefits of ASN Blocking ASN blocking provides several important security and operational advantages: ### Security Benefits * **Block Malicious Networks**: Prevent access from autonomous systems known for hosting malicious activity, botnets, or spam operations * **Control Cloud Provider Access**: Restrict or allow access from specific cloud providers (AWS, Azure, GCP, etc.) * **Block VPN/Proxy Services**: Deny access from commercial VPN and proxy service providers to prevent anonymous access * **Datacenter Filtering**: Block traffic from datacenter networks while allowing residential ISPs * **Compliance Requirements**: Meet regulatory requirements that restrict access from certain network types or providers ## Implementing ASN Blocking with Bypass Rules ASN blocking in Pangolin is implemented using [bypass rules](/manage/access-control/rules) with ASN-based matching. You can create rules that either allow or deny access based on the visitor's Autonomous System Number. To apply the same ASN rules to multiple public resources, define them in a [resource policy](/manage/resources/public/resource-policies) and attach that policy to each resource. Pangolin Dashboard ### Setting Up ASN Blocking Rules 1. Navigate to your target resource and select the **Rules** tab 2. Create a new rule and select **ASN** as the match type 3. Choose an ASN from the dropdown of common providers, or manually enter a specific ASN number 4. Choose your rule action: * **Allow**: Bypass authentication for users from specific ASNs * **Deny**: Block all access from specific ASNs * **Pass to Auth**: Let users from specific ASNs proceed to authentication ### Common ASNs The dropdown includes many commonly-used ASNs such as: * **Cloud Providers**: Amazon (AS16509), Google Cloud (AS15169), Microsoft Azure (AS8075), DigitalOcean (AS14061) * **Major ISPs**: Comcast (AS7922), AT\&T (AS7018), Verizon (AS701), Deutsche Telekom (AS3320) * **VPN/Proxy Services**: NordVPN (various), ExpressVPN (various), Mullvad (AS42831) * **CDN Providers**: Cloudflare (AS13335), Fastly (AS54113), Akamai (various) If the ASN you need isn't in the dropdown, you can manually enter the ASN number (e.g., AS12345 or just 12345). ### Common ASN Blocking Patterns #### Block VPN and Proxy Services Create deny rules for known VPN and proxy ASNs to prevent anonymous access: 1. Create **Deny** rules for each VPN/proxy provider ASN 2. Select ASNs from the dropdown or enter them manually 3. Set appropriate priorities #### Block Datacenter Traffic Block access from datacenter and hosting provider ASNs while allowing residential users: 1. Create **Deny** rules for major cloud and hosting provider ASNs 2. Include providers like AWS, GCP, Azure, DigitalOcean, etc. 3. This helps ensure only real users from residential ISPs can access your resources #### Allow Only Specific Networks Create a default deny rule and explicitly allow only approved ASNs: 1. Create a **Deny** rule matching all traffic with priority 100 2. Create **Allow** rules for specific approved ASNs with higher priority (e.g., 10, 20, 30) #### Regional ISP Control Allow access only from specific country ISPs while blocking others: 1. **Combine with Country Rules**: Use ASN rules to specify which ISPs are allowed 2. Create **Allow** rules for major residential ISPs in your target countries 3. Block datacenter and VPN ASNs that might circumvent country restrictions ### Best Practices ASN blocking affects all users from that network. Be careful when blocking large ISPs or cloud providers, as legitimate users or your own infrastructure may be affected. ### Finding ASN Numbers If you need to find the ASN for a specific network or provider: 1. Use online tools like [bgp.he.net](https://bgp.he.net/) or [ipinfo.io](https://ipinfo.io/) 2. Search by company name, IP address, or ASN number 3. Enter the ASN in the rule configuration (with or without the "AS" prefix) ### Rule Priority Example ``` Priority 1: Allow - ASN: AS7922 (Comcast) Priority 2: Allow - ASN: AS7018 (AT&T) Priority 3: Deny - ASN: AS13335 (Cloudflare - VPN) Priority 4: Deny - ASN: AS16509 (Amazon - Datacenter) ``` This configuration allows access from residential users on Comcast and AT\&T while blocking Cloudflare's VPN service and Amazon datacenters. ### Advanced Patterns #### Block Bot Networks Identify and block ASNs associated with automated bot traffic: 1. Monitor your access logs for suspicious ASNs 2. Create **Deny** rules for ASNs showing bot-like behavior 3. Regularly review and update your blocklist # Blueprints Source: https://docs.pangolin.net/manage/blueprints Define Pangolin resources and site settings declaratively with YAML or container labels
Blueprints let you define Pangolin resources as code. Instead of configuring every site, target, and access rule manually in the dashboard, you describe the desired state in YAML or container labels and let Pangolin apply it consistently. Use blueprints when you want: * Repeatable rollouts across many sites * Version control for infrastructure and access settings * A source of truth that can be reviewed, templated, and automated Some features in this documentation are marked with **(EE)**, which means they require [Enterprise Edition](/self-host/enterprise-edition). ## Blueprint Mental Model A blueprint can contain up to four top-level sections: * **`public-resources`**: Internet-facing HTTP, TCP, UDP, SSH, RDP, or VNC resources * **`private-resources`**: Client-only access to hosts or CIDR ranges * **`public-policies`**: Reusable authentication and access policy objects * **`sites`**: Site-level settings such as container label discovery ```yaml theme={"theme":"gruvbox-light-hard"} public-resources: : ... private-resources: : ... public-policies: : ... sites: : ... ``` Think of the resource key as your stable ID inside the blueprint. The fields under that key describe what Pangolin should create or maintain. These keys are the same idea as the **Identifier** shown in the Pangolin app, called `niceId` in the API. Choose readable, stable keys such as `warehouse-west` or `web-app`; blueprints use them to match and update the same sites and resources on later applies. For more detail, see [What is an identifier?](/manage/common-api-routes#what-is-an-identifier). ## Choose A Format Pangolin supports two blueprint formats: ### YAML Use YAML when you want a readable file that can be committed to git, applied through Newt, pasted into the UI, or sent through the API. ### Container Labels Use container labels when the resource definition should live inside your Compose stack. This is especially useful when a container and its Pangolin resource should be managed together. ## How YAML Blueprints Are Applied Paste YAML into **Settings > Blueprints** in the Pangolin dashboard. Blueprint creation page in the Pangolin dashboard Run Newt with `--blueprint-file` to keep the file declarative and continuously applied: ```bash theme={"theme":"gruvbox-light-hard"} newt --blueprint-file /path/to/blueprint.yaml ``` If you only want a one-time bootstrap during provisioning, use [`--provisioning-blueprint-file`](/manage/sites/site-provisioning) instead. Apply a blueprint through the Pangolin API with an API key. See the [API documentation](https://api.pangolin.net/v1/docs/#/Organization/put_org__orgId__blueprint). `PUT /org/{orgId}/blueprint` ```json theme={"theme":"gruvbox-light-hard"} { "blueprint": "base64-encoded-json-content" } ``` [Python example](https://github.com/fosrl/pangolin/blob/dev/blueprint.py) Apply a blueprint directly from the Pangolin CLI when you want a one-off apply from a terminal, CI job, or local automation. **Using your logged-in user account** First log in and select the organization you want the blueprint applied to: ```bash theme={"theme":"gruvbox-light-hard"} pangolin login pangolin select org --org ``` Then apply the file: ```bash theme={"theme":"gruvbox-light-hard"} pangolin apply blueprint --file /path/to/blueprint.yaml ``` The CLI uses your active account and selected organization. You can optionally set the saved blueprint name: ```bash theme={"theme":"gruvbox-light-hard"} pangolin apply blueprint --file /path/to/blueprint.yaml --name production ``` **Using an Integration API key** For non-interactive automation, pass an Integration API key, API endpoint, and organization ID together: ```bash theme={"theme":"gruvbox-light-hard"} pangolin apply blueprint \ --file /path/to/blueprint.yaml \ --api-key \ --endpoint https://api.example.com \ --org ``` For Pangolin Cloud, use `https://api.pangolin.net` as the endpoint. For self-hosted Pangolin, use your API host, for example `https://api.your-domain.com`. See [Integration API](/manage/integration-api) for creating API keys and enabling the API on self-hosted deployments. You can also pipe a blueprint through stdin. When using stdin, provide `--name` because there is no filename to derive it from: ```bash theme={"theme":"gruvbox-light-hard"} render-blueprint | pangolin apply blueprint --file - --name production ``` `--blueprint-file` in Newt and container labels behave as an ongoing source of truth. Dashboard edits can be overwritten the next time the blueprint is applied. UI, API, and CLI applies are typically one-off operations. ## Quick Start YAML Example This example shows three common top-level sections in one file: ```yaml theme={"theme":"gruvbox-light-hard"} public-resources: web-app: name: Web App mode: http full-domain: app.example.com auth: sso-enabled: true whitelist-users: - admin@example.com targets: - site: my-site hostname: app port: 8080 method: http healthcheck: hostname: app port: 8080 path: /health private-resources: ssh-host: name: SSH Host mode: host sites: - my-site destination: 192.168.1.10 tcp-ports: "22" roles: - DevOps sites: my-site: name: My Site docker-socket-enabled: true ``` ## Public Resources Public resources expose services through Pangolin. * Use **`http`** for websites, APIs, and dashboards * Use **`tcp`** or **`udp`** for raw public services bound to a port on the Pangolin server * Use **`ssh`**, **`rdp`**, or **`vnc`** for protocol-specific resources Use `mode` for new blueprints. `protocol` is still accepted for backward compatibility and is normalized to `mode`. ### HTTP Resource Example ```yaml theme={"theme":"gruvbox-light-hard"} public-resources: app: name: App mode: http full-domain: app.example.com host-header: app.internal tls-server-name: app.internal headers: - name: X-Env value: production rules: - action: allow match: country value: US - action: deny match: path value: /admin auth: sso-enabled: true whitelist-users: - admin@example.com targets: - site: my-site hostname: app port: 8080 method: http ``` When applying a blueprint via Newt (using `--blueprint-file` or container labels), `site` on each target is optional. If omitted, the target is assigned to the site of the Newt that applied the blueprint. ### Raw TCP Or UDP Example ```yaml theme={"theme":"gruvbox-light-hard"} public-resources: mqtt: name: Mosquitto MQTT mode: tcp proxy-port: 1883 targets: - site: my-site hostname: mqtt-server port: 1883 ``` For raw resources: * `proxy-port` is required * Target `method` must not be set * `auth` is not supported ### Targets-Only Resources A public resource can contain only `targets`. This is useful when you want to add or manage targets for an existing resource definition without repeating all resource-level fields. ```yaml theme={"theme":"gruvbox-light-hard"} public-resources: extra-targets: targets: - site: secondary-site hostname: app-2 port: 8080 method: http - site: tertiary-site hostname: app-3 port: 8080 method: http ``` When a resource is targets-only, `name` and `mode` are not required. ### Authentication Example Authentication is configured inside `auth` and is supported only for HTTP resources. ```yaml theme={"theme":"gruvbox-light-hard"} public-resources: secure-app: name: Secure App mode: http full-domain: secure.example.com auth: pincode: 123456 password: strong-password basic-auth: user: demo password: change-me sso-enabled: true sso-roles: - Member sso-users: - user@example.com whitelist-users: - admin@example.com ``` ### Maintenance Page **(EE)** The `maintenance` object lets you present a maintenance page for a public HTTP resource. ```yaml theme={"theme":"gruvbox-light-hard"} public-resources: app: name: App mode: http full-domain: app.example.com maintenance: enabled: true type: automatic title: Scheduled Maintenance message: We are upgrading the service. estimated-time: 2 hours targets: - site: my-site hostname: app port: 8080 method: http ``` Maintenance `type` values: * **`forced`**: Always show the maintenance page * **`automatic`**: Show it only when all targets are unhealthy or the sites are offline ## Private Resources Private resources define what Pangolin clients can reach after they connect to your organization. * Use **`mode: host`** for a single host or DNS name * Use **`mode: cidr`** for an entire network range * Use **`mode: http`** to expose an internal HTTP endpoint to clients via a private domain * Use **`mode: ssh`** for SSH access workflows (including native auth-daemon mode) When applying a blueprint via Newt (using `--blueprint-file` or container labels), `sites` is optional. If omitted, the resource is assigned to the site of the Newt that applied the blueprint. ```yaml theme={"theme":"gruvbox-light-hard"} private-resources: internal-net: name: Internal Network mode: cidr destination: 10.0.0.0/24 sites: - my-site tcp-ports: "22,443,8000-9000" udp-ports: "53,123" disable-icmp: false alias: "*.internal.example.com" roles: - Developer users: - user@example.com machines: - machine-id-1 internal-app: name: Internal App mode: http destination: 10.0.0.5 destination-port: 8080 sites: - my-site full-domain: app.internal.example.com ssl: true scheme: https roles: - Member ``` ## Container Labels Format Container labels are the same blueprint schema flattened into dot-separated keys: * Start every label with `pangolin.` * Keep the same object path as YAML * Use array indexes for lists, such as `[0]` Example YAML: ```yaml theme={"theme":"gruvbox-light-hard"} public-resources: my-app: headers: - name: X-Env value: prod ``` Equivalent Compose labels: ```yaml theme={"theme":"gruvbox-light-hard"} labels: - pangolin.public-resources.my-app.headers[0].name=X-Env - pangolin.public-resources.my-app.headers[0].value=prod ``` Container labels are continuously applied. Treat the Compose file as the source of truth because dashboard edits can be overwritten. ### Enable Container Label Discovery To use container labels, Newt must be able to read the Docker socket: ```bash theme={"theme":"gruvbox-light-hard"} newt --docker-socket /var/run/docker.sock ``` Or with an environment variable: ```bash theme={"theme":"gruvbox-light-hard"} DOCKER_SOCKET=/var/run/docker.sock ``` ### Docker Compose Example ```yaml theme={"theme":"gruvbox-light-hard"} services: newt: image: fosrl/newt container_name: newt restart: unless-stopped volumes: - /var/run/docker.sock:/var/run/docker.sock environment: - PANGOLIN_ENDPOINT=https://app.pangolin.net - NEWT_ID=h1rbsgku89wf9z3 - NEWT_SECRET=z7g54mbcwkglpx1aau9gb8mzcccoof2fdbs97keoakg2pp5z - DOCKER_SOCKET=/var/run/docker.sock nginx1: image: nginxdemos/hello container_name: nginx1 labels: - pangolin.public-resources.nginx.name=nginx - pangolin.public-resources.nginx.full-domain=nginx.fosrl.io - pangolin.public-resources.nginx.protocol=http - pangolin.public-resources.nginx.headers[0].name=X-Example-Header - pangolin.public-resources.nginx.headers[0].value=example-value - pangolin.public-resources.nginx.targets[0].method=http - pangolin.public-resources.nginx.targets[0].path=/path - pangolin.public-resources.nginx.targets[0].path-match=prefix nginx2: image: nginxdemos/hello container_name: nginx2 labels: - pangolin.public-resources.nginx.targets[1].method=http - pangolin.public-resources.nginx.targets[1].hostname=nginx2 - pangolin.public-resources.nginx.targets[1].port=80 networks: default: name: pangolin_default ``` This creates a single Pangolin resource with multiple targets: Example resource ### Container Label Behavior If `hostname` or `port` are not set explicitly, Pangolin can detect them from the container configuration. The hostname typically defaults to the container name, and port detection is based on the container's `expose` configuration. If no `site` is specified on a target (public resource) or on a private resource, it is assigned to the site of the Newt that applied the blueprint — whether through container labels or `--blueprint-file`. Labels from multiple containers can be merged into one logical resource, which is useful when different containers contribute different targets. ## Configuration Reference Use this section when you need the full schema. The order below mirrors the blueprint structure rather than the dashboard UI. ### Top-Level Object ```yaml theme={"theme":"gruvbox-light-hard"} public-resources: app: name: App mode: http full-domain: app.example.com targets: - hostname: app port: 80 method: http private-resources: ssh-host: name: SSH Host mode: host sites: - my-site destination: 192.168.1.10 tcp-ports: "22" public-policies: default-member: name: Default Member Policy sso: true apply-rules: false rules: [] sites: my-site: name: My Site docker-socket-enabled: true ``` Public proxy resources keyed by resource ID. YAML: `public-resources: { web-app: { ... } }`\ Container label: `pangolin.public-resources.web-app.name=Web App` Private resources keyed by resource ID. YAML: `private-resources: { internal-net: { ... } }`\ Container label: `pangolin.private-resources.internal-net.mode=cidr` Reusable policy definitions keyed by policy ID. YAML: `public-policies: { default-member: { ... } }`\ Container label: `pangolin.public-policies.default-member.name=Default Member Policy` Site-level settings keyed by site ID. YAML: `sites: { my-site: { name: My Site } }`\ Container label: `pangolin.sites.my-site.name=My Site` Display name for the site. YAML: `name: My Site`\ Container label: `pangolin.sites.my-site.name=My Site` Enables blueprint discovery from container labels for that site. **Default**: `true` YAML: `docker-socket-enabled: true`\ Container label: `pangolin.sites.my-site.docker-socket-enabled=true` ### Public Resource Object (`public-resources`) ```yaml theme={"theme":"gruvbox-light-hard"} public-resources: web-app: name: Web App mode: http policy: default-member full-domain: app.example.com enabled: true host-header: internal.example.local tls-server-name: internal.example.local headers: - name: X-Env value: prod rules: - action: allow match: country value: US - action: deny match: region value: 019 auth: pincode: 123456 sso-enabled: true whitelist-users: - admin@example.com maintenance: enabled: true type: automatic targets: - site: my-site hostname: app port: 8080 method: http path: / path-match: prefix rewrite-path: / rewrite-match: prefix healthcheck: hostname: app port: 8080 path: /health ``` A single public resource definition. Human-readable resource name. Required unless the resource is targets-only. YAML: `name: Web App`\ Container label: `pangolin.public-resources.web-app.name=Web App` Resource mode. Preferred over `protocol`. **Options**: `http`, `tcp`, `udp`, `ssh`, `rdp`, `vnc` YAML: `mode: http`\ Container label: `pangolin.public-resources.web-app.mode=http` Deprecated resource type field. Use `mode` instead. **Options**: `http`, `tcp`, `udp`, `ssh`, `rdp`, `vnc` YAML: `protocol: http`\ Container label: `pangolin.public-resources.web-app.protocol=http` Policy key to attach from `public-policies`. YAML: `policy: default-member`\ Container label: `pangolin.public-resources.web-app.policy=default-member` Optional SSL/TLS flag present in the schema. YAML: `ssl: true`\ Container label: `pangolin.public-resources.web-app.ssl=true` Public hostname for HTTP resources. Required when `protocol: http`. YAML: `full-domain: app.example.com`\ Container label: `pangolin.public-resources.web-app.full-domain=app.example.com` Public port for raw TCP or UDP resources. Required when `mode` is `tcp` or `udp`. YAML: `proxy-port: 3000`\ Container label: `pangolin.public-resources.raw-api.proxy-port=3000` Enables Proxy Protocol on TCP resources. YAML: `proxy-protocol: true`\ Container label: `pangolin.public-resources.raw-api.proxy-protocol=true` Proxy Protocol version when `proxy-protocol` is enabled. **Minimum**: `1`\ YAML: `proxy-protocol-version: 1`\ Container label: `pangolin.public-resources.raw-api.proxy-protocol-version=1` Disables the resource without removing it. YAML: `enabled: true`\ Container label: `pangolin.public-resources.web-app.enabled=true` Overrides the upstream `Host` header sent to the target. YAML: `host-header: internal.example.local`\ Container label: `pangolin.public-resources.web-app.host-header=internal.example.local` Overrides the TLS SNI hostname used for upstream TLS connections. YAML: `tls-server-name: internal.example.local`\ Container label: `pangolin.public-resources.web-app.tls-server-name=internal.example.local` Static headers added to proxied requests. Container labels for arrays must include an index (`[0]`, `[1]`, ...). YAML: `headers: [{ name: X-Env, value: prod }]`\ Container label: `pangolin.public-resources.web-app.headers[0].name=X-Env` Header name. YAML: `name: X-Env`\ Container label: `pangolin.public-resources.web-app.headers[0].name=X-Env` Header value. YAML: `value: prod`\ Container label: `pangolin.public-resources.web-app.headers[0].value=prod` Ordered access rules for public resources. Container labels for arrays must include an index (`[0]`, `[1]`, ...). YAML: `rules: [{ action: allow, match: country, value: US }]`\ Container label: `pangolin.public-resources.web-app.rules[0].action=allow` What Pangolin should do when the rule matches. **Options**: `allow`, `deny`, `pass` YAML: `action: allow`\ Container label: `pangolin.public-resources.web-app.rules[0].action=allow` Match type for the rule. **Options**: `cidr`, `path`, `ip`, `country`, `asn`, `region` YAML: `match: country`\ Container label: `pangolin.public-resources.web-app.rules[0].match=country` Value to compare against, such as an IP, CIDR, path, two letter country code, ASN, or region. YAML: `value: US`\ Container label: `pangolin.public-resources.web-app.rules[0].value=US` When using match: region, the value must be a supported region code. The following region IDs are available, based on the United Nations geoscheme:
  • 002: Africa
    • 015: Northern Africa
    • 014: Eastern Africa
    • 017: Middle Africa
    • 018: Southern Africa
    • 011: Western Africa
  • 019: Americas
    • 029: Caribbean
    • 013: Central America
    • 005: South America
    • 021: Northern America
  • 142: Asia
    • 143: Central Asia
    • 030: Eastern Asia
    • 035: South-Eastern Asia
    • 034: Southern Asia
    • 145: Western Asia
  • 150: Europe
    • 151: Eastern Europe
    • 154: Northern Europe
    • 039: Southern Europe
    • 155: Western Europe
  • 009: Oceania
    • 053: Australia and New Zealand
    • 054: Melanesia
    • 057: Micronesia
    • 061: Polynesia
Use the top-level code (e.g., 019 for all Americas) or a subregion code (e.g., 021 for Northern America) as needed.
Explicit rule priority. If omitted, priority is assigned from the rule order. YAML: `priority: 10`\ Container label: `pangolin.public-resources.web-app.rules[0].priority=10` Enables or disables the rule. **Default**: `true`\ YAML: `enabled: true`\ Container label: `pangolin.public-resources.web-app.rules[0].enabled=true`
Authentication settings for HTTP resources. Not allowed for `tcp` or `udp`. Numeric PIN required before access is granted. YAML: `pincode: 123456`\ Container label: `pangolin.public-resources.web-app.auth.pincode=123456` Shared password gate for the resource. YAML: `password: super-secret`\ Container label: `pangolin.public-resources.web-app.auth.password=super-secret` Enables Pangolin sign-in for the resource. YAML: `sso-enabled: true`\ Container label: `pangolin.public-resources.web-app.auth.sso-enabled=true` Roles allowed through SSO. Container labels for arrays must include an index (`[0]`, `[1]`, ...). YAML: `sso-roles: [Member]`\ Container label: `pangolin.public-resources.web-app.auth.sso-roles[0]=Member` Specific user identifiers allowed through SSO. Container labels for arrays must include an index (`[0]`, `[1]`, ...). YAML: `sso-users: [user@example.com]`\ Container label: `pangolin.public-resources.web-app.auth.sso-users[0]=user@example.com` Whitelisted emails or patterns allowed for email-based access flows. Container labels for arrays must include an index (`[0]`, `[1]`, ...). YAML: `whitelist-users: [admin@example.com]`\ Container label: `pangolin.public-resources.web-app.auth.whitelist-users[0]=admin@example.com` Identity provider ID to redirect to automatically. YAML: `auto-login-idp: 1`\ Container label: `pangolin.public-resources.web-app.auth.auto-login-idp=1` HTTP basic auth settings. Basic auth username. YAML: `user: demo`\ Container label: `pangolin.public-resources.web-app.auth.basic-auth.user=demo` Basic auth password. YAML: `password: change-me`\ Container label: `pangolin.public-resources.web-app.auth.basic-auth.password=change-me` Compatibility flag for basic auth behavior. **Default**: `true` YAML: `extendedCompatibility: true`\ Container label: `pangolin.public-resources.web-app.auth.basic-auth.extendedCompatibility=true` Maintenance page configuration for public resources **(EE)**. YAML: `maintenance: { enabled: true, type: forced }`\ Container label: `pangolin.public-resources.web-app.maintenance.enabled=true` Turns the maintenance page feature on for the resource. When Pangolin should show the page. **Options**: `forced`, `automatic` Main heading shown on the maintenance page. Message shown to users while the resource is unavailable. Optional estimate for when the service will return. Auth-daemon configuration for supported modes. PAM behavior. Passthrough will require entering the username, password, or private key when connecting. Push will sync a Pangolin user with the configuration from the roles onto the auth-daemon and not prompt a log in. **Options**: `passthrough`, `push`\ **Default**: `passthrough` Auth-daemon mode. To use the built in ssh server inside of the site connector use `native`. For external ssh servers, use `remote` or `site` depending on where the server is. **Options**: `site`, `remote`, `native`\ **Default**: `site` Remote auth-daemon port. Required when `auth-daemon.mode` is `remote`. Backend destinations for the resource. Container labels for arrays must include an index (`[0]`, `[1]`, ...). YAML: `targets: [{ hostname: app, port: 8080, method: http }]`\ Container label: `pangolin.public-resources.web-app.targets[0].hostname=app` Site that hosts the target. Optional when deploying from a Newt — if omitted, the target is assigned to the site of the Newt that applied the blueprint. YAML: `site: my-site`\ Container label: `pangolin.public-resources.web-app.targets[0].site=my-site` Upstream protocol for HTTP resources. **Options**: `http`, `https`, `h2c` YAML: `method: http`\ Container label: `pangolin.public-resources.web-app.targets[0].method=http` Target hostname or IP address. YAML: `hostname: app`\ Container label: `pangolin.public-resources.web-app.targets[0].hostname=app` Target port. YAML: `port: 8080`\ Container label: `pangolin.public-resources.web-app.targets[0].port=8080` Disables the target without deleting it. YAML: `enabled: true`\ Container label: `pangolin.public-resources.web-app.targets[0].enabled=true` Internal port override used in container-oriented setups. YAML: `internal-port: 8080`\ Container label: `pangolin.public-resources.web-app.targets[0].internal-port=8080` Path condition used for HTTP routing. YAML: `path: /`\ Container label: `pangolin.public-resources.web-app.targets[0].path=/` Matching mode for `path`. **Options**: `exact`, `prefix`, `regex` Replacement path or prefix used during path rewriting. YAML: `rewrite-path: /`\ Container label: `pangolin.public-resources.web-app.targets[0].rewrite-path=/` Deprecated alias for `rewrite-path`. Rewrite mode. **Options**: `exact`, `prefix`, `regex`, `stripPrefix` Target priority used in routing decisions. **Range**: `1-1000`\ **Default**: `100` YAML: `priority: 100`\ Container label: `pangolin.public-resources.web-app.targets[0].priority=100` Health monitoring for the target. Hostname or IP used for the health check. Port used for the health check. Enables health checking for the target. HTTP path to check, such as `/health`. Scheme to use for the check. Health check mode supported by the schema. Check interval while the target is healthy. Check interval while the target is unhealthy. Deprecated alias for `unhealthy-interval`. Timeout for each health check attempt. Headers sent with the health check request. Container labels for arrays must include an index (`[0]`, `[1]`, ...). YAML: `headers: [{ name: X-Health-Check, value: true }]`\ Container label: `pangolin.public-resources.web-app.targets[0].healthcheck.headers[0].name=X-Health-Check` Header name. Header value. Whether redirects should be followed. Deprecated alias for `follow-redirects`. HTTP method for the check request. Expected HTTP status code.
### Private Resource Object (`private-resources`) ```yaml theme={"theme":"gruvbox-light-hard"} private-resources: internal-net: name: Internal Network mode: cidr sites: - my-site destination: 10.0.0.0/24 enabled: true tcp-ports: "22,443" udp-ports: "53" disable-icmp: false alias: "*.internal.example.com" roles: - Member users: - user@example.com machines: - machine-id-1 internal-app: name: Internal App mode: http sites: - my-site destination: 10.0.0.5 destination-port: 8080 full-domain: app.internal.example.com ssl: true scheme: https enabled: true ``` A single private resource definition. Display name for the resource. YAML: `name: Internal Network`\ Container label: `pangolin.private-resources.internal-net.name=Internal Network` Private resource type. **Options**: `host`, `cidr`, `http`, `ssh` * `host`: A single host or IP. If `destination` is a domain, `alias` is required. * `cidr`: An entire IPv4 or IPv6 CIDR range. * `http`: An internal HTTP endpoint exposed to clients via `full-domain`. * `ssh`: SSH access resource. `destination` may be omitted only when `auth-daemon.mode` is `native` (or when `auth-daemon` is omitted). YAML: `mode: cidr`\ Container label: `pangolin.private-resources.internal-net.mode=cidr` Sites that host the resource. Optional when deploying from a Newt — if omitted, the resource is assigned to the site of the Newt that applied the blueprint. Container labels for arrays must include an index (`[0]`, `[1]`, ...). YAML: `sites: [my-site]`\ Container label: `pangolin.private-resources.internal-net.sites[0]=my-site` Deprecated. Use `sites` instead. YAML: `site: my-site`\ Container label: `pangolin.private-resources.internal-net.site=my-site` Host, IP, or CIDR block the client should reach. The accepted format depends on `mode`: * `host`: a valid IPv4/IPv6 address, or a hostname/domain (when using a domain, `alias` must also be set) * `cidr`: a valid IPv4 or IPv6 CIDR block * `http`: a host or IP for the upstream HTTP endpoint * `ssh`: optional only for `auth-daemon.mode: native`; required otherwise YAML: `destination: 10.0.0.0/24`\ Container label: `pangolin.private-resources.internal-net.destination=10.0.0.0/24` Auth-daemon configuration. PAM behavior. Passthrough will require entering the username, password, or private key when connecting. Push will sync a Pangolin user with the configuration from the roles onto the auth-daemon and not prompt a log in. **Options**: `passthrough`, `push`\ **Default**: `passthrough` Auth-daemon mode. To use the built in ssh server inside of the site connector use `native`. For external ssh servers, use `remote` or `site` depending on where the server is. **Options**: `site`, `remote`, `native`\ **Default**: `site` Remote auth-daemon port. Required when `auth-daemon.mode` is `remote`. Upstream port for the destination. Typically used with `mode: http` to point at the internal HTTP endpoint's port. YAML: `destination-port: 8080`\ Container label: `pangolin.private-resources.internal-app.destination-port=8080` Internal domain used to expose an HTTP private resource to clients. Applies when `mode: http`. YAML: `full-domain: app.internal.example.com`\ Container label: `pangolin.private-resources.internal-app.full-domain=app.internal.example.com` Whether SSL/TLS should be used when serving the private HTTP resource. YAML: `ssl: true`\ Container label: `pangolin.private-resources.internal-app.ssl=true` Upstream scheme used by Pangolin to reach the destination for HTTP private resources. **Options**: `http`, `https` YAML: `scheme: https`\ Container label: `pangolin.private-resources.internal-app.scheme=https` Disables the resource without removing it. YAML: `enabled: true`\ Container label: `pangolin.private-resources.internal-net.enabled=true` Allowed TCP ports or ranges. Use comma-separated values for multiple ports or ranges, such as `22,443,8000-9000` or `*` for all ports or leave empty for no ports. **Default**: `*` YAML: `tcp-ports: "22,443"`\ Container label: `pangolin.private-resources.internal-net.tcp-ports=22,443` Allowed UDP ports or ranges. Use comma-separated values for multiple ports or ranges, such as `22,443,8000-9000` or `*` for all ports or leave empty for no ports. **Default**: `*` YAML: `udp-ports: "53"`\ Container label: `pangolin.private-resources.internal-net.udp-ports=53` Prevents ICMP traffic such as ping. **Default**: `false` YAML: `disable-icmp: false`\ Container label: `pangolin.private-resources.internal-net.disable-icmp=false` Internal DNS alias for the resource. Must be a fully qualified domain name and may include wildcards (`*`, `?`), e.g. `example.com`, `*.example.com`, or `host-0?.example.internal`. Required when `mode: host` and `destination` is a domain. YAML: `alias: "*.internal.example.com"`\ Container label: `pangolin.private-resources.internal-net.alias=*.internal.example.com` Roles allowed to access the resource. The `Admin` role is reserved and cannot be listed here. Container labels for arrays must include an index (`[0]`, `[1]`, ...). YAML: `roles: [Member]`\ Container label: `pangolin.private-resources.internal-net.roles[0]=Member` Individual users allowed to access the resource. Container labels for arrays must include an index (`[0]`, `[1]`, ...). YAML: `users: [user@example.com]`\ Container label: `pangolin.private-resources.internal-net.users[0]=user@example.com` Machine identities allowed to access the resource. Container labels for arrays must include an index (`[0]`, `[1]`, ...). YAML: `machines: [machine-id-1]`\ Container label: `pangolin.private-resources.internal-net.machines[0]=machine-id-1` ### Resource Policy Object (`public-policies`) ```yaml theme={"theme":"gruvbox-light-hard"} public-policies: default-member: name: Default Member Policy sso: true auto-login-idp: null sso-roles: - Member sso-users: [] password: null pincode: null basic-auth: null email-whitelist-enabled: false whitelist-users: - "*@example.com" apply-rules: true rules: - action: allow match: country value: US enabled: true ``` A reusable policy definition that can be referenced by public resources via `policy`. Display name for the policy. Enables SSO for the policy. **Default**: `true` Identity provider ID to redirect to automatically. Roles allowed by this policy. `Admin` is not allowed. Specific users allowed by this policy. Optional shared password. Optional 6-digit PIN. Optional basic-auth gate. Basic auth username. Basic auth password. Compatibility flag for basic auth behavior. **Default**: `true` Enables email whitelist matching. **Default**: `false` Allowed email addresses or wildcard-domain entries like `*@example.com`. Enables rule evaluation. **Default**: `false` Policy rule list using the same rule schema as public resource rules. ## Validation Rules And Constraints ### Core Rules 1. A public resource can be **targets-only**. In that case it may contain only `targets`, and `name` plus `mode` are not required. 2. For full public resources, `name` plus (`mode` or deprecated `protocol`) are required. 3. When mode/protocol is `http`, the resource must have `full-domain` and each target must include `method`. 4. When mode/protocol is `tcp` or `udp`, the resource must have `proxy-port`, targets must not include `method`, and `auth` is not allowed. 5. `proxy-protocol` and `proxy-protocol-version` are only valid when mode/protocol is `tcp`. 6. If `auth-daemon.mode` is `remote`, `auth-daemon.port` is required. 7. In private resources, `destination` is required unless `mode: ssh` with native auth-daemon mode. 8. `full-domain` values must be unique across public resources. 9. `proxy-port` values must be unique per protocol within `public-resources`. TCP `3000` and UDP `3000` can coexist, but two TCP resources cannot both use `3000`. 10. `alias` values must be unique across private resources in the blueprint. ### Common Validation Errors ### "Admin role cannot be included in sso-roles" `Admin` is reserved and cannot be used in `auth.sso-roles`. ### "Duplicate 'full-domain' values found" Every public HTTP resource must have its own unique `full-domain`. ### "Duplicate 'proxy-port' values found in public-resources" Two public resources with the same protocol cannot reuse the same `proxy-port`. ### "When protocol is 'http', all targets must have a 'method' field" Each HTTP target must specify `http`, `https`, or `h2c`. ### "When protocol is 'tcp' or 'udp', targets must not have a 'method' field" Raw targets do not use HTTP methods. ### "When protocol is 'tcp' or 'udp', 'auth' must not be provided" Authentication settings apply only to HTTP public resources. ### "'proxy-protocol' and 'proxy-protocol-version' can only be set when mode is 'tcp'" Only TCP public resources can define proxy protocol behavior. ### "port is required when auth-daemon mode is 'remote'" Set `auth-daemon.port` whenever `auth-daemon.mode: remote` is used. ### "destination is required unless mode is 'ssh' with auth-daemon mode 'native'" For private SSH resources, `destination` can be omitted only for native auth-daemon mode. ### "Resource must either be targets-only or have both 'name' and 'protocol' fields" Provide both fields for a full public resource definition, or remove everything except `targets`. ### "Duplicate 'alias' values found in private-resources" Private resource aliases must be unique within the blueprint. ### "Destination must be a valid IP address or valid domain AND alias is required" In `host` mode, the destination must be a valid host or IP. If you use a domain, provide `alias` as well. ### "Destination must be a valid CIDR notation for cidr mode" In `cidr` mode, `destination` must be a valid CIDR block such as `10.0.0.0/24`. ### "Admin role cannot be included in roles" `Admin` is reserved and cannot be used in private resource `roles`. # Branding Source: https://docs.pangolin.net/manage/branding Learn how to customize the look your Pangolin dashboard and login pages with custom branding
Branding is only available in [Enterprise Edition](/self-host/enterprise-edition). Pangolin allows you to customize the appearance of your dashboard with your own branding, including colors, logos, and custom text for authentication pages. Branding is configured through the `privateConfig.yml` file. ## Organization Branding In the settings of each organization, there is an Authentication Page branding settings section. These settings enable you to brand the resource authentication page and organization authentication page for that specific organization. These settings will override anything set in the configuration file. ## Setting up Branding To get started, create a `privateConfig.yml` file next to your existing `config.yml` file: ```yaml title="privateConfig.yml" theme={"theme":"gruvbox-light-hard"} branding: app_name: "MyApp" logo: light_path: "/branding/logo-light.svg" dark_path: "/branding/logo-dark.svg" colors: light: primary: "hsl(212, 65%, 35%)" ring: "hsl(212, 65%, 35%)" primary-foreground: "hsl(0 0% 98%)" dark: primary: "hsl(212, 65%, 45%)" ring: "hsl(212, 65%, 35%)" primary-foreground: "hsl(0 0% 98%)" ``` ## Mounting Branding Assets For image assets like logos and backgrounds, you need to mount a volume to the Pangolin container to make them accessible to the application: ```yaml title="docker-compose.yml" theme={"theme":"gruvbox-light-hard"} volumes: - ./config:/app/config - ./branding:/app/public/branding ``` Your folder structure should look like this: ``` . ├── config/ │ ├── config.yml │ └── privateConfig.yml └── branding/ ├── logo-light.svg ├── logo-dark.svg └── favicon.ico ``` ## Reference All branding configuration options are optional. Only specify the sections you want to customize. ### Application Name The name of your application that appears in various places throughout the UI. **Example**: `"MyApp"` ### Favicon To customize the favicon, mount your favicon to `/app/public/favicon.ico` in the container. ```yaml title="docker-compose.yml" theme={"theme":"gruvbox-light-hard"} volumes: - ./config:/app/config - ./branding/favicon.ico:/app/public/favicon.ico ``` ### Background Image Path to a custom background image used on authentication pages. **Example**: `"/branding/backgrounds/bg.png"` Requires mounting a volume to `/app/public`. ### Colors Custom color scheme that overrides the default Tailwind classes for shadcn components. Colors can be provided in OKLCH or HSL format for example and apply to both light and dark modes. **Color Reference**: Available color properties include `background`, `foreground`, `card`, `card-foreground`, `popover`, `popover-foreground`, `primary`, `primary-foreground`, `secondary`, `secondary-foreground`, `muted`, `muted-foreground`, `accent`, `accent-foreground`, `destructive`, `destructive-foreground`, `border`, `input`, `ring`, `radius`, and `chart-1` through `chart-5`. **Example**: ```yaml theme={"theme":"gruvbox-light-hard"} colors: light: primary: "hsl(212, 65%, 35%)" background: "hsl(212, 65%, 35%)" foreground: "hsl(0 0% 98%)" dark: primary: "hsl(212, 65%, 45%)" background: "hsl(212, 65%, 35%)" foreground: "hsl(0 0% 98%)" ``` For a complete list of Tailwind color classes used by shadcn components, refer to the [shadcn/ui documentation](https://ui.shadcn.com/docs/theming). ### Logo Logo configuration for different parts of the application. Path to the logo image used in light mode. **Example**: `"/branding/logo-light.svg"` Path to the logo image used in dark mode. **Example**: `"/branding/logo-dark.svg"` Logo dimensions for authentication pages. Logo width in pixels. **Example**: `200` Logo height in pixels. **Example**: `80` Logo dimensions for the navigation bar. Logo width in pixels. **Example**: `150` Logo height in pixels. **Example**: `60` ### Footer Custom footer links displayed at the bottom of the page. **Example**: ```yaml theme={"theme":"gruvbox-light-hard"} footer: - text: "Privacy Policy" href: "https://example.com/privacy" - text: "Terms of Service" href: "https://example.com/terms" ``` The display text for the footer link. The URL or path for the footer link. ### Hide Auth Page Footer Hide the horizontal footer text that appears in the bottom layout of all authentication pages. **Default**: `false` ### Login Page Custom text for the login page. The subtitle text displayed below the title. **Example**: `"Sign in to continue to your account"` ### Signup Page Custom text for the signup page. The subtitle text displayed below the title. **Example**: `"Join us and get started today"` ### Resource Auth Page Configuration for the resource authentication page shown to users when accessing protected resources. Whether to display the logo on the resource auth page. **Default**: `true` Whether to hide the "Powered by Pangolin" text on the resource auth page. **Default**: `false` The main title text displayed on the resource auth page. **Example**: `"Access Restricted"` The subtitle text displayed below the title. **Example**: `"Please sign in to view this resource"` ### Emails Email customization settings. Custom signature appended to sent emails. **Example**: `"Best regards,\nThe MyApp Team"` Use `\n` for line breaks in the signature. Email color scheme. Primary color for email elements. **Example**: `"oklch(0.6717 0.1946 41.93)"` ## Applying Changes After creating or modifying `privateConfig.yml`, you must restart the Pangolin container for changes to take effect. # Archiving & Blocking Source: https://docs.pangolin.net/manage/clients/archiving-blocking Manage clients and block unwanted connections
## Blocking Devices To block a device that has been compromised or lost: 1. Navigate to the device in your client list 2. Click the Action Menu (three dots) 3. Select **Block** When you block a device, it is immediately moved to a restricted list and loses access to all resources or the ability to connect. ## Archiving Devices Devices cannot be permanently deleted from Pangolin. Instead, you can archive devices to remove them from your active client list. To archive a device: 1. Navigate to the device in your client list 2. Click the Action Menu (three dots) 3. Select **Archive** ### Why Archive Instead of Delete? Pangolin maintains a permanent audit trail of all devices that have accessed your resources. This ensures: * Block rules remain effective even for archived devices * Security logs and access history are preserved Archiving keeps your interface clean by hiding inactive or duplicate devices while maintaining the integrity of your security records. ## Filtering You can filter your client list to show only active, blocked, or archived devices. Client List Filters # Client Logs Source: https://docs.pangolin.net/manage/clients/client-logs View Pangolin client logs on Windows, Mac, Linux, Android, and iOS
How you access client logs depends on the platform. Use the section for your client type below. ## Windows You can view logs in the Pangolin client GUI under **Preferences > Logs**. You can also open the log file directly. The default location is: ``` C:\ProgramData\pangolin\logs\pangolin.log ``` To tail the log in PowerShell: ```powershell theme={"theme":"gruvbox-light-hard"} Get-Content "C:\ProgramData\pangolin\logs\pangolin.log" -Tail 0 -Wait ``` ## Mac Mac uses the Apple system logger. To stream Pangolin logs: ```bash theme={"theme":"gruvbox-light-hard"} log stream --predicate 'subsystem == "net.pangolin.Pangolin" OR subsystem == "net.pangolin.Pangolin.PacketTunnel"' --level debug --style compact ``` ## CLI / Linux The default log file location is: ``` ~/.config/pangolin/logs/client.log ``` If you run `pangolin up` in detached mode, you can tail logs with: ```bash theme={"theme":"gruvbox-light-hard"} pangolin logs client -f -n 10 ``` This example follows new log lines and shows the last 10 rows. Alternatively, run in attached mode to see logs in the current terminal session: ```bash theme={"theme":"gruvbox-light-hard"} sudo pangolin up --attach ``` ## Android View logs within the app under **Preferences > Logs**. ## iOS Because of how iOS handles system logging, attach the iPhone to a Mac and use Console.app to view logs. In Console.app, add a **Subsystem** filter for: ``` net.pangolin.Pangolin ``` # Configure Clients Source: https://docs.pangolin.net/manage/clients/configure-client Configure Olm for connecting to Pangolin clients
## GUI Clients (Mac, Windows, Android, iOS/iPadOS) Each respective client has a preferences window with all currently available configuration parameters. In your desktop client, click the menu bar or system tray icon, select "More" in the menu, and click "Preferences". In the mobile apps, navigate to the "Settings" screen. To troubleshoot connection or configuration issues, see [Client Logs](/manage/clients/client-logs) for how to view logs on each platform. ## Preferences The following preferences control how your client handles DNS resolution and network routing. Understanding these settings helps you configure Pangolin to work best with your network setup. #### Enable Aliases (Override DNS) When enabled, the client uses custom DNS servers to resolve internal resources and aliases. This overrides your system's default DNS settings. Queries that cannot be resolved as a Pangolin resource will be forwarded to your configured Upstream DNS Server. **When to use it**: This is required if you use aliases on resources in Pangolin. Aliases are friendly domain names assigned to private resources. Pangolin resolves these alias addresses over a private DNS server running in your client. **How it works**: The client loops back to itself to resolve the alias. This is why you may see your DNS server as an unfamiliar address (often like `100.90.128.x`) when this is enabled. When a request doesn't resolve to a Pangolin resource and is bound for another website (like `google.com`), it falls back to your configured upstream DNS server. #### DNS Over Tunnel When enabled, DNS queries are routed through the tunnel for remote resolution. To ensure queries are tunneled correctly, you must define the DNS server as a Pangolin resource and enter its address as an Upstream DNS Server. **When to use it**: Tunnel DNS is used when you want to send all DNS queries over the tunnel to a private resource made available in Pangolin. For example, if you host a DNS server like Pi-hole, you could define a private resource for Pi-hole on your remote network. Then in the Pangolin client, you would enable Tunnel DNS and set the host of the Pi-hole private resource as the tunnel DNS server. **How it works**: When a request needs to be resolved, Pangolin sends it over the tunnel to the site of the private resource with your DNS server. You must enable DNS Over Tunnel and also set the upstream DNS server to your private DNS server. This requires aliases "override DNS" to be enabled as well. This is because the client must take control of your DNS settings to route queries through the tunnel to your private DNS server. You cannot use an alias name for your DNS server. It must be the IP address of the resource. This is because it's pointing to the DNS server, so the DNS server can't resolve itself. #### Primary Upstream DNS This is the DNS server used to resolve queries that are not bound to a Pangolin alias when Override DNS or DNS Over Tunnel is enabled. When left blank, **System DNS** is used. This pulls the existing configured system DNS settings and applies them to the tunnel. If Tunnel DNS is enabled and System DNS is used, requests will likely fail if the DNS server is not accessible over the tunnel. #### Secondary Upstream DNS This is a fallback DNS server used to resolve queries that are not bound to a Pangolin alias when the primary server is unavailable. Ordering and priority of the server is not guaranteed, but it provides redundancy for DNS resolution. When left blank, **System DNS** is used, same as Primary Upstream DNS. #### Match Domains By default, when match domains are not set, all DNS queries are sent to the configured upstream DNS server. Match domains let you whitelist which domains should be sent to the upstream DNS server. When match domains are set, only matching queries go to upstream DNS; all other requests use the system's DNS servers. **When to use it**: When you have a private or corporate DNS server for specific domains (for example, `*.proxy.internal` or `corp.example.com`) and want everything else resolved by the system DNS as usual. **How it works**: With no match domains configured, every query is forwarded to your Upstream DNS Server. With match domains set, only queries that match the list are forwarded upstream; the rest use the system's default DNS servers. #### MTU You can set the maximum transmission unit (MTU) for the client’s internal WireGuard interface. This value is client-wide: every site the client connects to must use the same MTU on the site (Newt) side, or you can see fragmentation, failed handshakes, or unstable tunnels. See the **mtu** option on [Configure Sites](/manage/sites/configure-site) and set the same value on each of those sites. Changing MTU is advanced and not recommended for most users. Only change it when you have a specific, well-understood reason (for example, a constrained network path or a requirement from your infrastructure team). If you do change it, you must update every connected site to the identical value. ## Windows Client (Advanced) On Windows, the Pangolin GUI reads configuration from two `pangolin.json` files: * User config: `%LOCALAPPDATA%\Pangolin\pangolin.json` (for example, `C:\Users\USER\AppData\Local\Pangolin\pangolin.json`) * Global config: `%ProgramData%\Pangolin\pangolin.json` Most keys in the `Config` object below can be set in either file. If the same key exists in both places, the user config value overrides the global value. This lets administrators define global defaults while still allowing per-user overrides when needed. Keys marked **Global only** must be set in `%ProgramData%\Pangolin\pangolin.json`; restart the Pangolin manager/UI after changing them. JSON configuration for the Windows Pangolin client stored in `pangolin.json`. When true, matches the **Enable Aliases (Override DNS)** preference and lets the client take over DNS resolution for Pangolin resources. When true, matches the **DNS Over Tunnel** preference and sends DNS queries through the Pangolin tunnel. Primary upstream DNS server used when override/tunnel DNS is enabled. Optional secondary upstream DNS server used as a fallback when the primary is unavailable. Optional whitelist of domains sent to the configured upstream DNS server. When unset, all queries go to upstream DNS. When set, only matching queries use your Upstream DNS Server; all other requests use the system's DNS servers. Supports wildcards such as `*.proxy.internal`. When set, skips the deployment option screen during login; all login flows start directly with this server URL. Optional path appended to the server URL for authentication, for example `/auth/org/my-org` to always send users to a specific organization or branded login page. Most deployments should leave this unset. When true, hides and disables the settings form in the GUI so users cannot change these values themselves. When true, opens the Status tab immediately after clicking Connect so users can watch connection feedback while the tunnel is starting. MTU for the internal WireGuard interface. Changing this is advanced and not recommended unless you have a clear reason; if you set a non-default value, configure the same MTU on every site this client connects to—see [Configure Sites](/manage/sites/configure-site). When true, prioritizes local routes over remote ones by adding an arbitrary metric to the WireGuard routes. This is useful when you want to ensure that local network traffic (for example, to a printer or NAS) is not routed through the Pangolin tunnel. **Global only.** When true, periodically check for updates in the background. When false, automatic checks are off; users can still use **Check for Updates** unless that button is also disabled. If omitted, the default is `true`. Enabling checks surfaces the update UI when a new version exists (tray “Pangolin Update Available” and the update prompt). Intended for org admins / MDM so config is the source of truth. **Global only.** How often automatic checks run, in seconds. Values below `3600` (1 hour) are clamped to `3600`. Only matters when `autoUpdateChecksEnabled` is true. If omitted, the default is `86400` (24 hours). The client applies a small amount of jitter around this interval. **Global only.** When true, show **Check for Updates** in the system tray More menu. When false, hide that menu item. If omitted, the default is `true`. Manual checks always perform a live network lookup when the button is used. This is independent of `autoUpdateChecksEnabled`. **Global only.** Controls client log verbosity. Supported values include `debug` and `info`. If omitted, the default is `info`. As a system administrator, you can script placing `pangolin.json` in `%ProgramData%\Pangolin\` to set global defaults, and/or in each user's `%LOCALAPPDATA%\Pangolin\` folder for per-user overrides and targeted rollout behavior. For enterprise customers, contact us if you need a custom MSI installer with baked-in configuration; we can maintain custom installers as an add-on to your enterprise license. ## Mac Client (Advanced) On Mac, the Pangolin GUI reads configuration from `~/Library/Application Support/Pangolin/pangolin.json`. Restart Pangolin after editing for changes to apply. JSON configuration for the Mac Pangolin client stored in `pangolin.json`. When true, matches the **Enable Aliases (Override DNS)** preference and lets the client take over DNS resolution for Pangolin resources. When true, matches the **DNS Over Tunnel** preference and sends DNS queries through the Pangolin tunnel. Primary upstream DNS server used when override/tunnel DNS is enabled. Optional secondary upstream DNS server used as a fallback when the primary is unavailable. Optional whitelist of domains sent to the configured upstream DNS server. When unset, all queries go to upstream DNS. When set, only matching queries use your Upstream DNS Server; all other requests use the system's DNS servers. Supports wildcards such as `*.proxy.internal`. MTU for the internal WireGuard interface. Changing this is advanced and not recommended unless you have a clear reason; if you set a non-default value, configure the same MTU on every site this client connects to. See [Configure Sites](/manage/sites/configure-site). When true, periodically check for updates in the background. When false, automatic checks are off; users can still use **Check for Updates**. If omitted, the client default applies (may prompt the user on second launch). Enabling checks without `autoDownloadUpdatesEnabled` still surfaces the update UI when a new version exists. Intended for admin / MDM provisioning so config stays the source of truth over user toggles. When true, download updates silently when found and stage install for quit/relaunch. When false, show the normal update dialog instead of silent download. Silent download does not replace the running app mid-session; the update installs on quit (and relaunches), so long-running menu bar sessions may keep a staged update until quit. If omitted, the default is `false`. How often automatic checks run, in seconds. Values below `3600` (1 hour) are clamped to `3600`. Only matters when `autoUpdateChecksEnabled` is true. If omitted, the default is `86400` (24 hours). ## Android Battery Optimization To ensure Pangolin functions correctly in the background on Android devices, it's recommended to disable battery optimization for the app. This prevents the operating system from restricting its background activities, which could lead to disconnections. 1. Open the **Settings** app on your Android device. 2. Navigate to **Apps & notifications** (or simply **Apps** on some devices). 3. Find and select the Pangolin app from the list of installed apps. 4. Tap on **App battery usage**. 5. Select **Allow background usage** and enable if disabled. 6. From the options menu, choose **Unrestricted**. Android Battery Optimization Settings ## Pangolin CLI Refer to the [documentation in the official repository](https://github.com/fosrl/cli/blob/main/docs/pangolin.md) for the available commands, default values, and more. ## Olm (Advanced, Deprecated) We recommend using the Pangolin CLI for both user and machine clients if you're looking for a CLI interface. Olm is the underlying client for the Pangolin CLI. Olm is a command-line client for connecting machine clients in Pangolin. You can configure it using command-line flags, environment variables, or a configuration file. Expand the section below to view all available configuration options. ### Flags Olm ID generated by Pangolin to identify the client. **Example**: `31frd0uzbjvp721` A unique secret used to authenticate the client ID with the websocket. **Example**: `h51mmlknrvrwv8s4r1i210azhumt6isgbpyavxodibx1k2d6` Keep this secret private and secure. It's used for authentication. The endpoint where the Pangolin server resides for websocket connections. **Example**: `https://pangolin.example.com` Organization ID to connect to. User authentication token. MTU for the internal WireGuard interface. **Default**: `1280` DNS server to use to resolve the endpoint. **Default**: `8.8.8.8` Upstream DNS server(s), comma-separated. **Default**: `8.8.8.8:53` FQDN wildcard patterns (using `*` and `?` wildcards, comma-separated, e.g. `*.proxy.internal,*.host-0?.autoco.internal`) to check against local records/upstream DNS. Queries for domains that don't match any pattern are sent directly to the host's own system DNS servers instead of being resolved as Pangolin resources. **Default**: (empty, matches every domain) The log level to use for Olm output. **Options**: `DEBUG`, `INFO`, `WARN`, `ERROR`, `FATAL` **Default**: `INFO` Interval for pinging the server. **Default**: `3s` Timeout for each ping. **Default**: `5s` Name of the WireGuard interface. **Default**: `olm` Enable API server for receiving connection requests. **Default**: `false` HTTP server address (e.g., ':9452'). When unset, the HTTP API is not started and the socket API (see `socket-path`) is used instead. **Default**: (not set) Unix socket path (or named pipe on Windows). **Default**: `/var/run/olm.sock` (Linux/macOS) or `olm` (Windows) Disable hole punching. **Default**: `false` When enabled, the client uses custom DNS servers to resolve internal resources and aliases. This overrides your system's default DNS settings. Queries that cannot be resolved as a Pangolin resource will be forwarded to your configured Upstream DNS Server. **Default**: `true` When enabled, DNS queries are routed through the tunnel for remote resolution. To ensure queries are tunneled correctly, you must define the DNS server as a Pangolin resource and enter its address as an Upstream DNS Server. **Default**: `false` Optional comma-separated whitelist of domains sent to the configured upstream DNS server. When unset, all queries go to upstream DNS. When set, only matching queries use your Upstream DNS Server; all other requests use the system's DNS servers. Supports wildcards such as `*.proxy.internal`. Disable relay connections. **Default**: `false` When true, prioritizes local routes over remote ones by adding an arbitrary metric to the WireGuard routes. This is useful when you want to ensure that local network traffic (for example, to a printer or NAS) is not routed through the Pangolin tunnel. **Default**: `false` ### Environment Variables All CLI arguments can be set using environment variables as an alternative to command line flags. Environment variables are particularly useful when running Olm in containerized environments. When both environment variables and CLI arguments are provided, CLI arguments take precedence. Endpoint of your Pangolin server (equivalent to `--endpoint`) Olm ID generated by Pangolin (equivalent to `--id`) Olm secret for authentication (equivalent to `--secret`) Organization ID to connect to (equivalent to `--org`) User authentication token (equivalent to `--user-token`) MTU for the internal WireGuard interface (equivalent to `--mtu`) **Default**: `1280` DNS server to use to resolve the endpoint (equivalent to `--dns`) **Default**: `8.8.8.8` Upstream DNS server(s), comma-separated (equivalent to `--upstream-dns`) **Default**: `8.8.8.8:53` FQDN wildcard patterns, comma-separated (equivalent to `--match-domains-dns`) **Default**: (empty, matches every domain) Log level (equivalent to `--log-level`) **Default**: `INFO` Interval for pinging the server (equivalent to `--ping-interval`) **Default**: `3s` Timeout for each ping (equivalent to `--ping-timeout`) **Default**: `5s` Name of the WireGuard interface (equivalent to `--interface`) **Default**: `olm` Enable API server for receiving connection requests (equivalent to `--enable-api`) Set to "true" to enable **Default**: `false` HTTP server address (equivalent to `--http-addr`) **Default**: (not set) Unix socket path or Windows named pipe (equivalent to `--socket-path`) **Default**: `/var/run/olm.sock` (Linux/macOS) or `olm` (Windows) Disable hole punching (equivalent to `--disable-holepunch`) Set to "true" to disable **Default**: `false` Override system DNS settings (equivalent to `--override-dns`) Set to "true" to enable **Default**: `true` Route DNS queries through the tunnel (equivalent to `--tunnel-dns`) Set to "true" to enable **Default**: `false` Optional whitelist of domains sent to the configured upstream DNS server (equivalent to `--match_domains_dns`). When unset, all queries go to upstream DNS. When true, prioritizes local routes over remote ones by adding an arbitrary metric to the WireGuard routes. This is useful when you want to ensure that local network traffic (for example, to a printer or NAS) is not routed through the Pangolin tunnel. **Default**: `false` Disable relay connections (equivalent to `--disable-relay`) Set to "true" to disable **Default**: `false` Set to the location of a JSON file to load secret values ### Loading secrets from files You can use `CONFIG_FILE` to define a location of a config file to store the credentials between runs. ``` $ cat ~/.config/olm-client/config.json { "id": "spmzu8rbpzj1qq6", "secret": "f6v61mjutwme2kkydbw3fjo227zl60a2tsf5psw9r25hgae3", "endpoint": "https://app.pangolin.net", "org": "", "userToken": "", "mtu": 1280, "dns": "8.8.8.8", "upstreamDNS": ["8.8.8.8:53"], "matchDomainsDNS": [], "interface": "olm", "logLevel": "INFO", "enableApi": false, "httpAddr": "", "socketPath": "/var/run/olm.sock", "pingInterval": "3s", "pingTimeout": "5s", "disableHolepunch": false, "overrideDNS": true, "tunnelDNS": false, "disableRelay": false, "tlsClientCert": "", "preferLocalRoutes": false } ``` This file is also written to when olm first starts up. So you do not need to run every time with --id and secret if you have run it once! Default locations: * **macOS**: `~/Library/Application Support/olm-client/config.json` * **Windows**: `%PROGRAMDATA%\olm\olm-client\config.json` * **Linux/Others**: `~/.config/olm-client/config.json` ### API Olm can be started with a HTTP or socket API to configure and manage it. See the [API documentation](https://github.com/fosrl/olm/blob/main/API.md) for more details. # Client Credentials Source: https://docs.pangolin.net/manage/clients/credentials Understanding how client credentials work and how they can be rotated & regenerated
## Understanding Credentials Every machine client is provisioned with a unique identifier (ID), secret, and endpoint. The client uses the combination of these three to establish a secure, encrypted connection to the server. User devices use a special combination of credentials and temporary session tokens tied to the user account. Therefore, these credentials are obscured and can not be regenerated for user devices. To invalidate a user device, the user should logout via the client of choice. ### ID Example: `ln8yqs6w85la5zg` The ID represents the client connection in the system. Every machine client has an ID. This value is not a secret and it is okay if made publically available. ### Secret Example: `tfpwoc580jf1l1glfagix0o97p8kirjogdflqg604n0tr3to` The secret represents the "password" of the client. This secret must match the secret hashed in the system for the relevant ID. This is a *secret*! Only share it with trusted people and be sure to store it safely and securely. When the client connects, it uses this secret as a first handshake with the server. The server then passes temporary session credentials back to the site before it can initiate a websocket connection. Once the websocket connection is established, ephemeral keys are used to establish tunnels using WireGuard. ### Endpoint Example: `https://app.pangolin.net` or `https://pangolin.my-server.com` The endpoint is how the client knows which server to connect to. This is the fully qualified hostname of the Pangolin server (the URL you use to access the dashboard). For Pangolin cloud, the endpoint is `https://app.pangolin.net`. The client uses this endpoint ot establish a websocket connection and receive control messages from the server. ## Rotating and Regenerating Credentials This is an [Enterprise Edition](/self-host/enterprise-edition)-only feature. Client credentials can be regenerated. Regenerating credentials will completely invalidate the previous ID and secret. Use this feature if you have lost the secret and need to reset the credentials, or if you wish to rotate credentials on a regular basis for extra security. To regenerate credentials, visit Clients > Machines > Your Client > Credentials in the Pangolin admin dashboard. ### Regenerate vs. Regenerate and Disconnect Regenerate simply recreates the credentials and invalidates the old ones. The client will remain connected until you restart it with the new credentials. Regenerate and Disconnect recreates the credentials and invalides the old ones. The client will instantly disconnect and will require you to restart it with the new credentials. # Client Fingerprinting Source: https://docs.pangolin.net/manage/clients/fingerprinting A summary of device information that is collected during the connection
## Device Fingerprinting Pangolin clients collect device fingerprinting information and can perform security posture checks to help determine trusted devices. This information is used to enforce security policies, audit device configurations, and monitor compliance with organizational requirements. Snapshots of device information are collected periodically on each platform when clients are connected. ## Collected Fingerprint Information The following device attributes are collected on each device when available: * Username * Hostname * OS version * Kernel version * Architecture * Device model * Serial number ## Available Posture Checks Posture checks are only collected on [Pangolin Cloud](https://app.pangolin.net/auth/signup) and self-hosted [Enterprise Edition](/self-host/enterprise-edition). Posture checks are also collected on each platform; this is device state that could potentially change, such as biometric availability, firewall settings, and other related information if it is available. Posture checks are supported on a per-platform basis. ### Windows | Posture Check | What It Reports | | --------------------- | ----------------------------------------------------------------- | | Hard drive encryption | Whether the system disk and other disks are encrypted (BitLocker) | | Firewall | If the firewall (Windows or third party) is enabled | | Antivirus | If antivirus (Windows or third party) is installed and active | | TPM availability | If a Trusted Platform Module is available | ### macOS | Posture Check | What It Reports | | --------------------------------- | ---------------------------------------- | | Hard drive encryption | Whether the system disk is encrypted | | Biometric configuration | If Touch ID or Face ID is configured | | Firewall | If the macOS firewall is enabled | | System Integrity Protection (SIP) | Whether SIP is active | | Gatekeeper | Whether Gatekeeper is active | | Firewall stealth mode | Whether firewall stealth mode is enabled | | Auto updates | If automatic updates are enabled | ### Linux | Posture Check | What It Reports | | --------------------- | ------------------------------------------------ | | Hard drive encryption | If LUKS devices are detected on the system | | Firewall | Whether UFW, firewalld, or iptables are enabled | | AppArmor | If AppArmor is active | | SELinux | If SELinux is active | | TPM availability | If a Trusted Platform Module device is available | ### iOS No posture checks are currently supported on iOS. ### Android | Posture Check | What It Reports | | --------------------- | ----------------------------------- | | Hard drive encryption | If File-Based Encryption is enabled | # Firewall Quirks Source: https://docs.pangolin.net/manage/clients/firewalls Improve Pangolin direct-connect success behind restrictive firewalls
Most of the time, Pangolin works with existing firewall policies without any special tuning. Newt and Pangolin clients attempt NAT traversal first so traffic can flow directly between client and site. When direct connectivity cannot be established, traffic falls back to relaying through Gerbil. You can [learn more about how holepunching works here](https://pangolin.net/news/nat-holepunching). For background on direct vs relayed paths and how to verify connection type, see [NAT Traversal](/manage/clients/nat-traversal). ## Firewall compatibility and workarounds The table below summarizes common behavior and practical mitigations reported by users. | Firewall platform | Typical behavior with Pangolin | Practical workaround | | ------------------------------------ | ----------------------------------------- | --------------------------------------------------------------------------------------- | | Barracuda | Often relayed | Increase available UDP session capacity; optionally allow a dedicated inbound Newt port | | Check Point | Usually direct | No workaround typically required | | Cisco | Often relayed | Allow required UDP flows and, if needed, open a fixed Newt listening port | | Cisco Umbrella Endpoint Security | Usually relayed | Endpoint filtering commonly prevents direct paths | | Fortinet | Often connects directly | No workaround typically required | | OPNsense | Often relayed by default | Disable hard NAT | | pfSense | Often relayed by default | Disable hard NAT | | Palo Alto Networks | Mixed; may relay without NAT tuning | Use persistent NAT behavior to stabilize UDP mappings | | Sophos | Commonly direct | No workaround typically required | | UniFi Gateways (threat detection on) | Often relayed when threat detection is on | Allow peer-to-peer traffic categories | If your firewall is not listed and connections are consistently relayed, start with: 1. Confirm current path with `pangolin status` (CLI) or client status JSON (`isRelay`). 2. Keep relaying enabled for reliability while testing firewall adjustments. 3. If needed, pin Newt to a fixed listening port with `--port` / `PORT` and forward that port to the Newt host. ## Platform notes ### Barracuda Barracuda environments commonly struggle with simultaneous UDP flows from multiple clients, which can push connections to relay mode. What helps: * Increase the firewall's maximum UDP session capacity [by increasing the max udp parameter](https://campus.barracuda.com/product/cloudgenfirewall/doc/95258827/general-firewall-configuration/?sl=AX5zwzvyOBUvMAhoTe4U\&so=2) * If direct connectivity is still inconsistent, use a fixed Newt listening port and forward it explicitly. ### Check Point Check Point deployments generally allow Pangolin direct connectivity without extra configuration. ### Cisco Cisco firewalls often require additional policy tuning before direct paths become stable. #### Cisco Umbrella Endpoint Security When Cisco Umbrella Endpoint Security is in-path, direct peer connectivity is frequently blocked and relay becomes the normal outcome. ### Fortinet Fortinet environments are often stable for smaller deployments but may shift heavily to relay as concurrent client count grows. ### pfSense pfSense frequently land in relay mode unless inbound UDP mapping behavior is made explicit. Try the following changes: By default, pfSense software rewrites the source port on all outgoing connections to enhance security and prevent direct exposure of internal port numbers. Static port mapping in pfSense involves creating a fixed association between a specific external port number and an internal IP address and port, allowing incoming traffic to be directed to the correct destination within the local network. Locate the Firewall > NAT, Outbound tab. Select Hybrid Outbound NAT rule generation. Select Save. Select ↑ Add to create a new NAT rule to the top of the list. Configure the rule: IPv4+IPv6, for Protocol UDP, source address Any. Check Static Port in the Translation section of the page. Select Save. Select Apply Changes. ### OPNsense OPNsense frequently land in relay mode unless inbound UDP mapping behavior is made explicit. Try the following changes: By default, OPNsense software rewrites the source port on all outgoing connections to enhance security and prevent direct exposure of internal port numbers. Static port mapping in OPNsense involves creating a fixed association between a specific external port number and an internal IP address and port, allowing incoming traffic to be directed to the correct destination within the local network. Go to Firewall > NAT, Outbound tab. Select Hybrid Outbound NAT rule generation. Select Save. Select ↑ Add to create a new NAT rule to the top of the list. Configure the rule to match UDP traffic. Note, for each rule, select the appropriate Address Family (IP version), IPv4 for one and IPv6 for the other. Here is the Markdown table based on the image provided: | Interface | Source | Source Port | Destination | Destination Port | NAT Address | NAT Port | Static Port | Description | | --------- | ------- | ----------- | ----------- | ---------------- | ----------------- | -------- | ----------- | ------------- | | WAN | LAN net | udp/ \* | \* | udp/ \* | Interface address | \* | YES | Pangolin IPv4 | | WAN | LAN net | udp/ \* | \* | udp/ \* | Interface address | \* | YES | Pangolin IPv6 | Check Static Port in the Translation section of the page. Select Save. Select Apply Changes. ### Palo Alto Networks Palo Alto deployments can improve direct path reliability by using persistent NAT translation behavior (dynamic IP and port persistence) so UDP mappings do not churn between destinations. [Take a look at the documentation here](https://docs.paloaltonetworks.com/pan-os/10-1/pan-os-new-features/networking-features/persistent-nat-for-dipp). PAN-OS 11.1.x and later: Persistent Dynamic IP and Port Ensure stability and performance by referring to the list of preferred releases for PAN-OS. PAN-OS 11.1.1 contains the translation type for NAT policies, Persistent Dynamic IP and Port. In this translation type, once a client has sent a packet with a particular source port, the same NAT translated port number will be used for all destinations. This lets Tailscale to predict the port number and establish a direct connection. To use this translation type, go to NAT Policy Rule, select the Translated Packet tab, then select the Translation Type option Persistent Dynamic IP and Port. Earlier PAN-OS releases: Static IP With older PAN-OS releases and the Dynamic IP and Port translation type, every UDP stream will translate to a random UDP port. Opening a specific port will not allow traffic through, and Tailscale cannot predict what port number to try for a direct connection. With older PAN-OS releases it is possible to use the Static IP NAT Policy to enable one device within the protected zone to make direct connections. One might choose a subnet router or other high-traffic node to optimize. In the PAN-OS software create a NAT policy rule with Translation Type set to Static IP, instead of the default setting Dynamic IP And Port. This helps Tailscale figure out how to get packets through the NAT and establish direct connections. ### Sophos Sophos gateways usually work well with Pangolin's direct-connect flow under default NAT behavior. If relay usage rises unexpectedly, verify no extra IPS/inspection rule set is rewriting UDP sessions. ### UniFi Gateways On UniFi networks with threat detection enabled, peer-to-peer categories can interfere with hole punching. In UniFi Network version 9.0.107 and earlier, select to Settings, Firewall & Security, Edit threat categories, and uncheck P2P. In UniFi Network version 9.0.108 and later, select to Network, Security, Protection, Peer to Peer and Dark Web, and uncheck P2P. ## Verify improvements After each firewall change, validate results from a client: 1. Reconnect the site/client pair. 2. Run `pangolin status` (or check JSON status in GUI clients). 3. Confirm `RELAY=false` / `isRelay: false` for sessions expected to be direct. If direct paths still fail, keep relay enabled and continue iterating with narrower firewall adjustments rather than broad allow-all rules. # Install Clients Source: https://docs.pangolin.net/manage/clients/install-client Install native clients for Mac, Windows, and Linux
## Windows * [Pangolin for Windows Installer](https://pangolin.net/downloads/windows) - This is the official page to download the latest installer file for Windows. * [All Versions](https://github.com/fosrl/windows/releases) - The releases section of this repository contains release notes and download artifacts for the latest version and all older versions. ### Installation Steps 1. **Download and install the Pangolin client** Download and install the Pangolin client using the official .msi installer from the download button above. 2. **Launch Pangolin** Open Pangolin from the Start menu or the shortcut on your Desktop. 3. **Log in with your Pangolin account** Log in on your Pangolin Cloud account or your self-hosted Pangolin instance. * Click the Pangolin icon in the task bar's system tray and select Log in. ## Mac * [Pangolin for macOS Installer](https://pangolin.net/downloads/mac) - This is the official page to download the latest installer file for macOS. * [All Versions](https://github.com/fosrl/apple/releases) - The releases section of this repository contains release notes and download artifacts for the latest version and all older versions. ### Installation Steps 1. **Download and install the Pangolin client** Download and install the Pangolin client using the official .dmg installer from the download button above. * Open the downloaded .dmg file * Drag and drop Pangolin.app into your Applications folder 2. **Launch Pangolin** Open Pangolin from your Applications folder. 3. **Install the VPN configuration** Follow the Pangolin onboarding flow, which will guide you to install the Pangolin VPN configuration. * Select Open System Settings on startup when it asks to install a network extension. * In System Settings, under General > Login Items & Extension > By Category > Network Extensions, ensure that Pangolin.app is toggled on. * Select Allow when Pangolin asks to add a VPN configuration. 4. **Log in with your Pangolin account** Log in on your Pangolin Cloud account or your self-hosted Pangolin instance. * Click the Pangolin icon in the menu bar and select Log in. ## iOS/iPadOS * [Pangolin on the App Store](https://apps.apple.com/us/app/pangolin-client/id6757407406) - This is the official page to download the latest Pangolin app for iOS and iPadOS. ### Installation Steps 1. **Download and install the Pangolin app** Download and install the Pangolin app from the App Store using the link above. 2. **Launch Pangolin** Open the Pangolin app from your home screen. 3. **Install the VPN configuration** When prompted, allow Pangolin to add VPN configurations to your device. You may be asked to enter your device passcode or use Face ID/Touch ID to authorize the VPN configuration. 4. **Log in with your Pangolin account** Log in on your Pangolin Cloud account or your self-hosted Pangolin instance. 5. **Connect to Pangolin** Tap the Connect button to establish a VPN connection. ## Android * [Pangolin on Google Play](https://play.google.com/store/apps/details?id=net.pangolin.Pangolin) - This is the official page to download the latest Pangolin app for Android devices. * [All Versions](https://github.com/fosrl/android/releases) - The releases section of this repository contains release notes and download artifacts for the latest version and all older versions. ### Installation Steps 1. **Download and install the Pangolin app** Download and install the Pangolin app from the Google Play Store using the link above. 2. **Launch Pangolin** Open the Pangolin app from your app drawer or home screen. 3. **Log in with your Pangolin account** Log in on your Pangolin Cloud account or your self-hosted Pangolin instance. 4. **Connect to Pangolin** Tap the Connect button to establish a VPN connection. On the first connection, you may be prompted to allow the VPN connection. ## Pangolin CLI (Linux, macOS, Windows) Pangolin CLI is the recommended way to run a client using a command line interface on Mac and Linux. Pangolin CLI can run on Windows, but the CLI VPN functionality is not supported. You can still use Pangolin CLI on Windows for SSH alongside the Windows GUI client. Pangolin CLI supports running as user device with authentication or a machine client. ### Quick Install (Recommended) — Linux and macOS Use this command to automatically install Pangolin CLI. It detects your system architecture automatically and always pulls the latest version, adding `pangolin` to your PATH: ```bash theme={"theme":"gruvbox-light-hard"} curl -fsSL https://static.pangolin.net/get-cli.sh | bash ``` ### Windows Go to [GitHub releases](https://github.com/fosrl/cli/releases) and download the latest **MSI installer** or **EXE** for Windows. ### Manual Download Binaries for all platforms are available in the [GitHub releases](https://github.com/fosrl/cli/releases) for ARM and AMD64 (x86\_64) architectures. Download and install manually: ```bash theme={"theme":"gruvbox-light-hard"} wget -O pangolin "https://github.com/fosrl/cli/releases/download/{version}/pangolin-cli_{architecture}" && chmod +x ./pangolin ``` Replace `{version}` with the desired version and `{architecture}` with your architecture. Check the [release notes](https://github.com/fosrl/cli/releases) for the latest information. ### Installation Steps 1. **Download and install the Pangolin client** Install Pangolin using the installation script: ```bash theme={"theme":"gruvbox-light-hard"} curl -fsSL https://static.pangolin.net/get-cli.sh | bash ``` 2. **Log in with your Pangolin account** Log in on your Pangolin Cloud account or your self-hosted Pangolin instance: ```bash theme={"theme":"gruvbox-light-hard"} pangolin login ``` 3. **Start Pangolin** When logged in as a Pangolin user, connect by running: ```bash theme={"theme":"gruvbox-light-hard"} pangolin up ``` To launch a machine client without logging in, use your client credentials: ```bash theme={"theme":"gruvbox-light-hard"} pangolin up --id {client_id} --secret {client_secret} --endpoint {endpoint_url} --attach ``` The `--attach` flag runs the client in the foreground instead of spawning it as a background process. Pangolin CLI can be installed as a systemd service or run in a container. See the sections below for advanced setups. ### Systemd Service (Pangolin CLI) Create a basic systemd service for Pangolin CLI: ```ini title="/etc/systemd/system/pangolin-cli.service" theme={"theme":"gruvbox-light-hard"} [Unit] Description=Pangolin CLI After=network.target [Service] ExecStart=/usr/local/bin/pangolin up --id {client_id} --secret {client_secret} --endpoint {endpoint_url} --attach Restart=always User=root [Install] WantedBy=multi-user.target ``` Make sure to move the binary to `/usr/local/bin/pangolin` before creating the service. Replace `{client_id}`, `{client_secret}`, and `{endpoint_url}` with your machine client credentials and endpoint. ### Docker (Pangolin CLI) You can run Pangolin CLI with Docker Compose. For example, a service in your `docker-compose.yml` might look like this using environment variables (recommended): ```yaml theme={"theme":"gruvbox-light-hard"} services: pangolin-cli: image: fosrl/pangolin-cli:latest container_name: pangolin-cli restart: unless-stopped network_mode: host cap_add: - NET_ADMIN devices: - /dev/net/tun:/dev/net/tun environment: - PANGOLIN_ENDPOINT=https://app.pangolin.net - CLIENT_ID=5n52gnzfgl3tdox - CLIENT_SECRET=wyael1dhftekp0ii2ni0ym6xczwjnwmucy2vr6u9kgkp8tw9 ``` You can also pass the CLI args to the container: ```yaml theme={"theme":"gruvbox-light-hard"} services: pangolin-cli: image: fosrl/pangolin-cli:latest container_name: pangolin-cli restart: unless-stopped network_mode: host cap_add: - NET_ADMIN devices: - /dev/net/tun:/dev/net/tun command: - up - --id - "5n52gnzfgl3tdox" - --secret - "wyael1dhftekp0ii2ni0ym6xczwjnwmucy2vr6u9kgkp8tw9" - --endpoint - https://app.pangolin.net - --attach ``` **Docker Configuration Notes:** * `network_mode: host` brings the Pangolin CLI network interface to the host system, allowing the WireGuard tunnel to function properly * `cap_add: - NET_ADMIN` is required to grant the container permission to manage network interfaces * `devices: - /dev/net/tun:/dev/net/tun` is required to give the container access to the TUN device for creating WireGuard interfaces ## Olm (Advanced) Olm CLI is the most basic form of a client. All other clients implement Olm under the hood in some form. If you're looking for a CLI interface for a client, we recommend using Pangolin CLI where possible. Olm CLI is mainly only used for machine clients. Though the Pangolin CLI can also be used for machine clients, use Pangolin CLI if you expect to log in as a user. ### Binary Installation (Linux) #### Quick Install (Recommended) Use this command to automatically install Olm. It detects your system architecture automatically and always pulls the latest version, adding Olm to your PATH: ```bash theme={"theme":"gruvbox-light-hard"} curl -fsSL https://static.pangolin.net/get-olm.sh | bash ``` #### Windows If you would like to use Olm on Windows, wintun.dll is required. Please use latest installer from [GitHub releases](https://github.com/fosrl/olm/releases/latest). #### Manual Download Binaries for Linux, macOS, and Windows are available in the [GitHub releases](https://github.com/fosrl/olm/releases) for ARM and AMD64 (x86\_64) architectures. Download and install manually: ```bash theme={"theme":"gruvbox-light-hard"} wget -O olm "https://github.com/fosrl/olm/releases/download/{version}/olm_{architecture}" && chmod +x ./olm ``` Replace `{version}` with the desired version and `{architecture}` with your architecture. Check the [release notes](https://github.com/fosrl/olm/releases) for the latest information. ### Running Olm Run Olm with the configuration from Pangolin: ```bash theme={"theme":"gruvbox-light-hard"} olm \ --id 31frd0uzbjvp721 \ --secret h51mmlknrvrwv8s4r1i210azhumt6isgbpyavxodibx1k2d6 \ --endpoint https://example.com ``` ### Systemd Service Create a basic systemd service: ```ini title="/etc/systemd/system/olm.service" theme={"theme":"gruvbox-light-hard"} [Unit] Description=Olm After=network.target [Service] ExecStart=/usr/local/bin/olm --id 31frd0uzbjvp721 --secret h51mmlknrvrwv8s4r1i210azhumt6isgbpyavxodibx1k2d6 --endpoint https://example.com Restart=always User=root [Install] WantedBy=multi-user.target ``` Make sure to move the binary to `/usr/local/bin/olm` before creating the service! ### Docker You can also run it with Docker compose. For example, a service in your `docker-compose.yml` might look like this using environment vars (recommended): ```yaml theme={"theme":"gruvbox-light-hard"} services: olm: image: fosrl/olm container_name: olm restart: unless-stopped network_mode: host cap_add: - NET_ADMIN devices: - /dev/net/tun:/dev/net/tun environment: - PANGOLIN_ENDPOINT=https://example.com - OLM_ID=31frd0uzbjvp721 - OLM_SECRET=h51mmlknrvrwv8s4r1i210azhumt6isgbpyavxodibx1k2d6 ``` You can also pass the CLI args to the container: ```yaml theme={"theme":"gruvbox-light-hard"} services: olm: image: fosrl/olm container_name: olm restart: unless-stopped network_mode: host cap_add: - NET_ADMIN devices: - /dev/net/tun:/dev/net/tun command: - --id 31frd0uzbjvp721 - --secret h51mmlknrvrwv8s4r1i210azhumt6isgbpyavxodibx1k2d6 - --endpoint https://example.com ``` **Docker Configuration Notes:** * `network_mode: host` brings the olm network interface to the host system, allowing the WireGuard tunnel to function properly * `cap_add: - NET_ADMIN` is required to grant the container permission to manage network interfaces * `devices: - /dev/net/tun:/dev/net/tun` is required to give the container access to the TUN device for creating WireGuard interfaces ### Windows Service On Windows, olm has to be installed and run as a Windows service. When running it with the cli args, it will attempt to install and run the service to function like a cli tool. Minimum Windows version: Windows 10 #### Service Management Commands ``` # Install the service olm.exe install # Start the service olm.exe start # Stop the service olm.exe stop # Check service status olm.exe status # Remove the service olm.exe remove # Run in debug mode (console output) with our without id & secret olm.exe debug # Show help olm.exe help ``` Note running the service requires credentials in `%PROGRAMDATA%\olm\olm-client\config.json`. #### Service Configuration When running as a service, Olm will read configuration from environment variables or you can modify the service to include command-line arguments: 1. Install the service: `olm.exe install` 2. Set the credentials in `%PROGRAMDATA%\olm\olm-client\config.json`. Hint: if you run olm once with --id and --secret this file will be populated! 3. Start the service: `olm.exe start` #### Service Logs When running as a service, logs are written to: * Windows Event Log (Application log, source: "OlmWireguardService") * Log files in: `%PROGRAMDATA%\olm\logs\olm.log` You can view the Windows Event Log using Event Viewer or PowerShell: ```powershell theme={"theme":"gruvbox-light-hard"} Get-EventLog -LogName Application -Source "OlmWireguardService" -Newest 10 ``` ### Gotchas Olm creates a native tun interface. This usually requires sudo / admin permissions. Some notes: * **Windows**: Olm will run as a service. You can use the commands described [Configure Client](/manage/clients/configure-client) to manage it. You can use this to run it in the background if needed! * **LXC containers**: Need to be configured to allow tun access. On Proxmox see below. * **Linux**: May require root privileges or specific capabilities to create tun interfaces. * **macOS**: May require additional permissions for network interface creation. #### LXC Proxmox 1. Create your LXC container. 2. Go to the Resources tab of the container. 3. Select Add. Then select Device Passthrough. 4. On the Add Device prompt, enter dev/net/tun in the Device Path field and select Add. 5. If the container is running, shut it down and start it up again. Once /dev/net/tun is available, the olm can run within the LXC. # NAT Traversal Source: https://docs.pangolin.net/manage/clients/nat-traversal How clients connect on the same network, hole punch through NAT, or relay through Pangolin
When a client connects to a site, Pangolin tries to establish the most direct path possible. If the client and site are on the same local network, they form a direct peer-to-peer connection on that network. Otherwise, the client attempts NAT hole punching before falling back to relaying. ## Same Network Detection When a client and site share the same local network, they detect this and connect peer-to-peer over the LAN. Packets stay on the local network and are not routed out through the internet or your Pangolin server. The connection does not use the relay path when both peers are on the same network. Same-network connections typically offer the lowest latency because traffic never leaves the LAN. Newt collects IP addresses from the host's network interfaces and uses them to send UDP test packets to the client. If Newt runs inside a Docker container, it only sees the container's internal network, so run it on the host itself to ensure it can detect the host's real IP addresses. This detection also works across VLANs—if a client and site are on different VLANs but routing between them is configured correctly, the connection should still succeed. ## NAT Hole Punching NAT hole punching establishes a direct peer-to-peer connection between the client and site, bypassing the need to route traffic through the Pangolin server. The server coordinates the initial connection by helping both peers discover each other's network addresses, allowing them to establish a direct tunnel through their respective NATs and firewalls. For a deeper look at how Pangolin coordinates hole punching between clients and sites, see [NAT Hole Punching in Pangolin](https://pangolin.net/news/nat-holepunching). Direct connections typically offer: * Better performance (speed and bandwidth) * Lower VPS transit costs when you self-host ## Relaying If the site and client are unable to hole punch, they fall back to relaying through your Pangolin server. Clients can relay traffic through a Pangolin server—through Gerbil specifically. Gerbil listens on UDP port 21820 for new WireGuard connections and forwards the packets down the Newt site tunnels to the right peers. This means your connections back to your site do not require firewall config and uses the existing NAT hole punching capabilities of Newt. Relaying is reliable when direct paths are blocked by NAT or firewall rules, but traffic passes through your Pangolin node instead of traveling directly between the client and site. If you use [Pangolin Cloud](https://app.pangolin.net/auth/signup) and want relayed traffic to stay on infrastructure you control, [self-host a remote node](/manage/remote-node/understanding-nodes). Relayed client traffic then traverses your node rather than Pangolin's shared cloud infrastructure. ## Check Whether a Site Is Relayed You can confirm whether a connection is direct or relayed from the client. ### GUI clients In a GUI client (Mac, Windows, Android, or iOS/iPadOS), open **Preferences**, go to the **Status** tab, and switch to the **JSON** view. Under each connected site in the `peers` object, check `isRelay` and `isLocal`: * `isRelay: false` — direct hole-punched connection to the site * `isRelay: true` — traffic is relayed through your Pangolin server * `isLocal: true` — the site is on the same local network as the client * `isLocal: false` — the site is on a different network Example (values obfuscated): ```json theme={"theme":"gruvbox-light-hard"} { "agent": "Pangolin macOS", "connected": true, "orgId": "org_example123", "peers": { "1001": { "connected": true, "endpoint": "203.0.113.10:51820", "isRelay": false, "isLocal": true, "name": "Office Network", "siteId": 1001 }, "1002": { "connected": true, "endpoint": "198.51.100.5:21820", "isRelay": true, "isLocal": false, "name": "Remote Lab", "siteId": 1002 } }, "registered": true, "version": "0.8.4" } ``` In this example, **Office Network** is connected directly (`isRelay: false`) and **Remote Lab** is relayed (`isRelay: true`). ### CLI On Linux or when using [Pangolin CLI](/manage/clients/install-client), run `pangolin status`. The **CONNECTION** column shows whether each site is connected directly (`Direct`) or via relay (`Relay`): ```bash theme={"theme":"gruvbox-light-hard"} pangolin status AGENT VERSION STATUS ORG Pangolin CLI 0.10.1 Connected org_example123 SITE ENDPOINT STATUS LAST SEEN CONNECTION Office Network 203.0.113.10:51820 Connected 1s ago Direct Remote Lab 198.51.100.5:21820 Connected 1s ago Relay Worklab Lab 192.168.1.33:23423 Connected 1s ago Local ``` Use either view when troubleshooting hole punching or verifying that configuration changes took effect. ## Improve Hole Punching Reliability Newt supports NAT traversal to allow clients to connect directly to Newt sites without relaying through the Pangolin server, improving performance and reducing latency. In some environments, depending on the NAT type and firewall, you may need to tweak settings to get optimal connectivity in the firewall itself. See [Firewall Integrations](/manage/clients/firewalls) for Pangolin-specific guidance for common platforms. Another option is to keep Newt listening for client connections on a static port. This allows you to open a specific port in your firewall for Newt client connections instead of random high ports. You can do this by setting the `--port` flag or `PORT` environment variable and then opening this port in your firewall to DNAT to Newt. See [Configure Sites](/manage/sites/configure-site) for the full list of Newt flags and environment variables. ## FAQ The client and site detect that they share a local network and form a direct peer-to-peer connection on the LAN. Packets are not routed out of the network, and the connection does not use the relay path. Hole punching can fail when either side sits behind a restrictive NAT, symmetric NAT, or a firewall that blocks the UDP ports needed for peer discovery. When that happens, the client and site fall back to relaying through your Pangolin server so the tunnel still comes up reliably. **GUI clients:** Open **Preferences**, go to the **Status** tab, and switch to the **JSON** view. Each entry under `peers` includes an `isRelay` field—`false` for direct, `true` when relayed. **CLI:** Run `pangolin status` and check the **RELAY** column for each site. See [Check Whether a Site Is Relayed](#check-whether-a-site-is-relayed) for examples. Yes. Whether the path is direct or relayed, client-to-site traffic is encrypted over WireGuard. Relaying changes the network path, not the encryption of the tunnel itself. Direct hole-punched connections usually offer lower latency and higher throughput because traffic does not pass through your Pangolin node. Relayed connections add an extra hop through Gerbil, which can increase latency and bandwidth use on the node—especially relevant if you self-host and pay for transit. You can disable relaying with `disable-relay` in the client config or `--disable-relay` / `DISABLE_RELAY=true` on CLI clients. If hole punching fails, the client will not fall back to a relay and the site may not connect. See [Configure Clients](/manage/clients/configure-client) for details. Yes. Set `disable-holepunch` in the client config or use `--disable-holepunch` / `DISABLE_HOLEPUNCH=true` on CLI clients to skip hole punching and connect through the relay path. See [Configure Clients](/manage/clients/configure-client) for details. On self-hosted Pangolin, relayed traffic passes through your own Gerbil instance. On [Pangolin Cloud](https://app.pangolin.net/auth/signup), relayed traffic may traverse Pangolin's shared infrastructure unless you [self-host a remote node](/manage/remote-node/understanding-nodes), in which case relayed client traffic uses your node instead. Not always. Many networks hole punch successfully without inbound rules. If punching is unreliable, try [Firewall Integrations](/manage/clients/firewalls) guidance or pin Newt to a static port with `--port` / `PORT` and DNAT that port to Newt on the site host. # Understanding Clients Source: https://docs.pangolin.net/manage/clients/understanding-clients Create a client to connect to your Pangolin network from a remote computer
A client is a way to access resources on sites remotely and privately via a virtual private network. Clients are used with private resources to faciliate zero-trust network access. By default a client does not have access to any hosts on the local network of the site. Admins must explicitly define resources on the site and give specific users and roles access to the resources. Users must log in and connect from a Pangolin client available on [Windows, Mac, Linux, iOS/iPadOS, and Android](/manage/clients/install-client). Machines (automated systems and servers) connect with an ID and secret. ## Client Types There are two types of clients: user devices and machines. * Associated with a user in your Pangolin organization * Requires login to connect (password, 2fa, etc) * Available for download on Mac, Windows, and Linux * Represent a server or automated system instead of a user * Connect with an ID and secret * Available in CLI form with Pangolin CLI ### User Devices A user may download a client for their specific system. Before they can connect, they must select a Pangolin server to authenticate to using their provided Pangolin account. Users can log in as a Pangolin user or with your attached external identity provider. Examples include: * **SSH**: Admins and developers can connect with their client to specific hosts for SSH. * **RDP**: Users can connect to a remote host using familiar remote desktop software. Then, just like in the Pangolin dashboard, a user selects the organization to connect to. Once connected, all resources made available to the user in that organization become available via the tunnel. ### Machines Machine clients are for servers and automated systems that are not associated with a specific user. Examples include: * **CICD**: Access remote resources like a database in an automated deployment pipeline. * **Servers**: Provide a VPS with access to a resource running in a different network. Though you may connect a server via a user account using a CLI client, we recommend you specifically use a machine client. Machine clients authenticate with an ID and secret string. These credentials are passed via arguments into one of the supported Pangolin CLI clients. They can be revoked and rotated. ## Client Modalities Clients connect to sites using NAT hole punching or relaying. A client first attempts a direct peer-to-peer tunnel, then falls back to relaying through your Pangolin server if hole punching fails. See [NAT Traversal](/manage/clients/nat-traversal) for how each modality works, how to check whether a site is relayed, and tips for improving hole punching reliability. # Update Clients Source: https://docs.pangolin.net/manage/clients/update-client Update your installed client to the latest version
## Mac and Windows ### Automatic Updates (Recommended) The desktop clients for Mac and Windows will periodically check for updates in the background. When an update is available, they will request permission to update. However, you can manually check for updates in the menu bar or system tray menu, or by restarting the application. Once you accept the update, these clients will automatically download the latest version and replace itself on your computer. ### Manual Updates * **Mac**: Find the latest version in the [GitHub releases](https://github.com/fosrl/apple/releases). * **Windows**: Find the latest version in the [GitHub releases](https://github.com/fosrl/windows/releases). You can download the latest installer files and restart the installation process to install the latest version. Visit [https://pangolin.net/downloads](https://pangolin.net/downloads) to find the latest official installers for your platform. ## Pangolin CLI Find the latest version in the [GitHub releases](https://github.com/fosrl/cli/releases). ### Automatic Updates (Recommended) If you already have Pangolin CLI installed, use the update command: ```bash theme={"theme":"gruvbox-light-hard"} pangolin update ``` Or you can re-run the installation script: ```bash theme={"theme":"gruvbox-light-hard"} curl -fsSL https://static.pangolin.net/get-cli.sh | bash ``` ### Manual Updates Download the latest binary for your system from [GitHub releases](https://github.com/fosrl/cli/releases) and replace your existing binary. ```bash theme={"theme":"gruvbox-light-hard"} wget -O pangolin "https://github.com/fosrl/cli/releases/download/{version}/pangolin-cli_{architecture}" && chmod +x ./pangolin ``` Replace `{version}` with the desired version and `{architecture}` with your architecture. Check the [release notes](https://github.com/fosrl/cli/releases) for the latest information. # Common API Routes Source: https://docs.pangolin.net/manage/common-api-routes API routes and patterns for the most common actions and flows All routes in this guide require an API key. See [Integration API](/manage/integration-api) for creating and using API keys (Bearer token in the `Authorization` header). Many routes use an organization ID in the path — see [Organization ID](/manage/organizations/org-id) for where to find it. This guide is a **minimal** walkthrough for creating core Pangolin components (sites, resources, targets) via the API. It is not exhaustive — some elements are omitted for simplicity — but everything shown here works and illustrates patterns used elsewhere in the API. For full coverage of endpoints (get, update, delete, list, etc.), use the [Swagger docs](https://api.pangolin.net/v1/docs). ## What is an identifier? In the Pangolin app, sites, public resources, private resources, and clients have an **Identifier**. In the API, this value is called `niceId`. The identifier is a human-readable, organization-unique value you can use when you want something more stable and memorable than a numeric ID. For example, a site might have a numeric `siteId` like `8723` and a `niceId` like `warehouse-west`. Identifiers are especially useful for automation: * In [Blueprints](/manage/blueprints), resource and site keys act as stable identifiers. A target can reference a site by its identifier, such as `site: warehouse-west`. * In the API, responses include both the numeric ID and `niceId` when the object supports identifiers. Some routes can also look up objects by `niceId`, such as `GET /org/{orgId}/site/{niceId}` for sites. Use the numeric ID when an endpoint asks for `siteId`, `resourceId`, or `siteResourceId`. Use `niceId` when an endpoint or blueprint field asks for an identifier. ## Create site This section assumes you're creating a **Newt** site. For all Site endpoints, see [Site API (Swagger)](https://api.pangolin.net/v1/docs/#/Site). **PUT** `/org/{orgId}/site` **Path** * `orgId` (string) — organization ID **Body (Newt)** | Field | Type | Required | Description | | ------ | ------ | -------- | ------------------------- | | `name` | string | Yes | Display name for the site | | `type` | string | Yes | Use `"newt"` | **Example Response** ```json theme={"theme":"gruvbox-light-hard"} { "data": { "siteId": 8723, "niceId": "quiet-lerista-labialis", "name": "My Site", "type": "newt", "online": false, "address": "100.90.128.0", "newtId": "se897rmyjvamy5q", "secret": "ystsahnvqetgx1fhn2udylq0zqgv611zbfp6ya3jvfrprn7q" }, "success": true, "error": false, "message": "Site created successfully", "status": 201 } ``` ### Advanced option: pick-site-defaults Use this when you want to generate some template values and control the site creation. **GET** `/org/{orgId}/pick-site-defaults` Returns values you pass into the create-site endpoint. **Path** * `orgId` (string) — organization ID **Example Response** ```json theme={"theme":"gruvbox-light-hard"} { "data": { "newtId": "jwhk5154mfmos0s", "newtSecret": "8afipi4i79jjbsxjqpkgc0xe2ge143s54oi64mw5567mxgr8", "clientAddress": "100.90.128.0" }, "success": true, "error": false, "message": "Site defaults chosen successfully", "status": 200 } ``` ### Create site **PUT** `/org/{orgId}/site` **Path** * `orgId` (string) — organization ID **Body (Newt)** | Field | Type | Required | Description | | --------- | ------ | -------- | --------------------------------------- | | `name` | string | Yes | Display name for the site | | `address` | string | Yes | From pick-site-defaults `clientAddress` | | `type` | string | Yes | Use `"newt"` | | `newtId` | string | Yes | From pick-site-defaults `newtId` | | `secret` | string | Yes | From pick-site-defaults `newtSecret` | **Example Response** ```json theme={"theme":"gruvbox-light-hard"} { "data": { "siteId": 8723, "niceId": "quiet-lerista-labialis", "name": "My Site", "type": "newt", "online": false, "address": "100.90.128.0/24" "newtId": "se897rmyjvamy5q", "secret": "ystsahnvqetgx1fhn2udylq0zqgv611zbfp6ya3jvfrprn7q" }, "success": true, "error": false, "message": "Site created successfully", "status": 201 } ``` ## Create public HTTP resource You need a **domain ID** before creating a resource. List your org's domains, then create the resource with the chosen domain. For all Resource endpoints, see [Resource API (Swagger)](https://api.pangolin.net/v1/docs/#/Resource). Call the list-domains endpoint to get available domains and their `domainId` values. Call the create-resource endpoint with `http: true` and the `domainId` from step 1. Call the create-target endpoint for each backend (site + ip:port) that should serve traffic for the resource. ### List domains **GET** `/org/{orgId}/domains` Returns all domains for the organization. Use `domainId` from a domain when creating a resource. **Path** * `orgId` (string) — organization ID **Query** (optional) | Param | Type | Default | Description | | -------- | ------ | ------- | --------------------- | | `limit` | number | 1000 | Max domains to return | | `offset` | number | 0 | Pagination offset | **Example Response** ```json theme={"theme":"gruvbox-light-hard"} { "data": { "domains": [ { "domainId": "pg3i1k4lhibhl3i", "baseDomain": "pangolin.net", "verified": true, "type": "ns" }, { "domainId": "q1ngj5341k7oydo", "baseDomain": "bitwarden.pangolin.net", "verified": true, "type": "cname" } ], "pagination": { "total": 2, "limit": 1000, "offset": 0 } }, "success": true, "error": false, "message": "Domains retrieved successfully", "status": 200 } ``` ### Create public HTTP resource **PUT** `/org/{orgId}/resource` **Path** * `orgId` (string) — organization ID **Body (HTTP resource)** | Field | Type | Required | Description | | ----------- | -------------- | -------- | ------------------------------------------- | | `name` | string | Yes | Display name for the resource (1–255 chars) | | `http` | boolean | Yes | Use `true` for public HTTP resource | | `domainId` | string | Yes | From list-domains | | `protocol` | string | Yes | `"tcp"` | | `subdomain` | string \| null | No | See below. | **Subdomain and domain types** Domains come in three types: **ns** | **cname** | **wildcard**. **Wildcard** is only available in self-hosted Pangolin. Pangolin Cloud uses **ns** and **cname** only. * **ns** — You can use the base domain (set `subdomain` to `null`) or set a subdomain (e.g. `my-app` → `my-app.digpangolin.io`). * **cname** — Only the base domain is used; set `subdomain` to `null` (the domain’s `baseDomain` is the FQDN). * **wildcard** — Same as ns for subdomain behavior (self-hosted only). The `subdomain` value is combined with the base domain from `domainId` to form the FQDN. Omit `subdomain` or pass `null` when using the base domain alone. **Example request** ```json theme={"theme":"gruvbox-light-hard"} { "name": "My Resource", "http": true, "subdomain": "my-subdomain", "domainId": "pg3i1k4lhibhl3i", "protocol": "tcp" } ``` **Example Response** ```json theme={"theme":"gruvbox-light-hard"} { "data": { "resourceId": 9942, "niceId": "decent-louisiana-waterthrush", "name": "My Resource", "subdomain": "my-subdomain", "fullDomain": "my-subdomain.pangolin.net", "domainId": "pg3i1k4lhibhl3i" }, "success": true, "error": false, "message": "Http resource created successfully", "status": 201 } ``` ### Add targets to the resource **PUT** `/resource/{resourceId}/target` Add a target (backend) to a resource. Use the numeric `resourceId` from the create-resource response. The target is the site and address (ip + port) that will receive traffic for the resource. For all Target endpoints, see [Target API (Swagger)](https://api.pangolin.net/v1/docs/#/Target). **Path** * `resourceId` (number) — From create-resource response (e.g. `9943`) **Body** | Field | Type | Required | Description | | -------- | ------ | -------- | ------------------------------------------------------------ | | `siteId` | number | Yes | Site that hosts the backend (from create-site or list sites) | | `ip` | string | Yes | Backend IP or hostname | | `port` | number | Yes | Backend port | | `method` | string | Yes | e.g. `"http"` for HTTP resources | **Example request** ```json theme={"theme":"gruvbox-light-hard"} { "ip": "localhost", "port": 8080, "method": "http", "siteId": 5165 } ``` **Example response** ```json theme={"theme":"gruvbox-light-hard"} { "data": { "targetId": 11280, "resourceId": 9942, "siteId": 8723, "ip": "localhost", "method": "http", "port": 8080 }, "success": true, "error": false, "message": "Target created successfully", "status": 201 } ``` ## Create private resource In the API Private Resources are called **site resources**. You need an existing site. For more endpoints, see [API docs (Swagger)](https://api.pangolin.net/v1/docs/#/Resource). ### Create site resource **PUT** `/org/{orgId}/site-resource` **Path** * `orgId` (string) — organization ID **Body** | Field | Type | Required | Description | | -------------------- | --------- | --------------- | --------------------------------------------------------------------------------------------------------------------------------------- | | `name` | string | Yes | Display name (1–255 chars) | | `siteId` | number | Yes | Site that hosts the resource (from create-site or list sites) | | `mode` | string | Yes | `"host"` \| `"cidr"` | | `destination` | string | Yes | For `host`: IP or hostname (e.g. `localhost`). For `cidr`: CIDR (e.g. `10.0.0.0/24`). | | `alias` | string | For host+domain | Alias hostname (e.g. `private-resource.internal`). Required when `destination` is a domain; optional for IP. Must be unique in the org. | | `tcpPortRangeString` | string | Yes | See below. | | `udpPortRangeString` | string | Yes | See below. | | `disableIcmp` | boolean | No | Default `false` | | `authDaemonMode` | string | No | `"site"` \| `"remote"` | | `roleIds` | number\[] | No | Role IDs that can access (default `[]`) | | `userIds` | string\[] | No | User IDs that can access (default `[]`) | **TCP/UDP port range strings:** Control which ports are allowed for the private resource. * **`"*"`** — Allow all ports (common for TCP when you want full access to the host). * **`""`** (empty string) — Allow no ports. Use when you don’t need that protocol (e.g. `udpPortRangeString: ""` if only TCP is used). * **Specific ports or ranges** — Comma-separated list: single ports (e.g. `"80,443"`) or ranges (e.g. `"8000-9000"`). Example: `"80,443,8080-8090"` allows 80, 443, and 8080–8090. Use `tcpPortRangeString` and `udpPortRangeString` independently (e.g. TCP all, UDP none, or vice versa). If you omit `roleIds`/`userIds`, the org admin role is granted access by default. Add IDs to restrict access. **Example request** ```json theme={"theme":"gruvbox-light-hard"} { "name": "My Private Resource", "siteId": 8723, "mode": "host", "destination": "localhost", "alias": "private-resource.internal", "tcpPortRangeString": "*", "udpPortRangeString": "", "disableIcmp": false, "authDaemonMode": "site", "roleIds": [], "userIds": [] } ``` **Example response** ```json theme={"theme":"gruvbox-light-hard"} { "data": { "siteResourceId": 1165, "siteId": 8723, "niceId": "unsung-round-tailed-ground-squirrel", "name": "My Private Resource", "mode": "host", "destination": "localhost", "alias": "private-resource.internal", "tcpPortRangeString": "*", "udpPortRangeString": "", "disableIcmp": false, "authDaemonPort": 22123, "authDaemonMode": "site" }, "success": true, "error": false, "message": "Site resource created successfully", "status": 201 } ``` ## Assign users and roles to a resource (public or private) You can grant access to a **public resource** or a **private (site) resource** by adding roles or users. First list roles and users in the org to get IDs, then call the add endpoints. The Admin role cannot be assigned via these endpoints. ### Get role and user IDs **GET** `/org/{orgId}/roles` — Returns roles in the org. Use `roleId` (number) when adding a role to a resource. Query: `limit`, `offset` (optional). **GET** `/org/{orgId}/users` — Returns users in the org. Use `id` (string) as `userId` when adding a user to a resource. Query: `limit`, `offset` (optional). ### Public resource (HTTP/resources) **POST** `/resource/{resourceId}/roles/add` — **Path:** `resourceId` (number, from create-resource). **Body:** `{ "roleId": number }`. Admin role not allowed. **POST** `/resource/{resourceId}/users/add` — **Path:** `resourceId` (number). **Body:** `{ "userId": string }`. Both return `{ "data": {}, "success": true, "error": false, "message": "...", "status": 201 }`. ### Private resource (site resource) **POST** `/site-resource/{siteResourceId}/roles/add` — **Path:** `siteResourceId` (number, from create site-resource). **Body:** `{ "roleId": number }`. Admin role not allowed. **POST** `/site-resource/{siteResourceId}/users/add` — **Path:** `siteResourceId` (number). **Body:** `{ "userId": string }`. Same response shape as above. Role must belong to the same org as the site resource. For more endpoints (list/remove), see [Resource API](https://api.pangolin.net/v1/docs/#/Resource). # Community Blueprints Source: https://docs.pangolin.net/manage/community-blueprints-repo Community-maintained library of ready-to-use Pangolin Blueprints for self-hosted applications
The Pangolin Community Blueprints repository is a shared library of ready-to-use Docker Compose templates for common self-hosted services, already wired to expose those services through Pangolin. View the repository on GitHub to browse the available blueprints and learn more: [github.com/fosrl/blueprints](https://github.com/fosrl/blueprints)