docs: add new Access Control and Logs documentation pages

- Documented Access Control features (e.g., Device Approvals, Password Rotation, 2FA, Custom Login Pages).
- Added detailed descriptions for Logs & Analytics (Access Logs, Request Logs, Action Logs).
- Included configuration instructions and feature-specific notes for Pangolin Cloud and Enterprise Edition.

Signed-off-by: Stefan Mogeritsch <stefan.mo.co@gmail.com>
This commit is contained in:
2026-03-11 11:24:24 +01:00
parent a70f132fd9
commit aa157e82f8
87 changed files with 13163 additions and 0 deletions
@@ -0,0 +1,163 @@
> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pangolin.net/llms.txt
> Use this file to discover all available pages before exploring further.
# Cloudflare Proxy
<div id="pangolin-toc-cta" className="pangolin-toc-cta-source">
<Card title="Try free on Pangolin Cloud" icon="cloud" href="https://app.pangolin.net/auth/signup" arrow="true" cta="Sign up free">
Fastest way to get started with Pangolin using the hosted control plane. No credit card required.
</Card>
</div>
Pangolin works with Cloudflare proxy (orange cloud) enabled, but requires specific configuration:
<Warning>
**Terms of Service**: Enabling Cloudflare proxy binds you to Cloudflare's terms of service as traffic routes through their network.
</Warning>
### SSL Configuration
**Recommended setup:**
1. **Use wildcard certificates** with DNS-01 challenge
2. **Set SSL/TLS mode to Full (Strict)**
3. **Disable port 80** (not needed with wildcard certs)
<Info>
Pangolin will **not work** with Cloudflare's Full or Automatic SSL/TLS modes. Only Full (Strict) mode is supported.
</Info>
### WireGuard Configuration
Since Cloudflare proxy obscures the destination IP, you must explicitly set your VPS IP in
the [config file](/self-host/advanced/config-file):
```yaml theme={null}
gerbil:
base_endpoint: "YOUR_VPS_IP_ADDRESS" # Required with Cloudflare proxy
```
<Steps>
<Step title="Get your VPS IP">
Find your VPS public IP address:
```bash theme={null}
curl ifconfig.io
```
</Step>
<Step title="Update configuration">
Add the IP to your `config.yml`:
```yaml theme={null}
gerbil:
base_endpoint: "104.21.16.1" # Replace with your actual IP
```
</Step>
<Step title="Restart services">
Restart Pangolin to apply the changes:
```bash theme={null}
docker-compose restart
```
</Step>
</Steps>
### Getting the Real Client IP
Pangolin needs to know the original client IP address for features like rate limiting and logging. When Cloudflare proxy
is enabled, the API server sees Cloudflare's IP instead of the real client IP.
**Badger**, Pangolin's middleware for Traefik, automatically handles Cloudflare proxy IP extraction. Badger versions
1.3.0 and later automatically:
* Trust Cloudflare IP ranges
* Extract the real client IP from the `CF-Connecting-IP` header
* Set `X-Real-IP` and `X-Forwarded-For` headers for downstream services
<Info>
**Automatic Configuration**: Pangolin installer versions 1.14.0 and greater automatically add Badger to all Pangolin routes in Traefik. If you're using a newer installer, no manual configuration is needed.
</Info>
#### Manual Configuration
If you're using an older installer or need to manually configure Badger, add it to your Traefik configuration. Badger
must be applied to all routers that handle Pangolin traffic (API, dashboard, and WebSocket routes):
```yaml title="dynamic_config.yml" theme={null}
http:
middlewares:
badger:
plugin:
badger:
disableForwardAuth: true
routers:
# Next.js router (handles dashboard)
next-router:
rule: "Host(`pangolin.example.com`) && !PathPrefix(`/api/v1`)"
service: next-service
entryPoints:
- websecure
middlewares:
- badger
tls:
certResolver: letsencrypt
# API router (handles /api/v1 paths)
api-router:
rule: "Host(`pangolin.example.com`) && PathPrefix(`/api/v1`)"
service: api-service
entryPoints:
- websecure
middlewares:
- badger
tls:
certResolver: letsencrypt
# WebSocket router
ws-router:
rule: "Host(`pangolin.example.com`)"
service: api-service
entryPoints:
- websecure
middlewares:
- badger
tls:
certResolver: letsencrypt
```
**Why Badger is needed**: When `disableForwardAuth: true` is set, Badger extracts the real client IP from Cloudflare
proxy headers without performing authentication. This is necessary because forward authentication is only needed for
resources controlled by Pangolin, not for the main application routes. However, the main Pangolin containers and APIs
still need the real client IP for proper rate limiting and IP tracking.
#### Pangolin Configuration
Set `trust_proxy: 2` in your Pangolin config file. This tells Pangolin to trust the second-level proxy (Traefik is proxy
1, Cloudflare is proxy 2):
```yaml theme={null}
server:
trust_proxy: 2
```
<Warning>
**Update Badger**: Ensure you're running Badger version 1.3.0 or later to get real IP addresses in logs for Public resources. Update Badger if you're using an older version.
</Warning>
After making these changes, restart both Traefik and Pangolin for the configuration to take effect.
### Troubleshooting
If websockets are not connecting like from newt or clients, ensure that websockets are enabled in Cloudflare:
<Frame>
<img src="https://mintcdn.com/fossorial/VqiOoRUR8g1Tf03J/images/cf_websocket_box.png?fit=max&auto=format&n=VqiOoRUR8g1Tf03J&q=85&s=f107b9da12af22813deca776b8f2551e" width="600" centered data-og-width="1100" data-og-height="247" data-path="images/cf_websocket_box.png" data-optimize="true" data-opv="3" srcset="https://mintcdn.com/fossorial/VqiOoRUR8g1Tf03J/images/cf_websocket_box.png?w=280&fit=max&auto=format&n=VqiOoRUR8g1Tf03J&q=85&s=adbcd090cb9d1c52973a416aec8edcfc 280w, https://mintcdn.com/fossorial/VqiOoRUR8g1Tf03J/images/cf_websocket_box.png?w=560&fit=max&auto=format&n=VqiOoRUR8g1Tf03J&q=85&s=384b68f10a1253ea2ea2b729a6ac857b 560w, https://mintcdn.com/fossorial/VqiOoRUR8g1Tf03J/images/cf_websocket_box.png?w=840&fit=max&auto=format&n=VqiOoRUR8g1Tf03J&q=85&s=c8b28b64b577908e1b8503e08660e517 840w, https://mintcdn.com/fossorial/VqiOoRUR8g1Tf03J/images/cf_websocket_box.png?w=1100&fit=max&auto=format&n=VqiOoRUR8g1Tf03J&q=85&s=f958b0f1d0850a2719c6929e8ca54093 1100w, https://mintcdn.com/fossorial/VqiOoRUR8g1Tf03J/images/cf_websocket_box.png?w=1650&fit=max&auto=format&n=VqiOoRUR8g1Tf03J&q=85&s=71eafa9f2822cec53e703071adddb595 1650w, https://mintcdn.com/fossorial/VqiOoRUR8g1Tf03J/images/cf_websocket_box.png?w=2500&fit=max&auto=format&n=VqiOoRUR8g1Tf03J&q=85&s=a88c0baa21142f8a6abeeaf2b6436199 2500w" />
</Frame>
@@ -0,0 +1,75 @@
> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pangolin.net/llms.txt
> Use this file to discover all available pages before exploring further.
# Clustering for High Availability
<div id="pangolin-toc-cta" className="pangolin-toc-cta-source">
<Card title="Try free on Pangolin Cloud" icon="cloud" href="https://app.pangolin.net/auth/signup" arrow="true" cta="Sign up free">
Fastest way to get started with Pangolin using the hosted control plane. No credit card required.
</Card>
</div>
<Note>
Clustering is only available in [Enterprise Edition](/self-host/enterprise-edition). [Please reach out to us to deploy](https://pangolin.net/talk-to-us).
</Note>
Deploy multiple Pangolin servers enterprise-grade high availability and performance in large deployments.
## Overview
For organizations requiring maximum uptime and performance, Pangolin supports clustered deployments where multiple
server instances work together as a unified system. This architecture enables regional distribution, automatic failover,
and horizontal scaling to handle demanding production workloads.
## How Clustering Works
In a clustered configuration, multiple Pangolin server instances operate together, sharing state and coordinating
through a Postgres database and Valkey server. Each instance can independently serve user requests, manage
authentication, and coordinate with multiple Gerbil instances to support thousands of sites.
### Shared Database Backend
All Pangolin instances connect to a shared PostgreSQL database that stores the system's persistent state - including
user accounts, site configurations, resources, access policies, and organizational settings. This ensures that changes
made through any server instance are immediately available across the entire cluster.
### Real-time State Synchronization
Redis or Valkey provides real-time state synchronization and pub sub between cluster nodes like active sessions,
WebSocket connections, and tunnel status. When a user authenticates or a site connector establishes a connection to one
Pangolin instance, Redis ensures other nodes are aware of these active sessions for failover.
### Tunnel Management with Gerbil
Each Pangolin instance runs alongside its own Gerbil tunnel manager, which handles WireGuard connections to site
connectors. When a site connector needs to establish a tunnel, it can connect to any available Gerbil instance in the
cluster. For public resources, Gerbil instances are aware of all of the other nodes in the network and can route
incoming requests to any other Gerbil to exit through the right site. The distributed architecture ensures that tunnel
connectivity remains available even if individual nodes fail.
## Benefits of Clustering
**High Availability**: Eliminate single points of failure. If one server instance fails, traffic automatically routes to
healthy nodes without user disruption.
**Regional Distribution**: Deploy servers closer to your users and sites across different geographic regions to minimize
latency and improve performance.
**Horizontal Scaling**: Add more server instances to handle increased load as your organization grows, without
architectural changes.
**Zero-Downtime Updates**: Perform rolling updates by taking nodes offline one at a time while others continue serving
traffic.
## Enterprise Support
Clustered deployments require careful planning around database replication, Redis configuration, network topology, and
monitoring. These advanced architectures are available as part of
Pangolin's [Enterprise Edition](/self-host/enterprise-edition) with dedicated support for design, deployment, and
ongoing operations.
For organizations interested in clustering for high availability or regional distribution,
please [contact our enterprise team](https://pangolin.net/talk-to-us) to discuss your requirements and receive
implementation guidance.
@@ -0,0 +1,859 @@
> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pangolin.net/llms.txt
> Use this file to discover all available pages before exploring further.
# Configuration File
> Configure Pangolin using the config.yml file with detailed settings for all components
<div id="pangolin-toc-cta" className="pangolin-toc-cta-source">
<Card title="Try free on Pangolin Cloud" icon="cloud" href="https://app.pangolin.net/auth/signup" arrow="true" cta="Sign up free">
Fastest way to get started with Pangolin using the hosted control plane. No credit card required.
</Card>
</div>
The `config.yml` file controls all aspects of your Pangolin deployment, including server settings, domain configuration,
email setup, and security options. This file is mounted at `config/config.yml` in your Docker container.
## Setting up your `config.yml`
To get started, create a basic configuration file with the essential settings:
Minimal Pangolin configuration:
```yaml title="config.yml" theme={null}
# To see all available options, please visit the docs:
# https://docs.pangolin.net/
gerbil:
start_port: 51820
base_endpoint: "pangolin.example.com" # REPLACE WITH YOUR DOMAIN
app:
dashboard_url: "https://pangolin.example.com" # REPLACE WITH YOUR DOMAIN
log_level: "info"
telemetry:
anonymous_usage: true
domains:
domain1:
base_domain: "example.com" # REPLACE WITH YOUR DOMAIN
cert_resolver: "letsencrypt"
server:
secret: "your-strong-secret" # REPLACE
cors:
origins: ["https://pangolin.example.com"] # REPLACE WITH YOUR DOMAIN
methods: ["GET", "POST", "PUT", "DELETE", "PATCH"]
allowed_headers: ["X-CSRF-Token", "Content-Type"]
credentials: false
flags:
require_email_verification: false
disable_signup_without_invite: true
disable_user_create_org: false
allow_raw_resources: true
```
<Warning>
Generate a strong secret for `server.secret`. Use at least 32 characters with a mix of letters, numbers, and special characters.
If you need to CHANGE the server secret after the server has been started you must use the
`pangctl rotate-server-secret` command to re-encrypt sensitive
data. [Follow docs here](/self-host/advanced/container-cli-tool#rotate-server-secret).
</Warning>
## Reference
This section contains the complete reference for all configuration options in `config.yml`.
### Application Settings
<ResponseField name="app" type="object" required>
Core application configuration including dashboard URL, logging, and general settings.
<Expandable title="App">
<ResponseField name="dashboard_url" type="string" required>
The URL where your Pangolin dashboard is hosted.
**Examples**: `https://example.com`, `https://pangolin.example.com`
This URL is used for generating links, redirects, and authentication flows. You can run Pangolin on a subdomain or root domain.
</ResponseField>
<ResponseField name="log_level" type="string">
The logging level for the application.
**Options**: `debug`, `info`, `warn`, `error`
**Default**: `info`
</ResponseField>
<ResponseField name="save_logs" type="boolean">
Whether to save logs to files in the `config/logs/` directory.
**Default**: `false`
<Note>
When enabled, logs rotate automatically:
* Max file size: 20MB
* Max files: 7 days
</Note>
</ResponseField>
<ResponseField name="log_failed_attempts" type="boolean">
Whether to log failed authentication attempts for security monitoring.
**Default**: `false`
</ResponseField>
<ResponseField name="telemetry" type="object">
Telemetry configuration settings.
<Expandable title="Telemetry">
<ResponseField name="anonymous_usage" type="boolean">
Whether to enable anonymous usage telemetry.
**Default**: `true`
</ResponseField>
</Expandable>
</ResponseField>
<ResponseField name="notifications" type="object">
Notification configuration settings.
<Expandable title="Notification">
<ResponseField name="product_updates" type="boolean">
Whether to enable showing product updates notifications on the UI.
**Default**: `true`
</ResponseField>
<ResponseField name="new_releases" type="boolean">
Whether to enable showing new releases notifications on the UI.
**Default**: `true`
</ResponseField>
</Expandable>
</ResponseField>
</Expandable>
</ResponseField>
### Server Configuration
<ResponseField name="server" type="object" required>
Server ports, networking, and authentication settings.
<Expandable title="Server">
<ResponseField name="external_port" type="integer">
The port for the front-end API that handles external requests.
**Example**: `3000`
</ResponseField>
<ResponseField name="internal_port" type="integer">
The port for the internal private-facing API.
**Example**: `3001`
</ResponseField>
<ResponseField name="next_port" type="integer">
The port for the frontend server (Next.js).
**Example**: `3002`
</ResponseField>
<ResponseField name="integration_port" type="integer">
The port for the integration API (optional).
**Example**: `3003`
</ResponseField>
<ResponseField name="internal_hostname" type="string">
The hostname of the Pangolin container for internal communication.
**Example**: `pangolin`
<Tip>
If using Docker Compose, this should match your container name.
</Tip>
</ResponseField>
<ResponseField name="session_cookie_name" type="string">
The name of the session cookie for storing authentication tokens.
**Example**: `p_session_token`
**Default**: `p_session_token`
</ResponseField>
<ResponseField name="resource_access_token_param" type="string">
Query parameter name for passing access tokens in requests.
**Example**: `p_token`
**Default**: `p_token`
</ResponseField>
<ResponseField name="resource_access_token_headers" type="object">
HTTP headers for passing access tokens in requests.
<Expandable title="Headers">
<ResponseField name="id" type="string">
Header name for access token ID.
**Example**: `P-Access-Token-Id`
</ResponseField>
<ResponseField name="token" type="string">
Header name for access token.
**Example**: `P-Access-Token`
</ResponseField>
</Expandable>
</ResponseField>
<ResponseField name="resource_session_request_param" type="string">
Query parameter for session request tokens.
**Example**: `p_session_request`
**Default**: `p_session_request`
</ResponseField>
<ResponseField name="cors" type="object">
Cross-Origin Resource Sharing (CORS) configuration.
<Expandable title="CORS">
<ResponseField name="origins" type="array of strings">
Allowed origins for cross-origin requests.
**Example**: `["https://pangolin.example.com"]`
</ResponseField>
<ResponseField name="methods" type="array of strings">
Allowed HTTP methods for CORS requests.
**Example**: `["GET", "POST", "PUT", "DELETE", "PATCH"]`
</ResponseField>
<ResponseField name="allowed_headers" type="array of strings">
Allowed HTTP headers in CORS requests.
**Example**: `["X-CSRF-Token", "Content-Type"]`
</ResponseField>
<ResponseField name="credentials" type="boolean">
Whether to allow credentials in CORS requests.
**Default**: `true`
</ResponseField>
</Expandable>
</ResponseField>
<ResponseField name="trust_proxy" type="integer">
Number of proxy headers to trust for client IP detection.
**Example**: `1`
**Default**: `1`
<Tip>
Use `1` if running behind a single reverse proxy like Traefik.
</Tip>
</ResponseField>
<ResponseField name="dashboard_session_length_hours" type="integer">
Dashboard session duration in hours.
**Example**: `720` (30 days)
**Default**: `720`
</ResponseField>
<ResponseField name="resource_session_length_hours" type="integer">
Resource session duration in hours.
**Example**: `720` (30 days)
**Default**: `720`
</ResponseField>
<ResponseField name="secret" type="string" required>
Secret key for encrypting sensitive data.
**Environment Variable**: `SERVER_SECRET`
**Minimum Length**: 8 characters
**Example**: `"d28@a2b.2HFTe2bMtZHGneNYgQFKT2X4vm4HuXUXBcq6aVyNZjdGt6Dx-_A@9b3y"`
<Warning>
Generate a strong, random secret. This is used for encrypting sensitive data and should be kept secure.
If you need to CHANGE the server secret after the server has been started you must use the `pangctl rotate-server-secret` command to re-encrypt sensitive data. [Follow docs here](/self-host/advanced/container-cli-tool#rotate-server-secret).
</Warning>
</ResponseField>
<ResponseField name="maxmind_db_path" type="string">
Path to the MaxMind GeoIP database file for geolocation features.
**Example**: `./config/GeoLite2-Country.mmdb`
<Note>
Used for IP geolocation functionality. Requires a MaxMind GeoLite2 or GeoIP2 database file.
</Note>
</ResponseField>
</Expandable>
</ResponseField>
### Domain Configuration
<ResponseField name="domains" type="object" required>
Domain settings for SSL certificates and routing.
At least one domain must be configured.
It is best to add it in the UI for ease of use or when you want the
domain to *only be present in the org it was created in*.
You should create it in the config file for permanence across installs
and if you want the domain to be present in all orgs.
<Expandable title="Domains">
<ResponseField name="<domain_key>" type="object">
Domain configuration with a unique key of your choice.
<Expandable title="Domain Settings">
<ResponseField name="base_domain" type="string" required>
The base domain for this configuration.
**Example**: `example.com`
</ResponseField>
<ResponseField name="cert_resolver" type="string" required>
The Traefik certificate resolver name.
**Example**: `letsencrypt`
<Note>
This must match the certificate resolver name in your Traefik configuration.
</Note>
</ResponseField>
<ResponseField name="prefer_wildcard_cert" type="boolean">
Whether to prefer wildcard certificates for this domain.
**Example**: `true`
<Tip>
Useful for domains with many subdomains to reduce certificate management overhead.
</Tip>
</ResponseField>
</Expandable>
</ResponseField>
</Expandable>
</ResponseField>
### Traefik Integration
<ResponseField name="traefik" type="object">
Traefik reverse proxy configuration settings.
<Expandable title="Traefik">
<ResponseField name="http_entrypoint" type="string">
The Traefik entrypoint name for HTTP traffic.
**Example**: `web`
<Note>
Must match the entrypoint name in your Traefik configuration.
</Note>
</ResponseField>
<ResponseField name="https_entrypoint" type="string">
The Traefik entrypoint name for HTTPS traffic.
**Example**: `websecure`
<Note>
Must match the entrypoint name in your Traefik configuration.
</Note>
</ResponseField>
<ResponseField name="cert_resolver" type="string">
The default certificate resolver for domains created through the UI.
**Example**: `letsencrypt`
<Note>
This only applies to domains created through the Pangolin dashboard.
</Note>
</ResponseField>
<ResponseField name="prefer_wildcard_cert" type="boolean">
Whether to prefer wildcard certificates for UI-created domains.
**Example**: `true`
<Note>
This only applies to domains created through the Pangolin dashboard.
</Note>
</ResponseField>
<ResponseField name="additional_middlewares" type="array of strings">
Additional Traefik middlewares to apply to resource routers.
**Example**: `["middleware1", "middleware2"]`
<Note>
These middlewares must be defined in your Traefik dynamic configuration.
</Note>
</ResponseField>
<ResponseField name="certificates_path" type="string">
Path where SSL certificates are stored. This is used only with managed Pangolin deployments.
**Example**: `/var/certificates`
**Default**: `/var/certificates`
</ResponseField>
<ResponseField name="monitor_interval" type="integer">
Interval in milliseconds for monitoring configuration changes.
**Example**: `5000`
**Default**: `5000`
</ResponseField>
<ResponseField name="dynamic_cert_config_path" type="string">
Path to the dynamic certificate configuration file. This is used only with managed Pangolin deployments.
**Example**: `/var/dynamic/cert_config.yml`
**Default**: `/var/dynamic/cert_config.yml`
</ResponseField>
<ResponseField name="dynamic_router_config_path" type="string">
Path to the dynamic router configuration file.
**Example**: `/var/dynamic/router_config.yml`
**Default**: `/var/dynamic/router_config.yml`
</ResponseField>
<ResponseField name="site_types" type="array of strings">
Supported site types for Traefik configuration.
**Example**: `["newt", "wireguard", "local"]`
**Default**: `["newt", "wireguard", "local"]`
</ResponseField>
<ResponseField name="file_mode" type="boolean">
Whether to use file-based configuration mode for Traefik.
**Example**: `false`
**Default**: `false`
<Note>
When enabled, uses file-based dynamic configuration instead of API-based updates.
</Note>
</ResponseField>
<ResponseField name="pp_transport_prefix" type="string">
Prefix used for transport-related configurations. References servers transport config in dynamic Traefik file.
**Example**: `pp-transport-v`
**Default**: `pp-transport-v`
</ResponseField>
</Expandable>
</ResponseField>
### Gerbil Tunnel Controller
<ResponseField name="gerbil" type="object" required>
Gerbil tunnel controller settings for WireGuard tunneling.
<Expandable title="Gerbil">
<ResponseField name="base_endpoint" type="string" required>
Domain name included in WireGuard configuration for tunnel connections.
**Example**: `pangolin.example.com`
</ResponseField>
<ResponseField name="start_port" type="integer">
Starting port for WireGuard tunnels.
**Example**: `51820`
</ResponseField>
<ResponseField name="clients_start_port" type="integer">
Starting port for client WireGuard relay and hole punch port.
**Example**: `21820`
</ResponseField>
<ResponseField name="use_subdomain" type="boolean">
Whether to assign unique subdomains to Gerbil exit nodes.
**Default**: `false`
<Warning>
Keep this set to `false` for most deployments.
</Warning>
</ResponseField>
<ResponseField name="subnet_group" type="string">
IP address CIDR range for Gerbil exit node subnets.
**Example**: `10.0.0.0/8`
</ResponseField>
<ResponseField name="block_size" type="integer">
Block size for Gerbil exit node CIDR ranges.
**Example**: `24`
</ResponseField>
<ResponseField name="site_block_size" type="integer">
Block size for site CIDR ranges connected to Gerbil.
**Example**: `26`
</ResponseField>
</Expandable>
</ResponseField>
### Organization Settings
<ResponseField name="orgs" type="object">
Organization network configuration settings.
<Expandable title="Organizations">
<ResponseField name="block_size" type="integer">
Block size for organization CIDR ranges.
**Example**: `24`
**Default**: `24`
<Note>
Determines the subnet size allocated to each organization for network isolation.
</Note>
</ResponseField>
<ResponseField name="subnet_group" type="string">
IP address CIDR range for organization subnets.
**Example**: `100.90.128.0/24`
**Default**: `100.90.128.0/24`
<Note>
Base subnet from which organization-specific subnets are allocated.
</Note>
</ResponseField>
</Expandable>
</ResponseField>
### Rate Limiting
<ResponseField name="rate_limits" type="object">
Rate limiting configuration for API requests.
<Expandable title="Rate Limits">
<ResponseField name="global" type="object">
Global rate limit settings for all external API requests.
<Expandable title="Global">
<ResponseField name="window_minutes" type="integer">
Time window for rate limiting in minutes.
**Example**: `1`
</ResponseField>
<ResponseField name="max_requests" type="integer">
Maximum number of requests allowed in the time window.
**Example**: `100`
</ResponseField>
</Expandable>
</ResponseField>
<ResponseField name="auth" type="object">
Rate limit settings specifically for authentication endpoints.
<Expandable title="Auth Rate Limits">
<ResponseField name="window_minutes" type="integer">
Time window for authentication rate limiting in minutes.
**Example**: `1`
**Default**: `1`
</ResponseField>
<ResponseField name="max_requests" type="integer">
Maximum number of authentication requests allowed in the time window.
**Example**: `10`
**Default**: `500`
<Note>
Consider setting this lower than global limits for security.
</Note>
</ResponseField>
</Expandable>
</ResponseField>
</Expandable>
</ResponseField>
### Email Configuration
<ResponseField name="email" type="object">
SMTP settings for sending transactional emails.
<Expandable title="Email">
<ResponseField name="smtp_host" type="string">
SMTP server hostname.
**Example**: `smtp.gmail.com`
</ResponseField>
<ResponseField name="smtp_port" type="integer">
SMTP server port.
**Example**: `587` (TLS) or `465` (SSL)
</ResponseField>
<ResponseField name="smtp_user" type="string">
SMTP username.
**Example**: `no-reply@example.com`
</ResponseField>
<ResponseField name="smtp_pass" type="string">
SMTP password.
**Environment Variable**: `EMAIL_SMTP_PASS`
</ResponseField>
<ResponseField name="smtp_secure" type="boolean">
Whether to use secure connection (SSL/TLS).
**Default**: `false`
<Tip>
Enable this when using port 465 (SSL).
</Tip>
</ResponseField>
<ResponseField name="no_reply" type="string">
From address for sent emails.
**Example**: `no-reply@example.com`
<Note>
Usually the same as `smtp_user`.
</Note>
</ResponseField>
<ResponseField name="smtp_tls_reject_unauthorized" type="boolean">
Whether to fail on invalid server certificates.
**Default**: `true`
</ResponseField>
</Expandable>
</ResponseField>
### Feature Flags
<ResponseField name="flags" type="object">
Feature flags to control application behavior.
<Expandable title="Flags">
<ResponseField name="require_email_verification" type="boolean">
Whether to require email verification for new users.
**Default**: `false`
<Warning>
Only enable this if you have email configuration set up.
</Warning>
</ResponseField>
<ResponseField name="disable_signup_without_invite" type="boolean">
Whether to disable public user registration.
**Default**: `false`
<Note>
Users can still sign up with valid invites when enabled.
</Note>
</ResponseField>
<ResponseField name="disable_user_create_org" type="boolean">
Whether to prevent users from creating organizations.
**Default**: `false`
<Note>
Server admins can always create organizations.
</Note>
</ResponseField>
<ResponseField name="allow_raw_resources" type="boolean">
Whether to allow raw TCP/UDP resource creation.
**Default**: `true`
<Note>
If set to `false`, users will only be able to create http/https resources.
</Note>
</ResponseField>
<ResponseField name="enable_integration_api" type="boolean">
Whether to enable the integration API.
**Default**: `false`
</ResponseField>
<ResponseField name="disable_local_sites" type="boolean">
Whether to disable local site creation and management.
**Default**: `false`
<Note>
When enabled, users cannot create sites that connect to local networks.
</Note>
</ResponseField>
<ResponseField name="disable_basic_wireguard_sites" type="boolean">
Whether to disable basic WireGuard site functionality.
**Default**: `false`
<Note>
When enabled, only advanced WireGuard configurations are allowed.
</Note>
</ResponseField>
<ResponseField name="disable_product_help_banners" type="boolean">
Whether to disable product help banners in the UI at the top of screens.
**Default**: `false`
</ResponseField>
<ResponseField name="disable_config_managed_domains" type="boolean">
Whether to disable domains managed through the configuration file.
**Default**: `false`
<Note>
When enabled, only domains created through the UI are allowed.
</Note>
</ResponseField>
<ResponseField name="disable_enterprise_features" type="boolean">
Whether to disable features that are only available in the Enterprise Edition from showing in the UI.
**Default**: `false`
<Note>
When enabled, Enterprise-only features are hidden from the UI.
</Note>
</ResponseField>
</Expandable>
</ResponseField>
### Database Configuration
<ResponseField name="postgres" type="object">
PostgreSQL database configuration (optional).
<Expandable title="PostgreSQL">
<ResponseField name="connection_string" type="string" required>
PostgreSQL connection string.
**Example**: `postgresql://user:password@host:port/database`
<Note>
See [PostgreSQL documentation](/self-host/advanced/database-options#postgresql) for setup instructions.
</Note>
</ResponseField>
<ResponseField name="replicas" type="array of objects">
Read-only replica database configurations for load balancing.
<Expandable title="Replica Configuration">
<ResponseField name="connection_string" type="string" required>
Connection string for the read replica database.
**Example**: `postgresql://user:password@replica-host:port/database`
</ResponseField>
</Expandable>
</ResponseField>
<ResponseField name="pool" type="object">
Database connection pool settings.
<Expandable title="Pool Settings">
<ResponseField name="max_connections" type="integer">
Maximum number of connections to the primary database.
**Default**: `20`
**Example**: `50`
</ResponseField>
<ResponseField name="max_replica_connections" type="integer">
Maximum number of connections to replica databases.
**Default**: `10`
**Example**: `25`
</ResponseField>
<ResponseField name="idle_timeout_ms" type="integer">
Time in milliseconds before idle connections are closed.
**Default**: `30000` (30 seconds)
**Example**: `60000`
</ResponseField>
<ResponseField name="connection_timeout_ms" type="integer">
Time in milliseconds to wait for a database connection.
**Default**: `5000` (5 seconds)
**Example**: `10000`
</ResponseField>
</Expandable>
</ResponseField>
</Expandable>
</ResponseField>
## Environment Variables
Some configuration values can be set using environment variables for enhanced security:
| Name | Variable | Config |
|------------------------------|------------------------------|------------------------------|
| Server Secret | `SERVER_SECRET` | `server.secret` |
| Email Password | `EMAIL_SMTP_PASS` | `email.smtp_pass` |
| PostgreSQL Connection String | `POSTGRES_CONNECTION_STRING` | `postgres.connection_string` |
@@ -0,0 +1,109 @@
> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pangolin.net/llms.txt
> Use this file to discover all available pages before exploring further.
# Database Options
> Configure SQLite or PostgreSQL database for Pangolin
<div id="pangolin-toc-cta" className="pangolin-toc-cta-source">
<Card title="Try free on Pangolin Cloud" icon="cloud" href="https://app.pangolin.net/auth/signup" arrow="true" cta="Sign up free">
Fastest way to get started with Pangolin using the hosted control plane. No credit card required.
</Card>
</div>
Pangolin supports two database options: SQLite for simplicity and PostgreSQL for production deployments.
<CardGroup cols={2}>
<Card title="SQLite (Default)" icon="database">
* No configuration required
* Easy to use and portable
* Built into the main image
* Perfect for development
</Card>
<Card title="PostgreSQL" icon="database">
* Production-ready database
* Better performance at scale
* Requires separate image
* Advanced configuration options
</Card>
</CardGroup>
## SQLite
By default, Pangolin uses SQLite for its ease of use and portability.
**Docker Image**: `fosrl/pangolin:<version>`
<Note>
No configuration is required to use SQLite with Pangolin.
</Note>
## PostgreSQL
You can optionally use PostgreSQL for production deployments.
**Docker Image**: `fosrl/pangolin:postgresql-<version>`
### Configuration
Add the following section to your Pangolin configuration file:
```yaml title="config.yml" theme={null}
postgres:
connection_string: postgresql://<user>:<password>@<host>:<port>/<database>
```
<Warning>
Replace the placeholders with your actual PostgreSQL connection details.
</Warning>
### Docker Compose Example
This example sets up PostgreSQL with health checks to ensure the database is ready before Pangolin starts:
```yaml title="docker-compose.yml" theme={null}
name: pangolin
services:
pangolin:
image: fosrl/pangolin:postgresql-latest # Don't use latest in production
container_name: pangolin
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
volumes:
- ./config:/app/config
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3001/api/v1/"]
interval: "10s"
timeout: "10s"
retries: 15
# ... other services ...
postgres:
image: postgres:17
container_name: postgres
restart: unless-stopped
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
volumes:
- ./config/postgres:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
```
<Warning>
This example is not necessarily production-ready. Adjust the configuration according to your needs and security requirements.
</Warning>
<Note>
Do not use `latest` tags in production. Use specific version tags for stability.
</Note>
@@ -0,0 +1,83 @@
> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pangolin.net/llms.txt
> Use this file to discover all available pages before exploring further.
# Enable ASN Blocking
> Configuration requirements to enable ASN blocking in Pangolin
<div id="pangolin-toc-cta" className="pangolin-toc-cta-source">
<Card title="Try free on Pangolin Cloud" icon="cloud" href="https://app.pangolin.net/auth/signup" arrow="true" cta="Sign up free">
Fastest way to get started with Pangolin using the hosted control plane. No credit card required.
</Card>
</div>
To enable ASN blocking in Pangolin Community you must download and place the Maxmind ASN database into the `config/`
directory and update the config file. This can be done for free.
<Tip>
Remember to keep the ASN database updated regularly, as ASN assignments and network mappings can change over time. You can just repeat the download and extraction steps periodically to ensure your database is current.
</Tip>
<Tip>
It is possible to automate this process with a Docker container from Maxmind themself.
Have a look at this [Community guide](/self-host/community-guides/geolite2automation) on how to implement this!
</Tip>
You can use the installer to download and place the database for you, just grab the latest installer:
```bash theme={null}
curl -fsSL https://static.pangolin.net/get-installer.sh | bash
```
Then run the installer again:
```bash theme={null}
./installer
```
### Manual Installation Steps
<Steps>
<Step title="Download and extract the ASN database">
Download and extract the GeoLite2 ASN database using the following commands:
```bash theme={null}
# Download the GeoLite2 ASN database
curl -L -o GeoLite2-ASN.tar.gz https://github.com/GitSquared/node-geolite2-redist/raw/refs/heads/master/redist/GeoLite2-ASN.tar.gz
# Extract the database
tar -xzf GeoLite2-ASN.tar.gz
# Move the .mmdb file to the config directory
mv GeoLite2-ASN_*/GeoLite2-ASN.mmdb config/
# Clean up the downloaded files
rm -rf GeoLite2-ASN.tar.gz GeoLite2-ASN_*
```
</Step>
<Step title="Update the Pangolin config file">
Update your Pangolin configuration to point to the new ASN database file. Edit your `config/config.yml` file to include the following entry:
```yaml theme={null}
server:
maxmind_asn_path: "./config/GeoLite2-ASN.mmdb"
```
</Step>
<Step title="Restart Pangolin">
Restart your Pangolin instance to apply the changes:
```bash theme={null}
docker compose restart pangolin
```
</Step>
</Steps>
Alternatively you can create an account at [Maxmind](https://www.maxmind.com/en/geolite2/signup) to get a license key
and download the database directly from them.
@@ -0,0 +1,83 @@
> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pangolin.net/llms.txt
> Use this file to discover all available pages before exploring further.
# Enable Geo-blocking
> Configuration requirements to enable geoblocking in Pangolin
<div id="pangolin-toc-cta" className="pangolin-toc-cta-source">
<Card title="Try free on Pangolin Cloud" icon="cloud" href="https://app.pangolin.net/auth/signup" arrow="true" cta="Sign up free">
Fastest way to get started with Pangolin using the hosted control plane. No credit card required.
</Card>
</div>
To enable geoblocking in Pangolin Community you must download and place the Maxmind geoip database into the `config/`
directory and update the config file. This can be done for free.
<Tip>
Remember to keep the GeoIP database updated regularly, as IP-to-country mappings can change over time. You can just repeat the download and extraction steps periodically to ensure your database is current.
</Tip>
<Tip>
It is possible to automate this process with a Docker container from Maxmind themself.
Have a look at this [Community guide](/self-host/community-guides/geolite2automation) on how to implement this!
</Tip>
You can use the installer to download and place the database for you, just grab the latest installer:
```bash theme={null}
curl -fsSL https://static.pangolin.net/get-installer.sh | bash
```
Then run the installer again:
```bash theme={null}
./installer
```
### Manual Installation Steps
<Steps>
<Step title="Download and extract the GeoIP database">
Download and extract the GeoLite2 Country database using the following commands:
```bash theme={null}
# Download the GeoLite2 Country database
curl -L -o GeoLite2-Country.tar.gz https://github.com/GitSquared/node-geolite2-redist/raw/refs/heads/master/redist/GeoLite2-Country.tar.gz
# Extract the database
tar -xzf GeoLite2-Country.tar.gz
# Move the .mmdb file to the config directory
mv GeoLite2-Country_*/GeoLite2-Country.mmdb config/
# Clean up the downloaded files
rm -rf GeoLite2-Country.tar.gz GeoLite2-Country_*
```
</Step>
<Step title="Update the Pangolin config file">
Update your Pangolin configuration to point to the new GeoIP database file. Edit your `config/config.yml` file to include the following entry:
```yaml theme={null}
server:
maxmind_db_path: "./config/GeoLite2-Country.mmdb"
```
</Step>
<Step title="Restart Pangolin">
Restart your Pangolin instance to apply the changes:
```bash theme={null}
docker compose restart pangolin
```
</Step>
</Steps>
Alternatively, you can create an account at [Maxmind](https://www.maxmind.com/en/geolite2/signup) to get a license key
and download the database directly from them.
@@ -0,0 +1,82 @@
> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pangolin.net/llms.txt
> Use this file to discover all available pages before exploring further.
# Enable Integration API
> Enable and configure the Integration API for external access
<div id="pangolin-toc-cta" className="pangolin-toc-cta-source">
<Card title="Try free on Pangolin Cloud" icon="cloud" href="https://app.pangolin.net/auth/signup" arrow="true" cta="Sign up free">
Fastest way to get started with Pangolin using the hosted control plane. No credit card required.
</Card>
</div>
The Integration API provides programmatic access to Pangolin functionality. It includes OpenAPI documentation via
Swagger UI.
## Enable Integration API
Update your Pangolin configuration file:
```yaml title="config.yml" theme={null}
flags:
enable_integration_api: true
```
If you want to specify a port other than the default `3003`, you can do so in the config as well:
```yaml title="config.yml" theme={null}
server:
integration_port: 3003 # Specify different port
```
## Configure Traefik Routing
Add the following configuration to your `config/traefik/dynamic_config.yml` to expose the Integration API at
`https://api.example.com/v1`:
```yaml title="dynamic_config.yml" theme={null}
routers:
# Add the following two routers
int-api-router-redirect:
rule: "Host(`api.example.com`)"
service: int-api-service
entryPoints:
- web
middlewares:
- redirect-to-https
- badger # If you have Badger >=1.3.0 and it's enabled in the middlewares section of the dynamic config
int-api-router:
rule: "Host(`api.example.com`)"
service: int-api-service
entryPoints:
- websecure
tls:
certResolver: letsencrypt
services:
# Add the following service
int-api-service:
loadBalancer:
servers:
- url: "http://pangolin:3003"
```
## Access Documentation
Once configured, access the Swagger UI documentation at:
```
https://api.example.com/v1/docs
```
<Frame caption="Swagger UI documentation interface">
<img src="https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/swagger.png?fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=a64ee1f3a7c40bf4f2bd19fe3cc16de9" alt="Swagger UI Preview" data-og-width="4556" width="4556" data-og-height="2692" height="2692" data-path="images/swagger.png" data-optimize="true" data-opv="3" srcset="https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/swagger.png?w=280&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=163c4b68c9f9d9ee2589898f8af8fedf 280w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/swagger.png?w=560&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=6a88ca1b59f96467740945db31eeb486 560w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/swagger.png?w=840&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=336609c2ced100ba26453265a2bcb898 840w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/swagger.png?w=1100&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=2f48f7afc56f2708e0e47e39c22fe14c 1100w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/swagger.png?w=1650&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=5477b386ed0be57412eb78a0cd813ff6 1650w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/swagger.png?w=2500&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=502b80fffd80cb3dc1b8b7ad0a174112 2500w" />
</Frame>
<Note>
The Integration API will be accessible at `https://api.example.com/v1` for external applications.
</Note>
@@ -0,0 +1,135 @@
> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pangolin.net/llms.txt
> Use this file to discover all available pages before exploring further.
# Internal CLI (pangctl)
> Command-line tool for managing your Pangolin instance
<div id="pangolin-toc-cta" className="pangolin-toc-cta-source">
<Card title="Try free on Pangolin Cloud" icon="cloud" href="https://app.pangolin.net/auth/signup" arrow="true" cta="Sign up free">
Fastest way to get started with Pangolin using the hosted control plane. No credit card required.
</Card>
</div>
The Pangolin container includes a CLI tool called `pangctl` that provides commands to help you manage your Pangolin
instance.
## Accessing the CLI
Run the following command on the host where the Pangolin container is running:
```bash theme={null}
docker exec -it pangolin pangctl <command>
```
## Available Commands
To see all available commands:
```bash theme={null}
docker exec -it pangolin pangctl --help
```
## Set Admin Credentials
Set or reset admin credentials for your Pangolin instance:
```bash theme={null}
docker exec -it pangolin pangctl set-admin-credentials --email "admin@example.com" --password "Password123!"
```
<Warning>
Use a strong password and keep your admin credentials secure.
</Warning>
## Clear Exit Nodes
Clear all exit nodes from the database:
```bash theme={null}
docker exec -it pangolin pangctl clear-exit-nodes
```
<Warning>
This command permanently deletes all exit nodes from the database. This action cannot be undone.
</Warning>
## Reset User Security Keys
Reset a user's security keys (passkeys) by deleting all their webauthn credentials:
```bash theme={null}
docker exec -it pangolin pangctl reset-user-security-keys --email "user@example.com"
```
<Warning>
This command permanently deletes all security keys for the specified user. The user will need to re-register their security keys to use passkey authentication again.
</Warning>
## Rotate Server Secret
Rotate the server secret by decrypting all encrypted values with the old secret and re-encrypting with a new secret.
This command updates OIDC IdP configurations and license keys in the database, as well as the config file.
```bash theme={null}
docker exec -it pangolin pangctl rotate-server-secret --old-secret "current-secret" --new-secret "new-secret"
```
### Options
* `--old-secret` (required): The current server secret (for verification)
* `--new-secret` (required): The new server secret to use (must be at least 8 characters long)
* `--force` (optional): Force rotation even if the old secret doesn't match the config file. Use this if you know the
old secret is correct but the config file is out of sync.
<Warning>
This command performs a critical operation that affects all encrypted data in your database. Ensure you have a backup before running this command.
**Important considerations:**
* The new secret must be at least 8 characters long
* The new secret must be different from the old secret
* The command verifies the old secret matches the config file (unless `--force` is used)
* After rotation, you must restart the server for the new secret to take effect
* Using `--force` with an incorrect old secret will cause the rotation to fail or corrupt encrypted data
</Warning>
## Clear License Keys
Clear all license keys from the database:
```bash theme={null}
docker exec -it pangolin pangctl clear-license-keys
```
<Warning>
This command permanently deletes all license keys from the database. This action cannot be undone.
</Warning>
## Delete Client
Delete a client and all associated data (OLMs, current fingerprint, userClients, approvals). Snapshots are preserved.
```bash theme={null}
docker exec -it pangolin pangctl delete-client --orgId "org-123" --niceId "client-identifier"
```
### Options
* `--orgId` (required): The organization ID
* `--niceId` (required): The client niceId (identifier)
<Warning>
This command permanently deletes the client and its associated data:
* All OLMs (One-time Login Mechanisms) associated with the client
* Current fingerprint entries
* Approval records
* UserClient associations
**Note:** Snapshots are preserved and will not be deleted.
This action cannot be undone. Ensure you have backups if needed.
</Warning>
@@ -0,0 +1,868 @@
> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pangolin.net/llms.txt
> Use this file to discover all available pages before exploring further.
# Metrics
> Enable and consume OpenTelemetry & vendor specific metrics
<div id="pangolin-toc-cta" className="pangolin-toc-cta-source">
<Card title="Try free on Pangolin Cloud" icon="cloud" href="https://app.pangolin.net/auth/signup" arrow="true" cta="Sign up free">
Fastest way to get started with Pangolin using the hosted control plane. No credit card required.
</Card>
</div>
We provide metrics in the **OpenTelemetry** (OTel) format and additionally support the following vendor backends:
* **Prometheus** (native scrape and via OTel Collector)
## Why Metrics & OTel
Observability enables:
1. **Incident detection** (latency spikes, reconnect storms)
2. **Capacity planning** (bytes, active sessions)
3. **Userexperience SLAs** (p95 tunnel latency, auth latency)
4. **Faster RCA** (dimensions like `error_type`, `result`)
OpenTelemetry provides a **vendorneutral** pipeline so you can change backends without retouching instrumented code.
## Availability
Newt exposes metrics starting from specific releases, but metrics are disabled in their default configuration.
* Newt: metrics implemented since Newt 1.6.0 (disabled by default)
## Open Telemetry
Push metrics and traces to an **OTel Collector** or any backend that accepts OTLP.
<Tip>
If you only enable Prometheus scrape, leave `*_METRICS_OTLP_ENABLED=false` and omit OTLP vars.
</Tip>
<Note>
The OTel Collector commonly uses port <code>4317</code> for gRPC and <code>4318</code> for HTTP. Set <code>OTEL\_EXPORTER\_OTLP\_PROTOCOL</code> to <code>http/protobuf</code> for HTTP or <code>grpc</code> for gRPC, and point <code>OTEL\_EXPORTER\_OTLP\_ENDPOINT</code> accordingly.
For further customization, see the [OTel Collector documentation](https://opentelemetry.io/docs/collector/).
</Note>
<AccordionGroup>
<Accordion title="Newt Configuration">
<Tabs>
<Tab title="Environment Variables">
```text theme={null}
NEWT_METRICS_OTLP_ENABLED=true # enable OTLP exporter
OTEL_EXPORTER_OTLP_ENDPOINT=otel-collector:4317
OTEL_EXPORTER_OTLP_INSECURE=true # or false + TLS vars
OTEL_METRIC_EXPORT_INTERVAL=15s
# Optional auth / TLS
OTEL_EXPORTER_OTLP_HEADERS=authorization=Bearer%20XYZ
OTEL_EXPORTER_OTLP_CERTIFICATE=/etc/otel/ca.pem
```
</Tab>
<Tab title="CLI Args">
```text theme={null}
newt \
--metrics-otlp-enabled=true \ # alias for otel
--otel=true \
--otel-exporter-otlp-endpoint=otel-collector:4317 \
--otel-exporter-otlp-insecure=true \
--otel-metric-export-interval=15s \
--otel-exporter-otlp-headers=authorization=Bearer%20XYZ \
--otel-exporter-otlp-certificate=/etc/otel/ca.pem
```
See the [CLI reference](../../manage/sites/configure-site) for all available flags.
</Tab>
</Tabs>
</Accordion>
<Accordion title="Newt Configuration Examples">
<Tabs>
<Tab title="CLI (gRPC)">
```bash theme={null}
# Enable OTLP exporters and point to your Collector's gRPC receiver.
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4317"
export OTEL_EXPORTER_OTLP_PROTOCOL="grpc"
newt \
--otlp=true
--id saz281jfa8z37zg
--secret ssfdfsder33rrerrwe
--endpoint http://pangolin.example.com
```
</Tab>
<Tab title="Docker Compose">
```yaml title="docker-compose.metrics.yaml" theme={null}
services:
otel-collector:
image: ghcr.io/open-telemetry/opentelemetry-collector-releases/opentelemetry-collector-contrib:latest # DO NOT use 'latest' in production
command: ["--config=/etc/otel/config.yaml"]
volumes:
- ./otel-config.yaml:/etc/otel/config.yaml:ro
ports:
- "4317:4317" # gRPC
- "4318:4318" # HTTP
- "8888:8888" # Prometheus exporter (from the Collector) - Optional
newt:
image: fosrl/newt:latest # DO NOT use 'latest' in production
environment:
NEWT_METRICS_OTLP_ENABLED: "true"
OTEL_EXPORTER_OTLP_ENDPOINT: otel-collector:4317
OTEL_EXPORTER_OTLP_INSECURE: "true"
PANGOLIN_ENDPOINT: https://example.com
NEWT_ID: heresmynewtid
NEWT_SECRET: yoursupersecretkeyhere
```
```yaml title="otel-config.yaml" theme={null}
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors: {}
# Example exporters:
exporters:
otlp:
endpoint: otel-collector:4317
insecure: true
prometheus:
endpoint: "0.0.0.0:8889"
service:
pipelines:
metrics:
receivers: [otlp]
processors: []
exporters: [prometheus]
```
Forward to Remote Write Backend
```yaml title="otel-config-remote.yaml" theme={null}
exporters:
prometheusremotewrite:
endpoint: https://prom-remote.example.com/api/v1/write
headers:
X-Scope-OrgID: tenant-a
tls:
insecure_skip_verify: false
service:
pipelines:
metrics/remote:
receivers: [otlp]
processors: [batch]
exporters: [prometheusremotewrite]
```
<Note>
Combine exporters (e.g. local Prometheus + remote write) to retain fast local dashboards and ship longterm retention externally.
</Note>
</Tab>
</Tabs>
</Accordion>
</AccordionGroup>
## Prometheus (without OTel Collector)
<AccordionGroup>
<Accordion title="Newt Configuration">
Each service listens on an admin HTTP address (example Newt default `:2112`).
<Tabs>
<Tab title="Environment Variables">
```text theme={null}
NEWT_METRICS_PROMETHEUS_ENABLED=true # /metrics endpoint
NEWT_ADMIN_ADDR=:2112 # admin HTTP address
```
</Tab>
<Tab title="CLI Args">
```text theme={null}
newt \
--metrics-prometheus-enabled=true \ # alias for metrics
--metrics=true
--admin-addr=:2112 \
```
See the [CLI reference](../../manage/sites/configure-site) for all available flags.
</Tab>
</Tabs>
</Accordion>
<Accordion title="Newt Configuration Examples">
<Tabs>
<Tab title="CLI">
```bash theme={null}
newt \
--metrics-prometheus-enabled=true \
--admin-addr=:2112 \
--id saz281jfa8z37zg \
--secret ssfdfsder33rrerrwe \
--endpoint https://pangolin.example.com
```
</Tab>
<Tab title="Docker Compose">
```yaml title="docker-compose.metrics.yaml" theme={null}
services:
newt:
image: fosrl/newt:latest # DO NOT use 'latest' in production
environment:
NEWT_METRICS_OTLP_ENABLED: "true"
OTEL_EXPORTER_OTLP_ENDPOINT: otel-collector:4317
OTEL_EXPORTER_OTLP_INSECURE: "true"
PANGOLIN_ENDPOINT: https://example.com
NEWT_ID: saz281jfa8z37zg
NEWT_SECRET: ssfdfsder33rrerrwe
```
</Tab>
<Tab title="Prometheus Scrape Config">
```yaml title="prometheus.yml (fragment)" theme={null}
scrape_configs:
- job_name: pangolin
static_configs: [{ targets: ["pangolin:2112"] }]
```
</Tab>
</Tabs>
</Accordion>
</AccordionGroup>
## Full Metric Reference
**Version 1.0.0 from 2025-10-28**
Below are currently implemented metrics for **Newt**.
* **Metric**: exact metric name
* **Instrument & unit**: OTel instrument type and canonical unit
* **Purpose**: what the metric conveys / recommended use
* **Emission path**: subsystem responsible (for troubleshooting missing data)
* **Example series**: representative sample including labels
<Warning>
Names/labels can change between major versions. Avoid hardcoding full label sets in alerts; prefer existence checks and aggregate functions.
</Warning>
### Newt metrics
<Tabs>
<Tab title="OpenTelemetry (OTel)">
<ResponseField name="newt">
OpenTelemetry metric instruments exposed by Newt. Expand each section to see individual metrics with labels, units, emission points, and examples.
<Expandable title="Site & Build">
<ResponseField name="newt_site_registrations_total" type="Counter">
Counts Pangolin registration attempts keyed by result.
<Expandable title="Details">
**Unit:** 1\
**Labels:** `result` (`success`|`failure`), `site_id`\
**Emission path:** `telemetry.IncSiteRegistration`\
**Example:** `newt_site_registrations_total{result="success",site_id="abc"} 1`
</Expandable>
</ResponseField>
<ResponseField name="newt_site_online" type="ObservableGauge">
0/1 heartbeat for the active site.
<Expandable title="Details">
**Unit:** 1\
**Labels:** `site_id`\
**Emission path:** `state.TelemetryView` (callback)\
**Example:** `newt_site_online{site_id="self"} 1`
</Expandable>
</ResponseField>
<ResponseField name="newt_site_last_heartbeat_seconds" type="ObservableGauge">
Seconds since last Pangolin heartbeat.
<Expandable title="Details">
**Unit:** seconds\
**Labels:** `site_id`\
**Emission path:** `TouchHeartbeat` (callback)\
**Example:** `newt_site_last_heartbeat_seconds{site_id="self"} 3.2`
</Expandable>
</ResponseField>
<ResponseField name="newt_build_info" type="ObservableGauge">
Constant 1 with build metadata labels.
<Expandable title="Details">
**Unit:** 1\
**Labels:** `version`, `commit`\
**Emission path:** Build info registration\
**Example:** `newt_build_info{version="1.2.3",commit="abc123"} 1`
</Expandable>
</ResponseField>
<ResponseField name="newt_restart_count_total" type="Counter">
Process boot indicator (increments once per process start).
<Expandable title="Details">
**Unit:** 1\
**Labels:** —\
**Emission path:** `RegisterBuildInfo`\
**Example:** `newt_restart_count_total 1`
</Expandable>
</ResponseField>
<ResponseField name="newt_cert_rotation_total" type="Counter">
Certificate rotation events keyed by result.
<Expandable title="Details">
**Unit:** 1\
**Labels:** `result`\
**Emission path:** `IncCertRotation`\
**Example:** `newt_cert_rotation_total{result="success"} 1`
</Expandable>
</ResponseField>
<ResponseField name="newt_config_reloads_total" type="Counter">
Config reload attempts keyed by result.
<Expandable title="Details">
**Unit:** 1\
**Labels:** `result`\
**Emission path:** `telemetry.IncConfigReload`\
**Example:** `newt_config_reloads_total{result="success"} 1`
</Expandable>
</ResponseField>
<ResponseField name="newt_config_apply_seconds" type="Histogram (s)">
Duration per config-apply phase keyed by `phase` and `result`.
<Expandable title="Details">
**Unit:** seconds\
**Labels:** `phase`, `result`\
**Emission path:** `telemetry.ObserveConfigApply`\
**Example:** `newt_config_apply_seconds_bucket{phase="peer",result="success",le="0.1"} 3`
</Expandable>
</ResponseField>
</Expandable>
<Expandable title="Tunnel">
<ResponseField name="newt_tunnel_sessions" type="ObservableGauge">
Active sessions per tunnel (or collapsed).
<Expandable title="Details">
**Unit:** 1\
**Labels:** `site_id`, `tunnel_id`\
**Emission path:** `RegisterStateView`\
**Example:** `newt_tunnel_sessions{site_id="self",tunnel_id="wgpub"} 2`
</Expandable>
</ResponseField>
<ResponseField name="newt_tunnel_bytes_total" type="Counter (bytes)">
Traffic per tunnel, direction, and protocol.
<Expandable title="Details">
**Unit:** bytes\
**Labels:** `tunnel_id`, `direction` (`ingress`|`egress`), `protocol` (`tcp`|`udp`)\
**Emission path:** Proxy manager\
**Example:** `newt_tunnel_bytes_total{direction="egress",protocol="tcp",tunnel_id="wgpub"} 8192`
</Expandable>
</ResponseField>
<ResponseField name="newt_tunnel_latency_seconds" type="Histogram (s)">
RTT samples per tunnel/transport.
<Expandable title="Details">
**Unit:** seconds\
**Labels:** `tunnel_id`, `transport`\
**Emission path:** Health checks\
**Example:** `newt_tunnel_latency_seconds_bucket{transport="wireguard",le="0.05",tunnel_id="wgpub"} 4`
</Expandable>
</ResponseField>
<ResponseField name="newt_tunnel_reconnects_total" type="Counter">
Reconnect attempts keyed by initiator & reason.
<Expandable title="Details">
**Unit:** 1\
**Labels:** `tunnel_id`, `initiator` (`client`|`server`), `reason`\
**Emission path:** `telemetry.IncReconnect`\
**Example:** `newt_tunnel_reconnects_total{initiator="client",reason="timeout",tunnel_id="wgpub"} 3`
</Expandable>
</ResponseField>
</Expandable>
<Expandable title="Connection & Auth">
<ResponseField name="newt_connection_attempts_total" type="Counter">
Auth/WebSocket connection attempts keyed by transport & result.
<Expandable title="Details">
**Unit:** 1\
**Labels:** `transport`, `result`\
**Emission path:** `telemetry.IncConnAttempt`\
**Example:** `newt_connection_attempts_total{transport="websocket",result="failure"} 2`
</Expandable>
</ResponseField>
<ResponseField name="newt_connection_errors_total" type="Counter">
Connection errors keyed by transport and type.
<Expandable title="Details">
**Unit:** 1\
**Labels:** `transport`, `error_type`\
**Emission path:** `telemetry.IncConnError`\
**Example:** `newt_connection_errors_total{transport="auth",error_type="auth_failed"} 1`
</Expandable>
</ResponseField>
</Expandable>
<Expandable title="WebSocket">
<ResponseField name="newt_websocket_connect_latency_seconds" type="Histogram (s)">
Dial latency for Pangolin WebSocket.
<Expandable title="Details">
**Unit:** seconds\
**Labels:** `result`, `transport`\
**Emission path:** `ObserveWSConnectLatency`\
**Example:** `newt_websocket_connect_latency_seconds_bucket{result="success",transport="websocket",le="0.5"} 1`
</Expandable>
</ResponseField>
<ResponseField name="newt_websocket_disconnects_total" type="Counter">
WebSocket disconnects keyed by reason.
<Expandable title="Details">
**Unit:** 1\
**Labels:** `reason`, `tunnel_id`\
**Emission path:** `IncWSDisconnect`\
**Example:** `newt_websocket_disconnects_total{reason="remote_close",tunnel_id="wgpub"} 2`
</Expandable>
</ResponseField>
<ResponseField name="newt_websocket_keepalive_failures_total" type="Counter">
Ping/Pong failures observed by keepalive.
<Expandable title="Details">
**Unit:** 1\
**Labels:** `reason` (e.g., `ping_write`, `pong_timeout`)\
**Emission path:** `telemetry.IncWSKeepaliveFailure(ctx, "ping_write")`\
**Example:** `newt_websocket_keepalive_failures_total{reason="ping_write"} 1`
</Expandable>
</ResponseField>
<ResponseField name="newt_websocket_session_duration_seconds" type="Histogram (s)">
Duration of established WS sessions keyed by result.
<Expandable title="Details">
**Unit:** seconds\
**Labels:** `result` (`success`|`error`)\
**Emission path:** `telemetry.ObserveWSSessionDuration(ctx, time.Since(start).Seconds(), "error")`\
**Example:** `newt_websocket_session_duration_seconds_bucket{result="error",le="60"} 3`
</Expandable>
</ResponseField>
<ResponseField name="newt_websocket_connected" type="ObservableGauge">
Current WS connection state (0/1).
<Expandable title="Details">
**Unit:** 1\
**Labels:** —\
**Emission path:** `telemetry.SetWSConnectionState(true|false)`\
**Example:** `newt_websocket_connected 1`
</Expandable>
</ResponseField>
<ResponseField name="newt_websocket_reconnects_total" type="Counter">
WebSocket reconnect attempts keyed by reason.
<Expandable title="Details">
**Unit:** 1\
**Labels:** `reason`\
**Emission path:** `telemetry.IncWSReconnect(ctx, "ping_write")`\
**Example:** `newt_websocket_reconnects_total{reason="ping_write"} 1`
</Expandable>
</ResponseField>
<ResponseField name="newt_websocket_messages_total" type="Counter">
In/out WS messages keyed by direction & type.
<Expandable title="Details">
**Unit:** 1\
**Labels:** `direction` (`in`|`out`), `msg_type` (`ping`|`pong`|`text`|...)\
**Emission path:** `IncWSMessage`\
**Example:** `newt_websocket_messages_total{direction="out",msg_type="ping"} 4`
</Expandable>
</ResponseField>
</Expandable>
<Expandable title="Proxy">
<ResponseField name="newt_proxy_active_connections" type="ObservableGauge">
Active TCP/UDP proxy connections per tunnel/protocol.
<Expandable title="Details">
**Unit:** 1\
**Labels:** `protocol`, `tunnel_id`\
**Emission path:** Proxy callback\
**Example:** `newt_proxy_active_connections{protocol="tcp",tunnel_id="wgpub"} 3`
</Expandable>
</ResponseField>
<ResponseField name="newt_proxy_buffer_bytes" type="ObservableGauge (bytes)">
Proxy buffer pool size.
<Expandable title="Details">
**Unit:** bytes\
**Labels:** `protocol`, `tunnel_id`\
**Emission path:** Proxy callback\
**Example:** `newt_proxy_buffer_bytes{protocol="tcp",tunnel_id="wgpub"} 10240`
</Expandable>
</ResponseField>
<ResponseField name="newt_proxy_async_backlog_bytes" type="ObservableGauge (bytes)">
Unflushed async byte backlog.
<Expandable title="Details">
**Unit:** bytes\
**Labels:** `protocol`, `tunnel_id`\
**Emission path:** Proxy callback\
**Example:** `newt_proxy_async_backlog_bytes{protocol="udp",tunnel_id="wgpub"} 4096`
</Expandable>
</ResponseField>
<ResponseField name="newt_proxy_drops_total" type="Counter">
Proxy write drops keyed by protocol/tunnel.
<Expandable title="Details">
**Unit:** 1\
**Labels:** `protocol`, `tunnel_id`\
**Emission path:** `IncProxyDrops`\
**Example:** `newt_proxy_drops_total{protocol="udp",tunnel_id="wgpub"} 2`
</Expandable>
</ResponseField>
<ResponseField name="newt_proxy_accept_total" type="Counter">
Proxy accept events keyed by result/reason.
<Expandable title="Details">
**Unit:** 1\
**Labels:** `tunnel_id`, `protocol`, `result`, `reason`\
**Emission path:** `telemetry.IncProxyAccept(ctx, tunnelID, "tcp", "failure", "timeout")`\
**Example:** `newt_proxy_accept_total{protocol="tcp",result="failure",reason="timeout"} 1`
</Expandable>
</ResponseField>
<ResponseField name="newt_proxy_connections_total" type="Counter">
Lifecycle events (opened/closed) per connection.
<Expandable title="Details">
**Unit:** 1\
**Labels:** `tunnel_id`, `protocol`, `event` (`opened`|`closed`)\
**Emission path:** `telemetry.IncProxyConnectionEvent(ctx, tunnelID, "tcp", telemetry.ProxyConnectionOpened)`\
**Example:** `newt_proxy_connections_total{protocol="tcp",event="opened"} 1`
</Expandable>
</ResponseField>
<ResponseField name="newt_proxy_connection_duration_seconds" type="Histogram (s)">
Duration of completed proxy connections.
<Expandable title="Details">
**Unit:** seconds\
**Labels:** `tunnel_id`, `protocol`, `result`\
**Emission path:** `telemetry.ObserveProxyConnectionDuration(ctx, tunnelID, "tcp", "success", seconds)`\
**Example:** `newt_proxy_connection_duration_seconds_bucket{protocol="tcp",result="success",le="1"} 3`
</Expandable>
</ResponseField>
</Expandable>
</ResponseField>
</Tab>
<Tab title="Prometheus">
<ResponseField name="newt">
Prometheus-style series for the same Newt metrics. Names, labels, and examples mirror the OTel tab.
<Expandable title="Site & Build">
<ResponseField name="newt_site_registrations_total" type="counter">
Counts Pangolin registration attempts keyed by result.
<Expandable title="Details">
**Labels:** `result`, `site_id` • **Unit:** 1 • **Path:** `telemetry.IncSiteRegistration`\
**Example:** `newt_site_registrations_total{result="success",site_id="abc"} 1`
</Expandable>
</ResponseField>
<ResponseField name="newt_site_online" type="gauge">
0/1 heartbeat for the active site.
<Expandable title="Details">
**Labels:** `site_id` • **Unit:** 1 • **Path:** `state.TelemetryView`\
**Example:** `newt_site_online{site_id="self"} 1`
</Expandable>
</ResponseField>
<ResponseField name="newt_site_last_heartbeat_seconds" type="gauge">
Seconds since last Pangolin heartbeat.
<Expandable title="Details">
**Labels:** `site_id` • **Unit:** seconds • **Path:** `TouchHeartbeat`\
**Example:** `newt_site_last_heartbeat_seconds{site_id="self"} 3.2`
</Expandable>
</ResponseField>
<ResponseField name="newt_build_info" type="gauge">
Constant 1 with build metadata labels.
<Expandable title="Details">
**Labels:** `version`, `commit` • **Unit:** 1 • **Path:** Build info registration\
**Example:** `newt_build_info{version="1.2.3",commit="abc123"} 1`
</Expandable>
</ResponseField>
<ResponseField name="newt_restart_count_total" type="counter">
Process boot indicator (increments once).
<Expandable title="Details">
**Labels:** — • **Unit:** 1 • **Path:** `RegisterBuildInfo`\
**Example:** `newt_restart_count_total 1`
</Expandable>
</ResponseField>
<ResponseField name="newt_cert_rotation_total" type="counter">
Certificate rotation events keyed by result.
<Expandable title="Details">
**Labels:** `result` • **Unit:** 1 • **Path:** `IncCertRotation`\
**Example:** `newt_cert_rotation_total{result="success"} 1`
</Expandable>
</ResponseField>
<ResponseField name="newt_config_reloads_total" type="counter">
Config reload attempts keyed by result.
<Expandable title="Details">
**Labels:** `result` • **Unit:** 1 • **Path:** `telemetry.IncConfigReload`\
**Example:** `newt_config_reloads_total{result="success"} 1`
</Expandable>
</ResponseField>
<ResponseField name="newt_config_apply_seconds" type="histogram">
Duration per config-apply phase & result.
<Expandable title="Details">
**Labels:** `phase`, `result` • **Unit:** seconds • **Path:** `telemetry.ObserveConfigApply`\
**Example:** `newt_config_apply_seconds_bucket{phase="peer",result="success",le="0.1"} 3`
</Expandable>
</ResponseField>
</Expandable>
<Expandable title="Tunnel">
<ResponseField name="newt_tunnel_sessions" type="gauge">
Active sessions per tunnel (or collapsed).
<Expandable title="Details">
**Labels:** `site_id`, `tunnel_id` • **Unit:** 1 • **Path:** `RegisterStateView`\
**Example:** `newt_tunnel_sessions{site_id="self",tunnel_id="wgpub"} 2`
</Expandable>
</ResponseField>
<ResponseField name="newt_tunnel_bytes_total" type="counter">
Traffic per tunnel/direction/protocol.
<Expandable title="Details">
**Labels:** `tunnel_id`, `direction`, `protocol` • **Unit:** bytes • **Path:** Proxy manager\
**Example:** `newt_tunnel_bytes_total{direction="egress",protocol="tcp",tunnel_id="wgpub"} 8192`
</Expandable>
</ResponseField>
<ResponseField name="newt_tunnel_latency_seconds" type="histogram">
RTT samples per tunnel/transport.
<Expandable title="Details">
**Labels:** `tunnel_id`, `transport` • **Unit:** seconds • **Path:** Health checks\
**Example:** `newt_tunnel_latency_seconds_bucket{transport="wireguard",le="0.05",tunnel_id="wgpub"} 4`
</Expandable>
</ResponseField>
<ResponseField name="newt_tunnel_reconnects_total" type="counter">
Reconnect attempts by initiator & reason.
<Expandable title="Details">
**Labels:** `tunnel_id`, `initiator`, `reason` • **Unit:** 1 • **Path:** `telemetry.IncReconnect`\
**Example:** `newt_tunnel_reconnects_total{initiator="client",reason="timeout",tunnel_id="wgpub"} 3`
</Expandable>
</ResponseField>
</Expandable>
<Expandable title="Connection & Auth">
<ResponseField name="newt_connection_attempts_total" type="counter">
Auth/WebSocket attempts by transport & result.
<Expandable title="Details">
**Labels:** `transport`, `result` • **Unit:** 1 • **Path:** `telemetry.IncConnAttempt`\
**Example:** `newt_connection_attempts_total{transport="websocket",result="failure"} 2`
</Expandable>
</ResponseField>
<ResponseField name="newt_connection_errors_total" type="counter">
Connection errors by transport and type.
<Expandable title="Details">
**Labels:** `transport`, `error_type` • **Unit:** 1 • **Path:** `telemetry.IncConnError`\
**Example:** `newt_connection_errors_total{transport="auth",error_type="auth_failed"} 1`
</Expandable>
</ResponseField>
</Expandable>
<Expandable title="WebSocket">
<ResponseField name="newt_websocket_connect_latency_seconds" type="histogram">
Dial latency for Pangolin WebSocket.
<Expandable title="Details">
**Labels:** `result`, `transport` • **Unit:** seconds • **Path:** `ObserveWSConnectLatency`\
**Example:** `newt_websocket_connect_latency_seconds_bucket{result="success",transport="websocket",le="0.5"} 1`
</Expandable>
</ResponseField>
<ResponseField name="newt_websocket_disconnects_total" type="counter">
WS disconnects by reason.
<Expandable title="Details">
**Labels:** `reason`, `tunnel_id` • **Unit:** 1 • **Path:** `IncWSDisconnect`\
**Example:** `newt_websocket_disconnects_total{reason="remote_close",tunnel_id="wgpub"} 2`
</Expandable>
</ResponseField>
<ResponseField name="newt_websocket_keepalive_failures_total" type="counter">
Keepalive Ping/Pong failures.
<Expandable title="Details">
**Labels:** `reason` • **Unit:** 1 • **Path:** `telemetry.IncWSKeepaliveFailure(ctx, "ping_write")`\
**Example:** `newt_websocket_keepalive_failures_total{reason="ping_write"} 1`
</Expandable>
</ResponseField>
<ResponseField name="newt_websocket_session_duration_seconds" type="histogram">
Duration of established WebSocket sessions by result.
<Expandable title="Details">
**Labels:** `result` • **Unit:** seconds • **Path:** `telemetry.ObserveWSSessionDuration(...)`\
**Example:** `newt_websocket_session_duration_seconds_bucket{result="error",le="60"} 3`
</Expandable>
</ResponseField>
<ResponseField name="newt_websocket_connected" type="gauge">
Current WS connection status (0/1).
<Expandable title="Details">
**Labels:** — • **Unit:** 1 • **Path:** `telemetry.SetWSConnectionState(true|false)`\
**Example:** `newt_websocket_connected 1`
</Expandable>
</ResponseField>
<ResponseField name="newt_websocket_reconnects_total" type="counter">
Reconnect attempts by reason.
<Expandable title="Details">
**Labels:** `reason` • **Unit:** 1 • **Path:** `telemetry.IncWSReconnect(ctx, "ping_write")`\
**Example:** `newt_websocket_reconnects_total{reason="ping_write"} 1`
</Expandable>
</ResponseField>
<ResponseField name="newt_websocket_messages_total" type="counter">
In/out WS messages by direction & type.
<Expandable title="Details">
**Labels:** `direction`, `msg_type` • **Unit:** 1 • **Path:** `IncWSMessage`\
**Example:** `newt_websocket_messages_total{direction="out",msg_type="ping"} 4`
</Expandable>
</ResponseField>
</Expandable>
<Expandable title="Proxy">
<ResponseField name="newt_proxy_active_connections" type="gauge">
Active TCP/UDP proxy connections per tunnel/protocol.
<Expandable title="Details">
**Labels:** `protocol`, `tunnel_id` • **Unit:** 1 • **Path:** Proxy callback\
**Example:** `newt_proxy_active_connections{protocol="tcp",tunnel_id="wgpub"} 3`
</Expandable>
</ResponseField>
<ResponseField name="newt_proxy_buffer_bytes" type="gauge">
Proxy buffer pool size.
<Expandable title="Details">
**Labels:** `protocol`, `tunnel_id` • **Unit:** bytes • **Path:** Proxy callback\
**Example:** `newt_proxy_buffer_bytes{protocol="tcp",tunnel_id="wgpub"} 10240`
</Expandable>
</ResponseField>
<ResponseField name="newt_proxy_async_backlog_bytes" type="gauge">
Unflushed async byte backlog.
<Expandable title="Details">
**Labels:** `protocol`, `tunnel_id` • **Unit:** bytes • **Path:** Proxy callback\
**Example:** `newt_proxy_async_backlog_bytes{protocol="udp",tunnel_id="wgpub"} 4096`
</Expandable>
</ResponseField>
<ResponseField name="newt_proxy_drops_total" type="counter">
Proxy write drops per protocol/tunnel.
<Expandable title="Details">
**Labels:** `protocol`, `tunnel_id` • **Unit:** 1 • **Path:** `IncProxyDrops`\
**Example:** `newt_proxy_drops_total{protocol="udp",tunnel_id="wgpub"} 2`
</Expandable>
</ResponseField>
<ResponseField name="newt_proxy_accept_total" type="counter">
Proxy accept events by result/reason.
<Expandable title="Details">
**Labels:** `tunnel_id`, `protocol`, `result`, `reason` • **Unit:** 1 • **Path:** `telemetry.IncProxyAccept(...)`\
**Example:** `newt_proxy_accept_total{protocol="tcp",result="failure",reason="timeout"} 1`
</Expandable>
</ResponseField>
<ResponseField name="newt_proxy_connections_total" type="counter">
Connection lifecycle events (opened/closed).
<Expandable title="Details">
**Labels:** `tunnel_id`, `protocol`, `event` • **Unit:** 1 • **Path:** `telemetry.IncProxyConnectionEvent(...)`\
**Example:** `newt_proxy_connections_total{protocol="tcp",event="opened"} 1`
</Expandable>
</ResponseField>
<ResponseField name="newt_proxy_connection_duration_seconds" type="histogram">
Duration of completed proxy connections.
<Expandable title="Details">
**Labels:** `tunnel_id`, `protocol`, `result` • **Unit:** seconds • **Path:** `telemetry.ObserveProxyConnectionDuration(...)`\
**Example:** `newt_proxy_connection_duration_seconds_bucket{protocol="tcp",result="success",le="1"} 3`
</Expandable>
</ResponseField>
</Expandable>
</ResponseField>
</Tab>
</Tabs>
***
## References
* <Link href="https://opentelemetry.io/docs/">OpenTelemetry Documentation</Link>
* <Link href="https://prometheus.io/docs/introduction/overview/">Prometheus Documentation</Link>
<Tip>
Have improvements or a missing metric? Open an issue or PR referencing this page.
</Tip>
@@ -0,0 +1,240 @@
> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pangolin.net/llms.txt
> Use this file to discover all available pages before exploring further.
# Private Configuration File
> Configure advanced Pangolin settings using the privateConfig.yml file for enterprise features
<div id="pangolin-toc-cta" className="pangolin-toc-cta-source">
<Card title="Try free on Pangolin Cloud" icon="cloud" href="https://app.pangolin.net/auth/signup" arrow="true" cta="Sign up free">
Fastest way to get started with Pangolin using the hosted control plane. No credit card required.
</Card>
</div>
The `privateConfig.yml` file provides advanced configuration options for enterprise deployments. This file is mounted at
`config/privateConfig.yml` in your Docker container.
<Note>
The private configuration file is only used on enterprise deployments. If you're using Pangolin Community, refer to the [main configuration file documentation](/self-host/advanced/config-file) instead. The private config file is not required.
</Note>
## Setting up your `privateConfig.yml`
Here's a basic example with common settings:
```yaml title="private-config.yml" theme={null}
app:
identity_provider_mode: "org"
branding:
app_name: "My Company Portal"
hide_auth_layout_footer: false
```
## Reference
This section contains the complete reference for all configuration options in `private-config.yml`.
### Application Settings
<ResponseField name="app" type="object">
Regional and base domain configuration for multi-region deployments.
<Expandable title="properties">
<ResponseField name="identity_provider_mode" type="string" default="global">
Set the identity provider (IdP) mode for authentication. By default both global and org pages will show until set. See the [Identity Providers documentation](/manage/identity-providers/add-an-idp#identity-provider-types) for more details on how this affects authentication and user management.
Possible values:
* `global`: (default) Both global and organization-level IdP login pages are available. Users can authenticate using either global or organization-specific identity providers.
* `org`: Only organization-level IdP login pages are available. Users must authenticate using identity providers defined at the organization
```yaml theme={null}
app:
identity_provider_mode: "org"
```
</ResponseField>
<ResponseField name="region" type="string" default="default">
The region identifier for this Pangolin instance. Used for multi-region deployments.
```yaml theme={null}
app:
region: "us-east-1"
```
</ResponseField>
</Expandable>
</ResponseField>
### Server Configuration
<ResponseField name="server" type="object">
Advanced server configuration including encryption keys and API integrations.
<Expandable title="properties">
<ResponseField name="encryption_key" type="string" default="./config/encryption.pem" required>
Path to the RSA private key used for encrypting sensitive data. Must be at least 8 characters long. THIS IS ONLY USED WITH pangolin\_dns FEATURE FLAG ENABLED AND REQUIRES EXTERNAL COMPONENTS.
```yaml theme={null}
server:
encryption_key_path: "./config/encryption.pem"
```
<Warning>
The `encryption_key_path` must point to a valid RSA key file. Generate one using:
```bash theme={null}
openssl genrsa -out encryption.pem 4096
```
Keep this key secure and backed up - it encrypts sensitive data in your database.
</Warning>
</ResponseField>
</Expandable>
</ResponseField>
### Redis Configuration
<ResponseField name="redis" type="object">
Redis connection settings for caching, sessions, and rate limiting. Useful for clustering Pangolin nodes.
<Expandable title="properties">
<ResponseField name="host" type="string" required>
Redis server hostname or IP address.
```yaml theme={null}
redis:
host: "redis.example.com"
```
</ResponseField>
<ResponseField name="port" type="number" required>
Redis server port (1-65535).
```yaml theme={null}
redis:
port: 6379
```
</ResponseField>
<ResponseField name="password" type="string">
Redis authentication password.
```yaml theme={null}
redis:
password: "your-secure-password"
```
</ResponseField>
<ResponseField name="db" type="number" default="0">
Redis database number (0-15 typically).
```yaml theme={null}
redis:
db: 0
```
</ResponseField>
<ResponseField name="replicas" type="array">
Array of read replica configurations for high-availability deployments.
```yaml theme={null}
redis:
host: "redis-primary"
port: 6379
replicas:
- host: "redis-replica-1"
port: 6379
password: "replica-password"
db: 0
- host: "redis-replica-2"
port: 6379
password: "replica-password"
db: 0
```
<Expandable title="replica properties">
<ResponseField name="host" type="string" required>
Replica server hostname.
</ResponseField>
<ResponseField name="port" type="number" required>
Replica server port.
</ResponseField>
<ResponseField name="password" type="string">
Replica authentication password.
</ResponseField>
<ResponseField name="db" type="number" default="0">
Database number on replica.
</ResponseField>
</Expandable>
</ResponseField>
</Expandable>
</ResponseField>
### Gerbil Tunnel Configuration
<ResponseField name="gerbil" type="object">
Configuration for the Gerbil tunnel exit node integration.
<Expandable title="properties">
<ResponseField name="local_exit_node_reachable_at" type="string" default="http://gerbil:3004">
URL where the local Gerbil exit node can be reached by Pangolin. Useful when clustering multiple pangolin nodes. Overrides the value stored in the database. Useful when using Docker and address the local gerbil container using the host's address.
```yaml theme={null}
gerbil:
local_exit_node_reachable_at: "http://gerbil:3004"
```
</ResponseField>
</Expandable>
</ResponseField>
### Feature Flags
<ResponseField name="flags" type="object">
Feature toggles for advanced functionality.
<Expandable title="properties">
<ResponseField name="use_org_only_idp" type="boolean" default="false">
**DEPRECATED**! See `app.identity_provider_mode: "org"` instead.
Restrict identity provider (IdP) authentication to organization-level only.
```yaml theme={null}
flags:
use_org_only_idp: true
```
</ResponseField>
<ResponseField name="enable_redis" type="boolean" default="false">
Enable Redis for caching and session management. Requires `redis` configuration.
```yaml theme={null}
flags:
enable_redis: true
```
</ResponseField>
<ResponseField name="use_pangolin_dns" type="boolean" default="false">
Use Pangolin DNS servers for client connections instead of external DNS servers for DNS delegation and CNAME setups. Used for clustering Pangolin nodes. REQUIRES EXTERNAL COMPONENTS. PLEASE CONTACT SUPPORT TO OBTAIN ACCESS BEFORE ENABLING.
```yaml theme={null}
flags:
use_pangolin_dns: true
```
</ResponseField>
</Expandable>
</ResponseField>
### Branding Configuration
Please refer to the [branding configuration documentation](/manage/branding).
@@ -0,0 +1,55 @@
> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pangolin.net/llms.txt
> Use this file to discover all available pages before exploring further.
# Telemetry
> Understanding Pangolin's anonymous usage data collection
<div id="pangolin-toc-cta" className="pangolin-toc-cta-source">
<Card title="Try free on Pangolin Cloud" icon="cloud" href="https://app.pangolin.net/auth/signup" arrow="true" cta="Sign up free">
Fastest way to get started with Pangolin using the hosted control plane. No credit card required.
</Card>
</div>
Pangolin collects anonymous usage telemetry to help us understand how the software is used and guide future improvements
and feature development.
## What We Collect
The telemetry system collects **anonymous, aggregated data** about your Pangolin deployment. For example:
* **System metrics**: Number of sites, users, resources, and clients
* **Usage patterns**: Resource types, protocols, and SSO configurations
* **Performance data**: Site traffic volumes and online status
* **Deployment info**: App version and installation timestamp
## Privacy & Anonymity
**No personal information is ever collected or transmitted.** All data is:
* **Anonymized**: Identifying info is hashed using SHA-256
* **Non-identifying**: Cannot be used to identify specific users or organizations
## Configuration
You can control telemetry collection in your `config.yml`:
```yaml theme={null}
app:
telemetry:
anonymous_usage: true # Set to false to disable
```
## What This Helps
Anonymous usage data helps us:
* Identify popular features and usage patterns
* Prioritize development efforts
* Improve performance and reliability
* Make Pangolin better for everyone
If you have concerns about telemetry collection, you can disable it entirely by setting `anonymous_usage: false` in your
configuration.
@@ -0,0 +1,286 @@
> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pangolin.net/llms.txt
> Use this file to discover all available pages before exploring further.
# Wildcard Domains
> Configure wildcard SSL certificates for automatic subdomain security with DNS-01 challenge
<div id="pangolin-toc-cta" className="pangolin-toc-cta-source">
<Card title="Try free on Pangolin Cloud" icon="cloud" href="https://app.pangolin.net/auth/signup" arrow="true" cta="Sign up free">
Fastest way to get started with Pangolin using the hosted control plane. No credit card required.
</Card>
</div>
Wildcard certificates allow you to secure unlimited subdomains with a single SSL certificate, eliminating the need to
generate individual certificates for each subdomain. Pangolin uses Traefik's built-in Let's Encrypt integration to
automatically manage these certificates.
<Warning>
Before setting up wildcard certificates, you must have a domain that you own and control. You must also have access to the DNS records for this domain.
</Warning>
<Info>
Since Pangolin uses Traefik as a reverse proxy, it has built-in support for Let's Encrypt certificates. This allows you to easily secure your Pangolin instance and all proxied resources with HTTPS. Let's Encrypt provides free SSL certificates, which are automatically renewed.
</Info>
If you used the default settings during installation, your Traefik instance should be set up to use `HTTP-01` challenge
for certificate generation. This challenge is the easiest to configure and requires that the Traefik instance be
accessible from the internet on port 80.
<Note>
It is highly recommended that you read the [official Traefik documentation](https://doc.traefik.io/traefik/https/acme/) on ACME and Let's Encrypt before proceeding.
</Note>
## Benefits of Wildcard Certificates
<CardGroup cols={3}>
<Card title="Single Certificate" icon="certificate">
Secure unlimited subdomains with one certificate, reducing management overhead.
</Card>
<Card title="Instant Subdomains" icon="bolt">
Add new subdomains without waiting for certificate generation (up to a few minutes).
</Card>
<Card title="Rate Limit Friendly" icon="shield">
Reduce Let's Encrypt rate limit impact by using fewer certificate requests.
</Card>
</CardGroup>
### Examples
* A wildcard cert `*.example.com` could protect:
* `api.example.com`
* `blog.example.com`
* `dashboard.example.com`
* Another wildcard `*.subdomain.example.com` could protect:
* `api.subdomain.example.com`
* `blog.subdomain.example.com`
<Info>
The [rate limits](https://letsencrypt.org/docs/rate-limits/) for Let's Encrypt are per domain. Using a wildcard certificate reduces the number of domains you have, which can help you avoid hitting these limits.
</Info>
## Setting Up Wildcard Certificates
<Steps>
<Step title="Stop the stack">
Make sure the stack is not running before making configuration changes.
</Step>
<Step title="Update Traefik configuration">
Update the Traefik configuration to use the DNS-01 challenge instead of the HTTP-01 challenge. This tells Traefik to use your DNS provider to create the DNS records needed for the challenge.
</Step>
<Step title="Configure Pangolin">
Set the `prefer_wildcard_cert` flag to `true` in the Pangolin configuration file for your domain. This is also configurable in the Pangolin dashboard (once restarted).
```yaml title="config.yml" highlight={4} theme={null}
domains:
domain1:
base_domain: "example.com"
prefer_wildcard_cert: true
```
</Step>
</Steps>
<Note>
This setting will try to encourage Traefik to request one wildcard certificate for each level of the domain used by your existing resources.
**Example**: If you have two resources `blog.example.com` and `blog.subdomain.example.com`, Traefik should try to
request a wildcard certificate for `*.example.com` and `*.subdomain.example.com` automatically for you.
</Note>
## Traefik Configuration
### Default Config for HTTP-01 Challenge
This is the default config generated by the installer. This is shown here for reference to compare with the wildcard
config below.
<AccordionGroup>
<Accordion title="1. HTTP Challenge Configuration">
Tell Traefik to use the `web` entrypoint for the HTTP challenge.
```yaml title="traefik_config.yml" highlight={4,5} theme={null}
certificatesResolvers:
letsencrypt:
acme:
httpChallenge:
entryPoint: web
email: admin@example.com
storage: "/letsencrypt/acme.json"
caServer: "https://acme-v02.api.letsencrypt.org/directory"
```
</Accordion>
<Accordion title="2. Dynamic Configuration">
Set the cert resolver to `letsencrypt` and the entrypoint to `websecure` in the dynamic config.
```yaml title="dynamic_config.yml" theme={null}
next-router:
rule: "Host(`pangolin.example.com`) && !PathPrefix(`/api/v1`)"
service: next-service
entryPoints:
- websecure
tls:
certResolver: letsencrypt
```
</Accordion>
</AccordionGroup>
### Wildcard Config for DNS-01 Challenge
<Steps>
<Step title="1. Configure DNS Challenge">
Tell Traefik to use your DNS provider for the DNS challenge. In this example, we are using Cloudflare.
```yaml title="traefik_config.yml" highlight={4,5} theme={null}
certificatesResolvers:
letsencrypt:
acme:
dnsChallenge:
provider: "cloudflare" # your DNS provider
# see https://doc.traefik.io/traefik/https/acme/#providers
email: "admin@example.com"
storage: "/letsencrypt/acme.json"
caServer: "https://acme-v02.api.letsencrypt.org/directory"
```
</Step>
<Step title="2. Add Wildcard Domains">
Add the domain and wildcard domain to the domains section of the next (front end) router in the dynamic config. This tells Traefik to generate a wildcard certificate for the base domain and all subdomains.
```yaml title="dynamic_config.yml" highlight={8-12} theme={null}
next-router:
rule: "Host(`pangolin.example.com`) && !PathPrefix(`/api/v1`)"
service: next-service
entryPoints:
- websecure
tls:
certResolver: letsencrypt
domains:
- main: "example.com"
sans:
- "*.example.com"
```
</Step>
<Step title="3. Add Environment Variables">
Add the environment variables for your DNS provider to the Traefik service in the docker compose file. This allows Traefik to authenticate with your DNS provider to create the DNS records needed for the challenge.
```yaml title="docker-compose.yml" highlight={11-13} theme={null}
traefik:
image: traefik:v3.4.0
container_name: traefik
restart: unless-stopped
network_mode: service:gerbil
depends_on:
pangolin:
condition: service_healthy
command:
- --configFile=/etc/traefik/traefik_config.yml
# Add the environment variables for your DNS provider.
environment:
CLOUDFLARE_DNS_API_TOKEN: "your-cloudflare-api-token"
volumes:
- ./config/traefik:/etc/traefik:ro
- ./config/letsencrypt:/letsencrypt
```
</Step>
</Steps>
<Warning>
If you're using Cloudflare, make sure your API token has the permissions Zone/Zone/Read and Zone/DNS/Edit and make sure it applies to all zones.
</Warning>
<Info>
Traefik supports most DNS providers. You can find a full list of supported providers and how to configure them in the [Traefik documentation on providers](https://doc.traefik.io/traefik/https/acme/#providers).
</Info>
## Verify it Works
<Tips>
<Tip title="Clear Old Certificates">
You can ensure Traefik doesn't try to use the old certs by deleting the previously used `acme.json` file. This will force Traefik to generate a new certificate on the next start.
</Tip>
</Tips>
<Steps>
<Step title="Start the stack">
Start the stack and watch the logs. You should notice that Traefik is making calls to your DNS provider to create the necessary records to complete the challenge.
</Step>
<Step title="Check logs">
For debugging purposes, you may find it useful to set the log level of Traefik to `debug` in the `traefik_config.yml` file.
</Step>
<Step title="Test new resource">
After Traefik is done waiting for the cert to verify, try to create a new resource with an unused subdomain. Traefik should not try to generate a new certificate, but instead use the wildcard certificate. The domain should also be secured immediately instead of waiting for a new certificate to be generated.
</Step>
<Step title="Verify certificate">
You can also check the volume (in the example above at `config/letsencrypt/`) for the correct certificates. In the `acme.json` file you should see something similar to the following. Note the `*.` in the domain.
</Step>
</Steps>
```json highlight={5} theme={null}
{
"Certificates": [
{
"domain": {
"main": "example.com",
"sans": [
"*.example.com"
]
},
"certificate": "...",
"key": "...",
"Store": "default"
}
]
}
```
## Troubleshooting
<AccordionGroup>
<Accordion title="Certificate not generating">
**Problem**: Wildcard certificate not being created.
**Solutions**:
* Verify DNS provider credentials are correct
* Check that API token has proper permissions
* Ensure domain ownership and DNS access
* Review Traefik logs for specific error messages
</Accordion>
<Accordion title="DNS challenge failing">
**Problem**: DNS-01 challenge not completing.
**Solutions**:
* Verify DNS provider is supported by Traefik
* Check API token permissions and scope
* Ensure DNS propagation has completed
* Review provider-specific configuration
</Accordion>
<Accordion title="Old certificates still being used">
**Problem**: Traefik using old HTTP-01 certificates.
**Solution**: Delete the `acme.json` file to force new certificate generation.
</Accordion>
</AccordionGroup>
@@ -0,0 +1,44 @@
> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pangolin.net/llms.txt
> Use this file to discover all available pages before exploring further.
# Without Tunneling
> Use Pangolin as a local reverse proxy without Gerbil tunneling
<div id="pangolin-toc-cta" className="pangolin-toc-cta-source">
<Card title="Try free on Pangolin Cloud" icon="cloud" href="https://app.pangolin.net/auth/signup" arrow="true" cta="Sign up free">
Fastest way to get started with Pangolin using the hosted control plane. No credit card required.
</Card>
</div>
Use Pangolin as a local reverse proxy and authentication manager
You can use Pangolin without Gerbil and tunneling. In this configuration, Pangolin acts as a normal reverse proxy and
authentication manager that can be deployed on your local network to provide access to resources.
<Note>
You can also use "local" sites to expose resources on the same VPS as Pangolin in addition to remote sites.
</Note>
## Setup
### Using the Installer
When asked if you want to install Gerbil for tunneling, select **No**. Gerbil will be removed from the Docker Compose
configuration.
### Manual Installation
Follow the [manual install steps](/self-host/manual/docker-compose), but **Gerbil is not required**. Your Docker Compose
should not include the Gerbil container.
## How It Works
When Gerbil starts up, it registers itself with Pangolin. By not installing Gerbil, you will only have the option to
choose the "Local" connection method. This means Traefik will use the local network to reach your resources.
<Warning>
All setup remains the same, except Pangolin and Traefik must now be on the same network as the resources you want to proxy to.
</Warning>
@@ -0,0 +1,150 @@
> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pangolin.net/llms.txt
> Use this file to discover all available pages before exploring further.
# Choosing a VPS
> Compare hosting options and find the best VPS for your Pangolin deployment
<div id="pangolin-toc-cta" className="pangolin-toc-cta-source">
<Card title="Try free on Pangolin Cloud" icon="cloud" href="https://app.pangolin.net/auth/signup" arrow="true" cta="Sign up free">
Fastest way to get started with Pangolin using the hosted control plane. No credit card required.
</Card>
</div>
Pangolin generally requires minimal resources to run effectively. A basic VPS with **1 vCPU, 1GB RAM, and 8GB SSD** is
sufficient for most deployments.
<CardGroup cols={2}>
<Card title="Minimum Requirements">
* **CPU**: 1 vCPU
* **RAM**: 1GB
* **Storage**: 8GB SSD
</Card>
<Card title="Recommended Specs">
* **CPU**: 2 vCPU
* **RAM**: 2GB
* **Storage**: 20GB SSD
</Card>
</CardGroup>
## Recommended Options
<Tip>
We're part of RackNerd's affiliate program. Using our links helps support Pangolin development at no extra cost to you.
</Tip>
<CardGroup cols={2}>
<Card title="RackNerd - 1GB RAM VPS" href="https://my.racknerd.com/aff.php?aff=13788&pid=903">
**\$11.29/Year**
* 1 vCPU, 1GB RAM, 24GB SSD, 2TB Bandwidth
</Card>
<Card title="RackNerd - 2GB RAM VPS" href="https://my.racknerd.com/aff.php?aff=13788&pid=904">
**\$18.29/Year**
* 1 vCPU, 2GB RAM, 40GB SSD, 3.5TB Bandwidth
* Great value for performance
</Card>
<Card title="RackNerd - 3.5GB RAM VPS" href="https://my.racknerd.com/aff.php?aff=13788&pid=905">
**\$32.49/Year**
* 2 vCPU, 3.5GB RAM, 65GB SSD, 7TB Bandwidth
</Card>
</CardGroup>
## DigitalOcean Marketplace
Deploy Pangolin with a single click using the DigitalOcean marketplace. The one-click installer automatically provisions
a DigitalOcean droplet with Pangolin pre-installed and firewall rules configured.
<Tip>
We're part of the DigitalOcean affiliate program. Using our links helps support Pangolin development at no extra cost to you. If you decide to use the DigitalOcean one-click installer, please use our link.
</Tip>
<CardGroup cols={2}>
<Card title="View App Listing" href="https://marketplace.digitalocean.com/apps/pangolin-ce-1?refcode=edf0480eeb81">
View Pangolin on the DigitalOcean marketplace
</Card>
<Card title="Deploy Now (Affiliate Link)" href="https://marketplace.digitalocean.com/apps/pangolin-ce-1?refcode=edf0480eeb81&action=deploy">
Create your droplet with Pangolin pre-installed
</Card>
</CardGroup>
## VPS Provider Comparison
| Provider | Plan | CPU | RAM | Storage | Price/Month | Price/Year | Best For |
|-------------------------------------------------------------------|---------------|--------|-------|----------|-------------|------------|--------------------|
| **[RackNerd](https://my.racknerd.com/aff.php?aff=13788)** | Starter | 1 vCPU | 1GB | 24GB SSD | \$0.94 | \$11.29 | Budget users |
| **[RackNerd](https://my.racknerd.com/aff.php?aff=13788)** | Basic | 1 vCPU | 2GB | 40GB SSD | \$1.52 | \$18.29 | Small teams |
| **[RackNerd](https://my.racknerd.com/aff.php?aff=13788)** | Standard | 2 vCPU | 3.5GB | 65GB SSD | \$2.71 | \$32.49 | Growing teams |
| **[Hetzner Cloud](https://www.hetzner.com/cloud)** | CX11 | 2 vCPU | 4GB | 40GB SSD | \$4.59 | \$55.08 | Performance |
| **[UpCloud](https://upcloud.com/pricing/)** | 1xCPU | 1 vCPU | 1GB | 10GB SSD | \$3.30 | \$39.60 | European users |
| **[Vultr](https://www.vultr.com/pricing)** | Cloud Compute | 1 vCPU | 1GB | 25GB SSD | \$5.00 | \$60.00 | Global presence |
| **[Linode](https://www.linode.com/pricing/)** | Nanode | 1 vCPU | 1GB | 25GB SSD | \$5.00 | \$60.00 | Developer friendly |
| **[DigitalOcean](https://www.digitalocean.com/pricing/droplets)** | Basic | 1 vCPU | 1GB | 25GB SSD | \$6.00 | \$72.00 | Easy setup |
| **[OVHcloud](https://www.ovhcloud.com/en/vps/)** | Starter | 2 vCPU | 2GB | 40GB SSD | \$5.50 | \$66.00 | European users |
| **[AWS EC2](https://instances.vantage.sh/)** | t3.micro | 2 vCPU | 1GB | 8GB SSD | \$8.50 | \$102.00 | Enterprise |
<Info>
Prices shown are approximate and may vary based on location, promotions, and currency exchange rates. Check provider websites for current pricing.
</Info>
## Selection Criteria
When choosing your VPS provider, consider these factors:
<AccordionGroup>
<Accordion title="Performance Requirements">
**Resource usage depends on several key factors:**
**Primary factors:**
* **Number of connected sites**: More sites = higher CPU and memory usage
* **Data throughput**: Amount of traffic transiting through the server
**Secondary factors:**
* **Dashboard UI usage**: Active admin sessions and configuration changes
* **Database activity**: User management, logging, and analytics queries
</Accordion>
<Accordion title="Geographic Location">
**Choose a data center close to your users:**
* **North America**: RackNerd, DigitalOcean, Vultr
* **Europe**: Hetzner, OVHcloud, UpCloud
* **Asia Pacific**: Vultr, Linode, DigitalOcean
* **Global**: AWS, Google Cloud, Azure
</Accordion>
<Accordion title="Support and Reliability">
**Consider these factors:**
* **Uptime guarantees**: Most providers offer 99.9%+
* **Support quality**: 24/7 support vs. community forums
* **Backup options**: Automated backups vs. manual
* **Monitoring**: Built-in monitoring tools
</Accordion>
<Accordion title="Cost Considerations">
**Hidden costs to watch for:**
* **Bandwidth overages**: Most plans include 1-2TB/month
* **Backup storage**: Additional charges for automated backups
* **IPv4 addresses**: Some providers charge extra
* **Support tiers**: Premium support may cost extra
</Accordion>
</AccordionGroup>
@@ -0,0 +1,226 @@
> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pangolin.net/llms.txt
> Use this file to discover all available pages before exploring further.
# CrowdSec
<div id="pangolin-toc-cta" className="pangolin-toc-cta-source">
<Card title="Try free on Pangolin Cloud" icon="cloud" href="https://app.pangolin.net/auth/signup" arrow="true" cta="Sign up free">
Fastest way to get started with Pangolin using the hosted control plane. No credit card required.
</Card>
</div>
<Note>
This is a community guide and is not officially supported. If you have any issues, please reach out to the [author](https://github.com/Lokowitz).
</Note>
CrowdSec is a modern, open-source, collaborative behavior detection engine, integrated with a global IP reputation
network. It functions as a massively multiplayer firewall, analyzing visitor behavior and responding appropriately to
various types of attacks.
## Installation
Crowdsec can be installed using the Pangolin Installer.
## Configuration
By default, Crowdsec is installed with a basic configuration, which includes
the [Crowdsec Bouncer Traefik plugin](https://plugins.traefik.io/plugins/6335346ca4caa9ddeffda116/crowdsec-bouncer-traefik-plugin).
### Choose the right logs
#### Syslog
For systems utilizing Syslog, the following volumes should be added to the `docker-compose.yml` file:
```yaml theme={null}
service:
crowdsec:
volumes:
- /var/log/auth.log:/var/log/auth.log:ro
- /var/log/syslog:/var/log/syslog:ro
```
Create a `syslog.yaml` file under `/config/crowdsec/acquis.d` with the following content:
```yaml theme={null}
filenames:
- /var/log/auth.log
- /var/log/syslog
labels:
type: syslog
```
#### Journalctl
To log iptables to journalctl, execute the following command on your host system:
```bash theme={null}
iptables -A INPUT -j LOG --log-prefix "iptables: "
```
Update the `docker-compose.yml` file as follows:
```yaml theme={null}
service:
crowdsec:
image: crowdsecurity/crowdsec:latest-debian
environment:
COLLECTIONS: crowdsecurity/traefik crowdsecurity/appsec-virtual-patching crowdsecurity/appsec-generic-rules crowdsecurity/linux crowdsecurity/iptables
volumes:
- ./config/crowdsec:/etc/crowdsec
- ./config/crowdsec/db:/var/lib/crowdsec/data
- ./config/traefik/logs:/var/log/traefik:ro
- /var/log/journal:/var/log/host:ro
```
Create a `journalctl.yaml` file under `/config/crowdsec/acquis.d` with the following content:
```yaml theme={null}
source: journalctl
journalctl_filter:
- "--directory=/var/log/host/"
labels:
type: syslog
```
### Securing the Host System (SSH)
By default, only Traefik requests are secured through the Crowdsec bouncer. To extend protection to your host system (
e.g., SSH), follow these steps to add a firewall bouncer:
1. Install the Crowdsec repositories. Refer to
the [installation documentation](https://docs.crowdsec.net/docs/next/getting_started/install_crowdsec/#install-our-repositories):
```bash theme={null}
curl -s https://install.crowdsec.net | sudo sh
```
2. Install the firewall bouncer. For Debian/Ubuntu systems using IPTables, refer to
the [documentation](https://docs.crowdsec.net/u/bouncers/firewall/):
```bash theme={null}
sudo apt install crowdsec-firewall-bouncer-iptables
```
3. Create an API key for the firewall bouncer to communicate with your CrowdSec Docker container. ("vps-firewall" is a
placeholder name for the key):
```bash theme={null}
docker exec -it crowdsec cscli bouncers add vps-firewall
```
4. Copy the dispalyed API key and insert it into the bouncer's configuration file:
```bash theme={null}
nano /etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml
```
5. Restart the firewall bouncer:
```bash theme={null}
systemctl restart crowdsec-firewall-bouncer
```
6. Update the `docker-compose.yml` file to expose communication port `8080` for the CrowdSec container and restart the
container:
```yaml theme={null}
service:
crowdsec:
ports:
- 6060:6060 # Metrics port
- 8080:8080 # Local API port
```
<Warning>
Dockers NAT-based port publishing feature automatically exposes all `ports:` defined in the `docker-compose` file on all network interfaces. This behavior can bypass your host firewall settings, potentially exposing services that you did not intend to make public.
Please see [complete warning about exposing ports](/self-host/dns-and-networking).
</Warning>
7. Verify communication between the firewall bouncer and the CrowdSec container by running:
```bash theme={null}
docker exec crowdsec cscli metrics
```
The output should look like this:
```bash theme={null}
+------------------------------------------------------------------+
| Local API Bouncers Metrics |
+---------------------------+----------------------+--------+------+
| Bouncer | Route | Method | Hits |
+---------------------------+----------------------+--------+------+
| traefik-bouncer | /v1/decisions/stream | HEAD | 2 |
| traefik-bouncer@10.0.4.20 | /v1/decisions | GET | 3 |
| vps-firewall | /v1/decisions/stream | GET | 84 | <---------
+---------------------------+----------------------+--------+------+
```
## Custom Ban Page
To display a custom ban page to attackers, follow these steps:
1. Place a `ban.html` page in the `/config/traefik` directory. If you prefer not to create your own, you can download
the official example:
```bash theme={null}
wget https://raw.githubusercontent.com/maxlerebourg/crowdsec-bouncer-traefik-plugin/refs/heads/main/ban.html
```
2. Update the `/config/traefik/dynamic_config.yml` file to include the following:
```yaml theme={null}
http:
middlewares:
crowdsec:
plugin:
crowdsec:
banHTMLFilePath: /etc/traefik/ban.html
```
## Custom Captcha Page
To use a custom captcha page, follow these steps:
1. Place a `captcha.html` page in the `/config/traefik` directory. If you don't want to create your own, you can
download the official example:
```bash theme={null}
wget https://raw.githubusercontent.com/maxlerebourg/crowdsec-bouncer-traefik-plugin/refs/heads/main/captcha.html
```
2. Update the `/config/traefik/dynamic_config.yml` file with the following configuration, replacing `<SERVICE>` with
your captcha provider (MUST BE either `hcaptcha`, `recaptcha`, or `turnstile`), and `<KEY>` with the appropriate site
and secret keys:
```yaml theme={null}
http:
middlewares:
crowdsec:
plugin:
crowdsec:
captchaHTMLFilePath: /etc/traefik/captcha.html
captchaGracePeriodSeconds: 300
captchaProvider: <SERVICE>
captchaSiteKey: <KEY>
captchaSecretKey: <KEY>
```
## Testing
You can test your configuration by adding a temporary ban or captcha for your IP. The ban will last for one minute.
To add a ban:
```bash theme={null}
docker exec crowdsec cscli decisions add --ip <YOUR IP> -d 1m --type ban
```
To trigger a captcha challenge:
```bash theme={null}
docker exec crowdsec cscli decisions add --ip <YOUR IP> -d 1m --type captcha
```
@@ -0,0 +1,148 @@
> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pangolin.net/llms.txt
> Use this file to discover all available pages before exploring further.
# GeoLite2 Automation
> A simple automation to download & update your GeoLite2 databases with geoipupdate
<div id="pangolin-toc-cta" className="pangolin-toc-cta-source">
<Card title="Try free on Pangolin Cloud" icon="cloud" href="https://app.pangolin.net/auth/signup" arrow="true" cta="Sign up free">
Fastest way to get started with Pangolin using the hosted control plane. No credit card required.
</Card>
</div>
<Note>
This is a community guide and is not officially supported. If you have any issues, please reach out to the [author](https://github.com/txwgnd).
</Note>
This automation lets your system automatically download & upgrade the `GeoLite2-Country` and `GeoLite2-ASN` databases
from Maxmind to use for geoblocking and ASN blocking on your Pangolin host. It's utilizing Maxmind'
s [geoipupdate](https://github.com/maxmind/geoipupdate/tree/main) Docker container to achieve this.
Maxmind's service is free of charge for development, personal or community
use. [Quote](https://support.maxmind.com/knowledge-base/articles/create-a-maxmind-account#h_01G4G4NG5C63BQ6HRG6MSS50T3)
# Table of Contents
1. **[Requirements](#1-requirements)**
2. **[Maxmind Account](#2-maxmind-account)**
3. **[API key creation](#3-api-key-creation)**
4. **[Modification of Pangolin's `docker-compose.yml`](#4-modification-of-pangolins-docker-compose-yml)**
5. **[Modification of Pangolin's `config.yml`](#5-modification-of-pangolins-config-yml)**
## 1. Requirements
* A Maxmind account for API access
* Pangolin version 1.11.0 or higher
## 2. Maxmind Account
To be able to use Maxmind's service you need to request access to the GeoLite2 databases and create an account on
their [website](https://www.maxmind.com/en/geolite2/signup?utm_source=kb\&utm_medium=kb-link\&utm_campaign=kb-create-account).
After you successfully created an account visit the mainpage again and login to your new account.
## 3. API key creation
The next step is to create an API key for `geoipupdate`. You'll find an entry called `Manage license keys` in the menu
on the left side. Head to this page and click on `Generate new license key`.
<Frame caption="Maxmind's Manage license keys page">
<img src="https://mintcdn.com/fossorial/WZ4OcypdChdYMxya/images/maxmind_manage-license-keys.jpeg?fit=max&auto=format&n=WZ4OcypdChdYMxya&q=85&s=45cc57507e2f28814baceabeac81da9a" alt="Maxmind's Manage license keys page" data-og-width="2792" width="2792" data-og-height="1634" height="1634" data-path="images/maxmind_manage-license-keys.jpeg" data-optimize="true" data-opv="3" srcset="https://mintcdn.com/fossorial/WZ4OcypdChdYMxya/images/maxmind_manage-license-keys.jpeg?w=280&fit=max&auto=format&n=WZ4OcypdChdYMxya&q=85&s=8180cc300150dfcb01c08083bf78524e 280w, https://mintcdn.com/fossorial/WZ4OcypdChdYMxya/images/maxmind_manage-license-keys.jpeg?w=560&fit=max&auto=format&n=WZ4OcypdChdYMxya&q=85&s=d82f7823cc52940ab01c187cb5011a43 560w, https://mintcdn.com/fossorial/WZ4OcypdChdYMxya/images/maxmind_manage-license-keys.jpeg?w=840&fit=max&auto=format&n=WZ4OcypdChdYMxya&q=85&s=3a837b0adaa483fe95b414f83765d72b 840w, https://mintcdn.com/fossorial/WZ4OcypdChdYMxya/images/maxmind_manage-license-keys.jpeg?w=1100&fit=max&auto=format&n=WZ4OcypdChdYMxya&q=85&s=bbbba78330b1c69004368cad6ada0ef3 1100w, https://mintcdn.com/fossorial/WZ4OcypdChdYMxya/images/maxmind_manage-license-keys.jpeg?w=1650&fit=max&auto=format&n=WZ4OcypdChdYMxya&q=85&s=301d34d12c0619bdb1de32a3cc4db17c 1650w, https://mintcdn.com/fossorial/WZ4OcypdChdYMxya/images/maxmind_manage-license-keys.jpeg?w=2500&fit=max&auto=format&n=WZ4OcypdChdYMxya&q=85&s=48b6917e61cda0a1b7bb0de946983cb2 2500w" />
</Frame>
Give your new key a name. E.g. `Pangolin`.
<Frame caption="Choose a name for the key">
<img src="https://mintcdn.com/fossorial/WZ4OcypdChdYMxya/images/maxmind_create-key-page.jpeg?fit=max&auto=format&n=WZ4OcypdChdYMxya&q=85&s=0558ff4f3da32cc7622bb516b736e72a" alt="Maxmind's key creation page" data-og-width="2014" width="2014" data-og-height="624" height="624" data-path="images/maxmind_create-key-page.jpeg" data-optimize="true" data-opv="3" srcset="https://mintcdn.com/fossorial/WZ4OcypdChdYMxya/images/maxmind_create-key-page.jpeg?w=280&fit=max&auto=format&n=WZ4OcypdChdYMxya&q=85&s=6e0a7d0eb4fb01c71b06deaf18c3cd24 280w, https://mintcdn.com/fossorial/WZ4OcypdChdYMxya/images/maxmind_create-key-page.jpeg?w=560&fit=max&auto=format&n=WZ4OcypdChdYMxya&q=85&s=413dac8ceb1247709542e95be4b5bbdb 560w, https://mintcdn.com/fossorial/WZ4OcypdChdYMxya/images/maxmind_create-key-page.jpeg?w=840&fit=max&auto=format&n=WZ4OcypdChdYMxya&q=85&s=dbbd65d58084dfc7151c21663fc041b2 840w, https://mintcdn.com/fossorial/WZ4OcypdChdYMxya/images/maxmind_create-key-page.jpeg?w=1100&fit=max&auto=format&n=WZ4OcypdChdYMxya&q=85&s=528a07b151c87eec1fee9a214f969116 1100w, https://mintcdn.com/fossorial/WZ4OcypdChdYMxya/images/maxmind_create-key-page.jpeg?w=1650&fit=max&auto=format&n=WZ4OcypdChdYMxya&q=85&s=df460bb545e889b4d92e2ce5fea26c64 1650w, https://mintcdn.com/fossorial/WZ4OcypdChdYMxya/images/maxmind_create-key-page.jpeg?w=2500&fit=max&auto=format&n=WZ4OcypdChdYMxya&q=85&s=bec43d2ae69d890c3d61558294bcfdb3 2500w" />
</Frame>
After your key got created the webpage will show you your Account ID as well as the API key. Save the key now because it
can only be seen once. Don't panic if something goes wrong, you can easily create new keys.
<Frame caption="Key successfully created">
<img src="https://mintcdn.com/fossorial/WZ4OcypdChdYMxya/images/maxmind_key-created.jpeg?fit=max&auto=format&n=WZ4OcypdChdYMxya&q=85&s=538be94f872890457482ecb8ec9289f1" alt="The key got created successfully" data-og-width="1960" width="1960" data-og-height="570" height="570" data-path="images/maxmind_key-created.jpeg" data-optimize="true" data-opv="3" srcset="https://mintcdn.com/fossorial/WZ4OcypdChdYMxya/images/maxmind_key-created.jpeg?w=280&fit=max&auto=format&n=WZ4OcypdChdYMxya&q=85&s=c45add128f333755e926de48ba54eb50 280w, https://mintcdn.com/fossorial/WZ4OcypdChdYMxya/images/maxmind_key-created.jpeg?w=560&fit=max&auto=format&n=WZ4OcypdChdYMxya&q=85&s=0937bac3fecd007a211cd032b9dbda39 560w, https://mintcdn.com/fossorial/WZ4OcypdChdYMxya/images/maxmind_key-created.jpeg?w=840&fit=max&auto=format&n=WZ4OcypdChdYMxya&q=85&s=62c6d99d867cdcf78dccc840d5405642 840w, https://mintcdn.com/fossorial/WZ4OcypdChdYMxya/images/maxmind_key-created.jpeg?w=1100&fit=max&auto=format&n=WZ4OcypdChdYMxya&q=85&s=8426121d796b806c89453fe8fffee084 1100w, https://mintcdn.com/fossorial/WZ4OcypdChdYMxya/images/maxmind_key-created.jpeg?w=1650&fit=max&auto=format&n=WZ4OcypdChdYMxya&q=85&s=3601e6b1ce3c49eb7cccbfb84f93ed29 1650w, https://mintcdn.com/fossorial/WZ4OcypdChdYMxya/images/maxmind_key-created.jpeg?w=2500&fit=max&auto=format&n=WZ4OcypdChdYMxya&q=85&s=fa3446ea791ff59cb1758739a745e37b 2500w" />
</Frame>
After you clicked on `Return to list` you should see an overview of your keys bundled with some metadata.
## 4. Modification of Pangolin's `docker-compose.yml`
Now login to your Pangolin host and navigate to `/pangolin` in your user directory:
```bash theme={null}
cd pangolin
```
Shut down Pangolin with:
```bash theme={null}
docker compose down
```
Open `docker-compose.yml` with your favorite text editor.
E.g. nano:
```bash theme={null}
nano docker-compose.yml
```
Append this Docker compose service at the end of your stack and add your Account ID as well as your API key you created
in the last step:
```yaml theme={null}
services:
(...)
geoipupdate:
container_name: geoipupdate
image: ghcr.io/maxmind/geoipupdate
restart: unless-stopped
environment:
- 'GEOIPUPDATE_ACCOUNT_ID=' # Account ID
- 'GEOIPUPDATE_LICENSE_KEY=' # API key
- 'GEOIPUPDATE_EDITION_IDS=GeoLite2-Country GeoLite2-ASN' # Which dbs should be downloaded
- 'GEOIPUPDATE_FREQUENCY=72' # Update intervall in hours
volumes:
- './config/GeoLite2:/usr/share/GeoIP'
```
#### Note
If you use the standard Pangolin deployment you shouldn't need to modify the path.
This is the bare minimum to run the container. There are other optional environment variables available. Have a look at
their [docs](https://dev.maxmind.com/geoip/updating-databases/?lang=en)!
Save and close the file, but don't restart the stack yet!
## 5. Modification of Pangolin's config.yml
Navigate to `/config` within the same folder and open it with a text editor.
```bash theme={null}
cd config
```
Add these lines to the `server` object
```yaml theme={null}
server:
maxmind_db_path: "./config/GeoLite2/GeoLite2-Country.mmdb"
maxmind_asn_path: "./config/GeoLite2/GeoLite2-ASN.mmdb"
```
These entries tell the Pangolin application where to find the databases.
Save and close the file then navigate to the `pangolin` folder one level higher.
Restart your Pangolin stack with:
```bash theme={null}
docker compose up -d
```
Et voilà, you are now able to define country rules and ASN rules for your ressources! 🏁
btw: you can use these exact databases for your Traefik dashboard
too -> [Community Guide](/self-host/community-guides/traefiklogsdashboard)
@@ -0,0 +1,157 @@
> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pangolin.net/llms.txt
> Use this file to discover all available pages before exploring further.
# Home Assistant Add-on
<div id="pangolin-toc-cta" className="pangolin-toc-cta-source">
<Card title="Try free on Pangolin Cloud" icon="cloud" href="https://app.pangolin.net/auth/signup" arrow="true" cta="Sign up free">
Fastest way to get started with Pangolin using the hosted control plane. No credit card required.
</Card>
</div>
<Note>
This is a community add-on and is not officially supported. If you have any issues, please reach out to the [author](https://github.com/Ferdinand99/home-assistant-newt-addon).
</Note>
This Home Assistant add-on allows you to easily run **Newt** directly in Home Assistant. The add-on lets you configure *
*PANGOLIN\_ENDPOINT**, **NEWT\_ID**, and **NEWT\_SECRET** via the Home Assistant interface.
## Features
* Easy installation via Home Assistant Add-on Store
* Automated setup and execution of the Newt container
* Supports `amd64`, `armv7`, `armhf`, and `aarch64` architectures
* Automatic restart on crash
## Installation
### **1. Add the GitHub Repository as an Add-on Source**
* Go to **Settings → Add-ons → Add-on Store**.
* Click the menu (three dots in the top right) and select **Repositories**.
* Add the following URL:
```
https://github.com/Ferdinand99/home-assistant-newt-addon
```
or
```
https://git.opland.net/Ferdinand99/home-assistant-newt-addon/
```
1. Click **Add** and wait for the repository to load.
### **2. Install and Start the Add-on**
1. Find **Newt Add-on** in the list and click **Install**.
2. Go to the **Configuration** tab and enter your values for:
* **PANGOLIN\_ENDPOINT** (e.g., `https://example.com`)
* **NEWT\_ID**
* **NEWT\_SECRET**
3. Click **Save** and then **Start**.
4. Check the **Logs** tab to verify that everything is running correctly.
## **Configuration**
After installation, you can configure the add-on via the Home Assistant UI:
```yaml theme={null}
PANGOLIN_ENDPOINT: "https://example.com"
NEWT_ID: "your_newt_id"
NEWT_SECRET: "your_newt_secret"
```
### **Docker Environment Variables**
The following environment variables are passed to the `Newt` container:
* `PANGOLIN_ENDPOINT`
* `NEWT_ID`
* `NEWT_SECRET`
## Exposing Home Assistant through addon
1. Connect addon to your Pangolin by completing environment variables and starting the addon
2. In Pangolin create new HTTP resource for your new Tunnel with subdomain
3. Within the created Resource add new Target Configuration
| Method | IP / Hostname | Port |
|--------|---------------|------|
| HTTP | 127.0.0.1 | 8123 |
4. In Home Assistant's `configuration.yaml` add these two sections:
```yaml theme={null}
http:
use_x_forwarded_for: true
trusted_proxies:
- 127.0.0.1
homeassistant:
allowlist_external_urls:
- "https://<subdomain>.example.com" # <-- Replace with URL of created resource in Pangolin
```
4.5: If you want to use SSO Authentication in Pangolin you need to set up the `configuration.yaml` like this:
```
http:
cors_allowed_origins:
- https://google.com
- https://www.home-assistant.io
ip_ban_enabled: true
login_attempts_threshold: 2
use_x_forwarded_for: true
trusted_proxies:
- 127.0.0.1
- Local IP of your NEWT instance
- VPS IP
```
You also need to set up `Resource rules` in the pangolin
dashboard. [See rule overview here](/manage/access-control/rules).
Many thanks to steuerlexi for finding this out!
[https://github.com/fosrl/pangolin/issues/757#issuecomment-2903774897](https://github.com/fosrl/pangolin/issues/757#issuecomment-2903774897)
<Note>
Please see [http](https://www.home-assistant.io/integrations/http/) documentation and [allowlist\_external\_urls](https://www.home-assistant.io/integrations/homeassistant/#external_url) on Home Assistant site.
</Note>
5. Restart Home Assistant and your new Pangolin Proxy should be alive
## Troubleshooting
#### **Add-on does not start?**
* Check the logs in Home Assistant (`Settings → Add-ons → Newt → Logs`).
* Ensure that `PANGOLIN_ENDPOINT`, `NEWT_ID`, and `NEWT_SECRET` are set correctly.
#### **Changes in configuration do not take effect?**
* Restart the add-on after making changes.
* Try removing the container manually:
```shell theme={null}
docker stop newt
docker rm newt
```
* Then start the add-on again.
#### **Docker not available?**
* Home Assistant OS manages Docker automatically, but check if the system has access to Docker by running:
```shell theme={null}
docker info
```
If this fails, there may be a restriction in Home Assistant OS.
## Useful Links
* [HA addon repo](https://github.com/Ferdinand99/home-assistant-newt-addon)
* [Home Assistant](https://www.home-assistant.io/)
* [Docker Docs](https://docs.docker.com/)
@@ -0,0 +1,210 @@
> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pangolin.net/llms.txt
> Use this file to discover all available pages before exploring further.
# Metrics
<div id="pangolin-toc-cta" className="pangolin-toc-cta-source">
<Card title="Try free on Pangolin Cloud" icon="cloud" href="https://app.pangolin.net/auth/signup" arrow="true" cta="Sign up free">
Fastest way to get started with Pangolin using the hosted control plane. No credit card required.
</Card>
</div>
<Note>
This is a community guide and is not officially supported. If you have any issues, please reach out to the [author](https://github.com/Lokowitz).
</Note>
This is a basic example of collecting metrics from Traefik and CrowdSec using Prometheus and visualizing them with
Grafana dashboards.
<Warning>
Important for users with low-powered server (1GB RAM):
This setup will increase the use of your server RAM.
</Warning>
## Configuration
### Traefik
For claiming metrics from Traefik we have to adjust some configuration files.
1. Update the `docker-compose.yml` file of the Pangolin stack to expose metrics port `8082` for the Prometheus
connection:
```yaml theme={null}
service:
gerbil:
ports:
- 8082:8082
```
<Warning>
Dockers NAT-based port publishing feature automatically exposes all `ports:` defined in `docker-compose` file. This behavior can bypass your host firewall settings, potentially exposing services that you did not intend to make public.
Please see [complete warning about exposing ports](/self-host/dns-and-networking).
</Warning>
2. Update the `/config/traefik/traefik_config.yml` file to include the following:
```yaml theme={null}
entryPoints:
metrics:
address: ":8082"
metrics:
prometheus:
buckets:
- 0.1
- 0.3
- 1.2
- 5.0
entryPoint: metrics
addEntryPointsLabels: true
addRoutersLabels: true
addServicesLabels: true
```
3. Restart the Gerbil and Traefik container to apply the changes:
```bash theme={null}
sudo docker restart traefik gerbil
```
### Crowdsec
For claiming metrics from Crowdsec we have to adjust the docker compose files.
1. Update the `docker-compose.yml` file of the Pangolin stack to expose metrics port `6060` for the Prometheus
connection:
```yaml theme={null}
service:
crowdsec:
ports:
- 6060:6060
```
<Warning>
Dockers NAT-based port publishing feature automatically exposes all `ports:` defined in the `docker-compose` file on all network interfaces. This behavior can bypass your host firewall settings, potentially exposing services that you did not intend to make public.
Please see [complete warning about exposing ports](/self-host/dns-and-networking).
</Warning>
2. Restart the Crowdsec container to apply the changes:
```bash theme={null}
sudo docker restart crowdsec
```
## Prometheus
1. Create a new Prometheus container or add it to `docker-compose.yml` of Pangolin stack:
```yaml theme={null}
services:
prometheus:
container_name: prometheus
image: prom/prometheus:latest
restart: unless-stopped
ports:
- 9090:9090
volumes:
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
- ./config/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
- ./config/prometheus/data:/prometheus
```
<Warning>
Dockers NAT-based port publishing feature automatically exposes all `ports:` defined in the `docker-compose` file on all network interfaces. This behavior can bypass your host firewall settings, potentially exposing services that you did not intend to make public.
Please see [complete warning about exposing ports](/self-host/dns-and-networking).
</Warning>
2. Create a `prometheus.yml` file in the `/config/prometheus` directory with the following content:
```yaml theme={null}
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: "prometheus"
static_configs:
- targets: ["localhost:9090"]
- job_name: traefik
static_configs:
- targets: ["172.17.0.1:8082"]
- job_name: crowdsec
static_configs:
- targets: ["172.17.0.1:6060"]
```
3. Create a folder `data` in `/config/prometheus` and change the owner and owning group:
```bash theme={null}
chown nobody:nogroup data
```
4. Start the Prometheus container:
```bash theme={null}
sudo docker compose up -d
```
## Grafana
1. Create a new Grafana container or add it to `docker-compose.yml` of Pangolin stack:
```yaml theme={null}
services:
grafana:
image: grafana/grafana:latest
container_name: grafana
restart: unless-stopped
ports:
- 3000:3000
volumes:
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
- ./config/grafana/data:/var/lib/grafana
```
<Warning>
Dockers NAT-based port publishing feature automatically exposes all `ports:` defined in the `docker-compose` file on all network interfaces. This behavior can bypass your host firewall settings, potentially exposing services that you did not intend to make public.
Please see [complete warning about exposing ports](/self-host/dns-and-networking).
</Warning>
2. Start the Grafana container:
```bash theme={null}
sudo docker compose up -d
```
<Note>
Default login credentials for Grafana admin user is admin:admin.
</Note>
### Add Prometheus Connection
Add the Prometheus connection under Connections -> Add new connection.
Set `http://172.17.0.1:9090` as `Prometheus Server URL` and click `Save & test`.
### Add Dashboard
Add a Dashboard under Dashboard -> New -> Import and import a pre configured Dashboard or create your own.
#### Traefik
<Frame caption="Traefik Dashboard">
<img src="https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/traefik_dashboard.png?fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=6afb4e81847fffc7aaf6c45686826528" alt="Traefik Dashboard" data-og-width="1842" width="1842" data-og-height="770" height="770" data-path="images/traefik_dashboard.png" data-optimize="true" data-opv="3" srcset="https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/traefik_dashboard.png?w=280&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=bb9b62a17b2b7e7889158bd2795a01e2 280w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/traefik_dashboard.png?w=560&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=7454585b3478a194163804fdbf29e7eb 560w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/traefik_dashboard.png?w=840&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=e8f3caea9bb396b433a4c1ae60dddcc1 840w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/traefik_dashboard.png?w=1100&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=69121558ea6b5627a12148b50ba2c261 1100w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/traefik_dashboard.png?w=1650&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=bf2d1d60bd3d2dcc90b55438c6b668ac 1650w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/traefik_dashboard.png?w=2500&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=7c14e820503c65dfcd6ebdc754d3aa82 2500w" />
</Frame>
Template Import ID = 17346
[https://grafana.com/grafana/dashboards/17346-traefik-official-standalone-dashboard/](https://grafana.com/grafana/dashboards/17346-traefik-official-standalone-dashboard/)
#### Crowdsec
[https://github.com/crowdsecurity/grafana-dashboards/tree/master](https://github.com/crowdsecurity/grafana-dashboards/tree/master)
@@ -0,0 +1,183 @@
> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pangolin.net/llms.txt
> Use this file to discover all available pages before exploring further.
# Middleware Manager
<div id="pangolin-toc-cta" className="pangolin-toc-cta-source">
<Card title="Try free on Pangolin Cloud" icon="cloud" href="https://app.pangolin.net/auth/signup" arrow="true" cta="Sign up free">
Fastest way to get started with Pangolin using the hosted control plane. No credit card required.
</Card>
</div>
<Note>
This is a community guide and not officially supported. For issues, contributions, or bug reports, please use the [official GitHub repository](https://github.com/hhftechnology/middleware-manager).
</Note>
## What is Middleware Manager?
The **Middleware Manager** is a microservice that extends your existing traefik deployments.\
It provides a **web UI** to attach Traefik middlewares to resources without editing Pangolin itself.
#### Security Warning
Middlewares can strengthen security but also create vulnerabilities if misconfigured.
* Test in staging before production.
* Misusing forward authentication can leak credentials.
* Bad rate limiter configs may be bypassed.
* Header misconfigurations can expose apps to XSS/CSRF.
* Stacking too many middlewares impacts performance.
* Always check provider references (`@http` vs `@file`).
***
### Key Use Cases
* External authentication (Authelia, Authentik, JWT)
* Security headers and CSP policies
* Geographic IP blocking
* Rate limiting / DDoS protection
* Redirects & path rewrites
* CrowdSec and other security tool integrations
***
## Prerequisites
* A running **Pangolin v1.0.0+**
* Docker + Docker Compose
* Basic Traefik knowledge
* Admin access to your Pangolin host
***
## Step 1: Add Middleware Manager Service
Update your `docker-compose.yml`:
```yaml theme={null}
middleware-manager:
image: hhftechnology/middleware-manager:latest
container_name: middleware-manager
restart: unless-stopped
volumes:
- ./data:/data
- ./config/traefik/rules:/conf
- ./config/middleware-manager/templates.yaml:/app/config/templates.yaml # Optional custom templates
environment:
- PANGOLIN_API_URL=http://pangolin:3001/api/v1
- TRAEFIK_CONF_DIR=/conf
- DB_PATH=/data/middleware.db
- PORT=3456
ports:
- "3456:3456"
```
***
## Step 2: Create Required Directories
```bash theme={null}
mkdir -p ./config/traefik/rules
mkdir -p ./config/middleware-manager
```
Move any dynamic configs into `./config/traefik/rules`.
***
## Step 3: Update Traefik Volumes & Providers
In your `traefik` service:
```yaml theme={null}
volumes:
- ./config/traefik:/etc/traefik:ro
- ./config/letsencrypt:/letsencrypt
- ./config/traefik/logs:/var/log/traefik
- ./config/traefik/rules:/rules # required
```
In `traefik_config.yml`:
```yaml theme={null}
providers:
file:
directory: "/rules"
watch: true
```
***
## Step 4: Start Services
```bash theme={null}
docker compose up -d
```
***
## Step 5: Access the UI
Middleware Manager runs at:
👉 [http://localhost:3456](http://localhost:3456)
***
## Common Middleware Examples
### Rate Limiting
```yaml theme={null}
middlewares:
- id: "rate-limit"
type: "rateLimit"
config:
average: 100
burst: 50
```
### Security Headers
```yaml theme={null}
middlewares:
- id: "security-headers"
type: "headers"
config:
customResponseHeaders:
Server: ""
X-Powered-By: ""
browserXSSFilter: true
contentTypeNosniff: true
forceSTSHeader: true
stsSeconds: 63072000
```
***
## Troubleshooting
* **Service does not exist** → Check `@http` or `@file` suffix in references
* **Middleware does not exist** → Verify config and required plugins
* **No changes applied** → Check Traefik logs, middleware priority, restart services
* **UI not showing resources** → Confirm `PANGOLIN_API_URL` and network connectivity
* **Database errors** → Check `./data` permissions, or reset `middleware.db`
* \*\*CrowdSec errors → Ensure the crowdsec container is running; middlewares fail if the service is down.
* **Protecting Pangolin itself** → Apply middlewares (e.g. geoblock, headers) directly on the websecure entryPoint to
cover all traffic.
* **Applying to many services** → Attach middleware to entryPoints instead of individual resources to cover all
subdomains at once.
* **TCP / SMTP with STARTTLS** → Not supported. Traefik cannot handle STARTTLS negotiation (only implicit TLS like SMTPS
on 465).
***
## Final Notes
The Middleware Manager gives you a UI to work with Traefiks powerful middleware ecosystem.
* Start with simple configs → test thoroughly → expand gradually.
* Use templates where possible.
* Always validate in staging before production.
@@ -0,0 +1,97 @@
> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pangolin.net/llms.txt
> Use this file to discover all available pages before exploring further.
# Overview
<div id="pangolin-toc-cta" className="pangolin-toc-cta-source">
<Card title="Try free on Pangolin Cloud" icon="cloud" href="https://app.pangolin.net/auth/signup" arrow="true" cta="Sign up free">
Fastest way to get started with Pangolin using the hosted control plane. No credit card required.
</Card>
</div>
<Note>
These are community written guides and are not officially supported. If you have any issues, please reach out to the authors or the community on [Discord](https://pangolin.net/discord) or [Github discussions](https://github.com/orgs/fosrl/discussions).
</Note>
The modular design of this system enables the extension of its functionality through the integration of existing Traefik
plugins, such as Crowdsec and Geoblock.
Additionally, Prometheus can collect metrics from both CrowdSec and Traefik, which can then be visualized in Grafana to
monitor security events, request statistics, and traffic patterns in real time.
## Traefik plugins
For a complete list of available plugins, please refer to the [Plugin Catalog](https://plugins.traefik.io/plugins).
### Crowdsec Bouncer
When installing Crowdsec via the Pangolin installer, the Crowdsec Traefik Bouncer will be automatically installed and
configured by default. The configuration can be customized to meet your specific requirements.
The CrowdSec Bouncer plugin for Traefik integrates CrowdSecs security engine to block malicious traffic in real time.
It runs as middleware within a Traefik container and enforces decisions based on CrowdSecs threat intelligence. This
helps protect services from bots, attackers, and abusive IPs dynamically.
For additional information, consult the following resources:
* [Traefik Plugin Catalog](https://plugins.traefik.io/plugins/6335346ca4caa9ddeffda116/crowdsec-bouncer-traefik-plugin)
* [Github Repository](https://github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin)
### Geoblock
The GeoBlock plugin for Traefik is a middleware that restricts access based on the clients geographic location. It runs
within a Traefik container and uses IP-based geolocation to allow or block traffic from specific countries. This is
useful for security, compliance, or access control in Traefik-managed services.
<Note>
Pangolin now supports native geoblocking. The GeoBlock plugin is considered legacy in Pangolin setups. If you previously installed it, follow [Remove GeoBlock Plugin](/self-host/community-guides/remove-geoblock-plugin) before enabling native geoblocking.
</Note>
For more details, please refer to the following resources:
* [Github Repository](https://github.com/PascalMinder/geoblock)
### Middleware Manager
The Middlware manager is a microservice that allows you to add custom middleware to Pangolin / Traefik resources.
For more details, please refer to the following resources:
* [Github Repository](https://github.com/hhftechnology/middleware-manager)
## Metrics
Currently you can claim metric data from Traefik and Crowdsec with Prometheus and visualize it within a Grafana
Dashboard.
### Prometheus
Prometheus is an open-source monitoring and alerting toolkit designed for collecting and querying time-series metrics.
It runs as a Docker container and uses a pull-based model to scrape data from configured endpoints. Prometheus
integrates well with Grafana for visualization and Alertmanager for alert handling.
For more details, please refer to the following resources:
* [Homepage](https://prometheus.io/)
* [Github Repository](https://github.com/prometheus/prometheus)
### Grafana
Grafana is an open-source analytics and visualization platform used to monitor and display time-series data. It runs as
a Docker container and supports multiple data sources, including Prometheus, InfluxDB, and MySQL. Grafana provides
interactive dashboards, alerting, and extensive customization options for data visualization.
For more details, please refer to the following resources:
* [Homepage](https://grafana.com/)
* [Github Repository](https://github.com/grafana/grafana)
### Traefik Logs Dashboard
The Traefik Logs Dashboard is a real-time dashboard for analyzing Traefik logs with IP geolocation, status code
analysis, and service metrics.
For more details, please refer to the following resources:
* [Github Repository](https://github.com/hhftechnology/traefik-log-dashboard)
@@ -0,0 +1,83 @@
> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pangolin.net/llms.txt
> Use this file to discover all available pages before exploring further.
# Remove GeoBlock Plugin
<div id="pangolin-toc-cta" className="pangolin-toc-cta-source">
<Card title="Try free on Pangolin Cloud" icon="cloud" href="https://app.pangolin.net/auth/signup" arrow="true" cta="Sign up free">
Fastest way to get started with Pangolin using the hosted control plane. No credit card required.
</Card>
</div>
<Note>
This is a community guide and is not officially supported. If you have any issues, please reach out to the community on [Discord](https://pangolin.net/discord) or [Github discussions](https://github.com/orgs/fosrl/discussions).
</Note>
Pangolin now supports native geoblocking. If you previously installed the Traefik GeoBlock plugin, remove it before
enabling native geoblocking to avoid duplicate blocking or startup errors.
<Note>
After cleanup, follow [Enable Geo-blocking](/self-host/advanced/enable-geoblocking) to configure native geoblocking in Pangolin.
</Note>
## Remove the GeoBlock plugin
<Steps>
<Step title="Remove GeoBlock middleware references">
Remove any references to `geoblock@file` from your Traefik entry points, routers, or labels.
Example removal in `/config/traefik/traefik_config.yml`:
```yaml theme={null}
entryPoints:
websecure:
http:
middlewares:
# Remove this line
- geoblock@file
```
</Step>
<Step title="Remove the plugin definition from Traefik static config">
Delete the GeoBlock plugin block from `/config/traefik/traefik_config.yml`:
```yaml theme={null}
experimental:
plugins:
geoblock:
moduleName: github.com/PascalMinder/geoblock
version: v0.3.2
```
</Step>
<Step title="Remove the middleware configuration from dynamic config">
Delete the GeoBlock middleware section from `/config/traefik/dynamic_config.yml`:
```yaml theme={null}
http:
middlewares:
geoblock:
plugin:
geoblock:
...
```
</Step>
<Step title="Restart Traefik">
Restart Traefik to apply the changes:
```bash theme={null}
docker restart traefik
```
</Step>
</Steps>
## Next steps
Follow [Enable Geo-blocking](/self-host/advanced/enable-geoblocking) to configure native geoblocking in Pangolin.
@@ -0,0 +1,302 @@
> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pangolin.net/llms.txt
> Use this file to discover all available pages before exploring further.
# Traefik Log Dashboard (v2 Agent Architecture)
<div id="pangolin-toc-cta" className="pangolin-toc-cta-source">
<Card title="Try free on Pangolin Cloud" icon="cloud" href="https://app.pangolin.net/auth/signup" arrow="true" cta="Sign up free">
Fastest way to get started with Pangolin using the hosted control plane. No credit card required.
</Card>
</div>
<Note>
This is a community guide and is not officially supported. For issues or advanced configuration, please visit the [official repository](https://github.com/hhftechnology/traefik-log-dashboard).
</Note>
If youre already using the **Pangolin stack with Traefik as your reverse proxy**, you already have robust routing in
place.\
However, raw logs can be hard to interpret — making it difficult to visualize request patterns, latency, and geographic
origins.
The **new Traefik Log Dashboard (v2)** introduces a **lightweight agent-based architecture** with **multi-instance
scalability, enhanced GeoIP analytics, and a modern Next.js frontend** for real-time insights into your Traefik traffic.
***
## Highlights (New in v2)
* **Agent-based architecture**: The Go-powered agent parses logs, exposes metrics, and supports multiple Traefik
instances.
* **Multi-agent support**: Monitor multiple Traefik setups (e.g., production, staging) from one dashboard.
* **Next.js 14 frontend**: Real-time charts, filters, and system stats in a responsive UI.
* **Enhanced GeoIP**: Supports both **City** and **Country** MaxMind databases.
* **System monitoring**: Built-in CPU, memory, and disk tracking.
* **Bearer token authentication**: Secure access between dashboard and agents.
* **Backward compatible** with existing Traefik log setups.
***
## Prerequisites
* Docker + Docker Compose
* Traefik v2.x or v3.x (logs in JSON format)
* A working **Pangolin stack**
* (Optional) MaxMind GeoLite2 databases (City + Country)
***
## Step 1: Configure Traefik Logs
Ensure Traefik is outputting **JSON logs** and **access logs** are written to a file.
Update your `./config/traefik/traefik_config.yml`:
```yaml theme={null}
log:
level: INFO
filePath: "/var/log/traefik/traefik.log"
format: json
accessLog:
filePath: "/var/log/traefik/access.log"
format: json
fields:
defaultMode: keep
headers:
defaultMode: keep
```
> Tip: JSON format is required for accurate parsing by the new agent.
***
## Step 2: Add Dashboard and Agent Services
Extend your existing `docker-compose.yml` with the new services.
```yaml theme={null}
# Traefik Log Dashboard Agent
traefik-agent:
image: hhftechnology/traefik-log-dashboard-agent:latest
restart: unless-stopped
ports:
- "5000:5000"
volumes:
- ./data/positions:/data
- ./config/traefik/logs:/logs:ro
- ./config/maxmind:/geoip:ro
environment:
# Log Paths
- TRAEFIK_LOG_DASHBOARD_ACCESS_PATH=/logs/access.log
- TRAEFIK_LOG_DASHBOARD_ERROR_PATH=/logs/traefik.log
# Authentication
- TRAEFIK_LOG_DASHBOARD_AUTH_TOKEN=YOUR_API_TOKEN
# System Monitoring
- TRAEFIK_LOG_DASHBOARD_SYSTEM_MONITORING=true
# GeoIP Configuration
- TRAEFIK_LOG_DASHBOARD_GEOIP_ENABLED=true
- TRAEFIK_LOG_DASHBOARD_GEOIP_CITY_DB=/geoip/GeoLite2-City.mmdb
- TRAEFIK_LOG_DASHBOARD_GEOIP_COUNTRY_DB=/geoip/GeoLite2-Country.mmdb
# Log Format
- TRAEFIK_LOG_DASHBOARD_LOG_FORMAT=json
# Server Port
- PORT=5000
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:5000/api/logs/status"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
# Traefik Log Dashboard - Web UI
traefik-dashboard:
image: hhftechnology/traefik-log-dashboard:latest
container_name: traefik-log-dashboard
restart: unless-stopped
ports:
- "3000:3000"
environment:
# Agent Configuration
- AGENT_API_URL=http://traefik-agent:5000
- AGENT_API_TOKEN=YOUR_API_TOKEN
- NODE_ENV=production
- PORT=3000
depends_on:
traefik-agent:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
```
Please replace the YOUR\_API\_TOKEN with a secure token of your choice.
> Note: The new agent replaces both `log-dashboard-backend` and `log-dashboard-frontend` from the previous guide.
***
## Step 3: Setup MaxMind GeoIP (City + Country)
GeoIP is optional but highly recommended for geographic analytics and maps.
### 1. Create a free MaxMind account
[GeoLite2 Signup](https://www.maxmind.com/en/geolite2/signup)
Generate a license key and export it for Docker use:
```bash theme={null}
export MAXMIND_LICENSE_KEY=your_license_key_here
mkdir -p ./config/maxmind
```
### 2. Add the GeoIP Database Updater
Append this to your `docker-compose.yml`:
```yaml theme={null}
# Optional: MaxMind GeoIP Database Updater
maxmind-updater:
image: alpine:latest
restart: "no"
volumes:
- ./config/maxmind:/data
environment:
- MAXMIND_LICENSE_KEY=${MAXMIND_LICENSE_KEY:-your-license-key-here}
command: >
sh -c "
apk add --no-cache wget tar &&
cd /data &&
if [ ! -f GeoLite2-City.mmdb ] || [ \"$(find . -name 'GeoLite2-City.mmdb' -mtime +7)\" ]; then
echo 'Updating GeoLite2-City database...'
wget -O GeoLite2-City.tar.gz 'https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&license_key=${MAXMIND_LICENSE_KEY}&suffix=tar.gz' &&
tar --wildcards -xzf GeoLite2-City.tar.gz --strip-components=1 '*/GeoLite2-City.mmdb' &&
rm -f GeoLite2-City.tar.gz
fi &&
if [ ! -f GeoLite2-Country.mmdb ] || [ \"$(find . -name 'GeoLite2-Country.mmdb' -mtime +7)\" ]; then
echo 'Updating GeoLite2-Country database...'
wget -O GeoLite2-Country.tar.gz 'https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country&license_key=${MAXMIND_LICENSE_KEY}&suffix=tar.gz' &&
tar --wildcards -xzf GeoLite2-Country.tar.gz --strip-components=1 '*/GeoLite2-Country.mmdb' &&
rm -f GeoLite2-Country.tar.gz
fi &&
echo 'GeoIP databases updated successfully.'
"
```
***
## Step 4: Launch the Stack
```bash theme={null}
docker compose up -d
docker compose ps
```
***
## Step 5: Access the Dashboard
* **Web UI** → [http://localhost:3000](http://localhost:3000)
* Default data source: `traefik-agent:5000`
You should see real-time traffic metrics, GeoIP maps, error tracking, and system performance indicators.
***
## Key Features
**Real-time analytics** for request rates, response times, and errors
**GeoIP maps** with both City and Country-level resolution
**System health** (CPU, memory, disk)
**Multi-agent support** (monitor multiple Traefik instances)
**Secure API authentication** via token
**Responsive modern UI**
***
## Advanced: Multi-Agent Setup
You can deploy multiple `traefik-agent` instances across environments and connect them all to a single dashboard.
Example:
```yaml theme={null}
traefik-agent-prod:
image: hhftechnology/traefik-log-dashboard-agent:latest
ports: ["5000:5000"]
environment:
- TRAEFIK_LOG_DASHBOARD_AUTH_TOKEN=prod_token
- TRAEFIK_LOG_DASHBOARD_ACCESS_PATH=/logs/access.log
- TRAEFIK_LOG_DASHBOARD_GEOIP_ENABLED=true
volumes:
- /var/log/traefik/prod:/logs:ro
- ./config/maxmind:/geoip:ro
- ./data/positions-prod:/data
traefik-agent-staging:
image: hhftechnology/traefik-log-dashboard-agent:latest
ports: ["5001:5000"]
environment:
- TRAEFIK_LOG_DASHBOARD_AUTH_TOKEN=staging_token
- TRAEFIK_LOG_DASHBOARD_ACCESS_PATH=/logs/access.log
volumes:
- /var/log/traefik/staging:/logs:ro
- ./config/maxmind:/geoip:ro
traefik-dashboard:
image: hhftechnology/traefik-log-dashboard:latest
ports: ["3000:3000"]
environment:
- NODE_ENV=production
```
Then, in the **Dashboard → Settings → Agents**, add each agent URL and token.
***
## Performance Tuning
| Setting | Description | Recommended |
|-------------------------------------------|----------------------|-------------|
| `TRAEFIK_LOG_DASHBOARD_SYSTEM_MONITORING` | Enables system stats | `true` |
| `TRAEFIK_LOG_DASHBOARD_LOG_FORMAT` | Log parsing format | `json` |
***
## Troubleshooting
| Issue | Cause | Fix |
|-----------------------|--------------------------|---------------------------------------------------------------------|
| Dashboard not loading | Container not healthy | `docker compose ps` → check `health` |
| No logs appearing | Wrong log path or format | Ensure `access.log` is JSON and volume mounted |
| GeoIP missing | Missing databases | Run `maxmind-updater` or mount both `.mmdb` files |
| Auth errors | Token mismatch | Verify `AGENT_API_TOKEN` matches `TRAEFIK_LOG_DASHBOARD_AUTH_TOKEN` |
| Slow UI | Large logs | Use JSON logs + incremental read; prune logs periodically |
***
## Summary
* Replaces the old `log-dashboard-backend` + `log-dashboard-frontend` with the new **agent-based architecture**
* Supports **multiple Traefik instances**
* Adds **GeoLite2 Country + City databases**
* Integrates **real-time analytics + system monitoring**
* Uses **MaxMind license key** for GeoIP updates
* More stable with less memory
***
**Project Repository
** → [https://github.com/hhftechnology/traefik-log-dashboard](https://github.com/hhftechnology/traefik-log-dashboard)
```
```
@@ -0,0 +1,235 @@
> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pangolin.net/llms.txt
> Use this file to discover all available pages before exploring further.
# DNS & Networking
> Configure your domain, DNS records, and network settings for Pangolin deployment
<div id="pangolin-toc-cta" className="pangolin-toc-cta-source">
<Card title="Try free on Pangolin Cloud" icon="cloud" href="https://app.pangolin.net/auth/signup" arrow="true" cta="Sign up free">
Fastest way to get started with Pangolin using the hosted control plane. No credit card required.
</Card>
</div>
Pangolin requires proper DNS configuration and network setup to function correctly. This guide covers domain setup, DNS
records, port configuration, and networking considerations.
## DNS Configuration
### Basic DNS Records
You'll need to create A (or AAAA for IPv6) records pointing to your VPS IP address.
<Steps>
<Step title="Create wildcard record">
Create a wildcard subdomain record for your domain:
```
Type: A
Name: *
Value: YOUR_VPS_IP_ADDRESS
TTL: 300 (or default)
```
<Check>
This allows any subdomain (e.g., `app.example.com`, `api.example.com`) to resolve to your VPS.
</Check>
</Step>
<Step title="Create root domain record (optional)">
If you plan to use your root domain as a resource:
```
Type: A
Name: @ (or leave blank)
Value: YOUR_VPS_IP_ADDRESS
TTL: 300 (or default)
```
<Info>
This is only needed if you want to use `example.com` (not just subdomains) as a resource.
</Info>
</Step>
<Step title="Wait for propagation">
DNS changes can take 5 minutes to 48 hours to propagate globally.
<Tip>
Use Google DNS (8.8.8.8) or your provider's DNS to test changes faster.
</Tip>
</Step>
</Steps>
## Port Configuration
### Required Ports
Pangolin requires these ports to be open on your VPS:
<CardGroup cols={2}>
<Card title="TCP Port 80">
**HTTP/SSL Verification**
* Let's Encrypt domain validation
* Non-SSL resources
* Can be disabled with wildcard certs
</Card>
<Card title="TCP Port 443">
**HTTPS Traffic**
* Pangolin web dashboard
* SSL-secured resources
* Essential for operation
</Card>
<Card title="UDP Port 51820">
**Site Tunnels**
This is the default port for sites (Newt) to establish tunnels to the proxy (Gerbil).
</Card>
<Card title="UDP Port 21820">
**Client Tunnels**
This is the default port for clients relaying through Gerbil to newt. This port is only required for clients.
</Card>
</CardGroup>
<Warning>
Always verify your exposed ports (e.g., with [nmap](https://nmap.org/) or [RustScan](https://github.com/bee-san/RustScan)) and ensure you expose **only** the ports that are absolutely necessary. By tunneling out to the VPS, you are effectively including the VPS in your security boundary, so you must secure it as part of your overall network strategy. For more details, see [Dockers port publishing documentation](https://docs.docker.com/engine/network/packet-filtering-firewalls/#port-publishing-and-mapping).
</Warning>
### Docker Port Exposure
By default, Pangolin exposes these ports on all interfaces:
```yaml theme={null}
gerbil:
ports:
- "80:80" # HTTP/SSL verification and non-SSL resources
- "443:443" # HTTPS for web UI and SSL resources
- "51820:51820" # WireGuard for Newt connections
- "21820:21820" # WireGuard for client connections
```
### Firewall Configuration
Ensure your VPS firewall allows these ports:
<Tabs>
<Tab title="Cloud Provider">
Configure security groups/firewall rules in your cloud provider's dashboard to allow:
* TCP ports 80 and 443
* UDP ports 51820 and 21820
</Tab>
<Tab title="UFW (Ubuntu)">
```bash theme={null}
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 51820/udp
sudo ufw allow 21820/udp
sudo ufw enable
```
</Tab>
<Tab title="firewalld (CentOS/RHEL)">
```bash theme={null}
sudo firewall-cmd --permanent --add-port=80/tcp
sudo firewall-cmd --permanent --add-port=443/tcp
sudo firewall-cmd --permanent --add-port=51820/udp
sudo firewall-cmd --permanent --add-port=21820/udp
sudo firewall-cmd --reload
```
</Tab>
</Tabs>
## Internal Network Configuration
### Default Subnet Settings
Pangolin uses these default network settings:
```yaml theme={null}
gerbil:
block_size: 24
site_block_size: 30
subnet_group: 100.89.137.0/20
```
**What this means:**
* **Gerbil network**: Uses first /24 subnet in `100.89.137.0/20` range
* **Site allocation**: Each site gets a /30 subnet (4 IPs)
* **CGNAT range**: Avoids conflicts with most private networks
<Info>
The `100.89.137.0/20` range is in the CGNAT (Carrier-Grade NAT) space, which should avoid conflicts with typical private networks (192.168.x.x, 10.x.x.x, 172.16-31.x.x).
</Info>
<Warning>
**Important**: If this subnet conflicts with your network, change it in your config **before** registering your first Gerbil.
</Warning>
### Customizing Network Settings
If you need to change the default network:
```yaml theme={null}
gerbil:
block_size: 24 # Size of Gerbil's network block
site_block_size: 30 # Size of each site's network block
subnet_group: 10.0.0.0/8 # Custom subnet range
start_port: 51820 # WireGuard server port
```
<Tip>
For heavy WireGuard usage, consider increasing `site_block_size` to 29 (8 IPs) or 28 (16 IPs) per site.
</Tip>
## Docker Networking
### Local Services
When deploying services in Docker alongside Pangolin:
<AccordionGroup>
<Accordion title="Container Communication">
**For services in the same Docker Compose:**
* Use service names as hostnames
* Example: `http://pangolin:8080`
* Docker Compose creates internal network automatically
</Accordion>
<Accordion title="Host Machine Access">
**To access services on the host machine:**
* Use `172.17.0.1` (Docker bridge gateway)
* Or use `host.docker.internal` (Docker Desktop)
* Example: `http://172.17.0.1:3000`
</Accordion>
<Accordion title="External Services">
**For services outside Docker:**
* Use the host's public IP address
* Ensure firewall allows the required ports
* Consider using VPN or secure tunnels
</Accordion>
</AccordionGroup>
@@ -0,0 +1,230 @@
> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pangolin.net/llms.txt
> Use this file to discover all available pages before exploring further.
# Enterprise Edition
> Learn about Enterprise Edition features, licensing, and how to get started
<div id="pangolin-toc-cta" className="pangolin-toc-cta-source">
<Card title="Try free on Pangolin Cloud" icon="cloud" href="https://app.pangolin.net/auth/signup" arrow="true" cta="Sign up free">
Fastest way to get started with Pangolin using the hosted control plane. No credit card required.
</Card>
</div>
When self-hosting Pangolin, you can choose to run the **Community Edition** or the **Enterprise Edition**. Both editions
provide the same core functionality, but the Enterprise Edition unlocks additional features for qualifying users.
## Edition Comparison
### Community Edition
* Fully compliant with AGPL-3 license
* All core Pangolin features included
### Enterprise Edition
* Distributed under Fossorial Commercial License
* Includes all Community Edition features
* Unlocks "Paid Features" for qualifying users (see below)
* Requires valid license key for activation
## Licensing Overview
The Enterprise Edition is distributed under the **Fossorial Commercial License**, which provides different terms based
on your organization's revenue:
<Tabs>
<Tab title="Personal Use">
**Free for individuals and small businesses**
* **Revenue threshold**: Less than \$100,000 USD gross annual revenue
* **License cost**: Free
* **Features**: Full access to all "Paid Features"
* **Usage**: Personal and small business use allowed
<Check>
You still need to acquire a valid license key to unlock Enterprise features, even with free licensing.
</Check>
</Tab>
<Tab title="Business Use">
**Larger businesses require a paid license**
* **Revenue threshold**: \$100,000+ USD gross annual revenue
* **License cost**: Paid license required
* **Features**: Full access to all "Paid Features"
* **Usage**: Business use with commercial terms
<Warning>
Businesses exceeding the revenue threshold must purchase a commercial license to use Enterprise Edition features.
</Warning>
</Tab>
</Tabs>
To see pricing tiers for the self-hosted Enterprise Edition for
businesses, [click here](https://pangolin.net/pricing?hosting=self-host). For more on how to purchase a key,
see [Purchase a license key](/self-host/purchase-license-key).
## Getting Your License Key
<Steps>
<Step title="Create an account">
Visit [app.pangolin.net](https://app.pangolin.net) and create your account.
</Step>
<Step title="Navigate to licenses">
After logging in, go to the **Licenses** section in your account dashboard.
</Step>
<Step title="Fill out the application">
Complete the license application form.
<Tip>
Inaccurate representation is a violation of the license and will result in the license being revoked.
</Tip>
</Step>
<Step title="Receive your key">
Once approved, you'll receive your license key immediately. You can then activate it in your Pangolin instance in the Server Admin panel.
</Step>
</Steps>
## License Requirements
<AccordionGroup>
<Accordion title="How many license keys do I need?">
**One key per Pangolin server instance**
Each host (server) running Pangolin requires its own license key. You cannot share a single key across multiple servers.
<Info>
If you're running multiple Pangolin instances for high availability, each database needs its own license key.
</Info>
</Accordion>
<Accordion title="Can I get a trial license?">
Businesses that don't qualify for free personal licensing can reach out to [support@pangolin.net](mailto:support@pangolin.net) for a trial license key to test Enterprise features before purchasing.
<Warning>
Trial keys are only available for organizations that exceed the personal license revenue threshold.
</Warning>
</Accordion>
<Accordion title="What if I'm unsure about my license type?">
**Contact us for guidance**
If you're uncertain whether you qualify for free licensing or need a commercial license, reach out to [sales@pangolin.net](mailto:sales@pangolin.net) with your organization details.
We'll help determine the appropriate license type and pricing for your use case.
</Accordion>
</AccordionGroup>
## Activating Enterprise Edition
<Steps>
<Step title="Use Enterprise Edition image">
Update your Docker Compose configuration to use the Enterprise Edition image:
```yaml theme={null}
services:
pangolin:
image: fosrl/pangolin:ee-latest # Enterprise Edition
# ... rest of configuration
```
<Warning>
The Enterprise Edition image is tagged with `ee` (e.g., `fosrl/pangolin:ee-latest`) and is different from the Community Edition (`fosrl/pangolin:latest`). Make sure to use the correct image.
</Warning>
</Step>
<Step title="Restart Pangolin">
Restart your Pangolin services to activate the license:
```bash theme={null}
sudo docker compose restart pangolin
```
</Step>
<Step title="Add license key to instance">
Log in to the Pangolin instance via the server admin credentials. Visit the Server Admin panel and naviage to the License section (`/admin/license`). Enter and activate the license key.
<Info>
The license key should be provided exactly as received in your email confirmation.
</Info>
</Step>
<Step title="Verify activation">
Check your Pangolin dashboard to confirm Enterprise Edition features are unlocked.
</Step>
</Steps>
## Support and Contact
For licensing questions and quotes, email: [sales@pangolin.net](mailto:sales@pangolin.net)
Include your organization details and use case for faster assistance.
## Frequently Asked Questions
<AccordionGroup>
<Accordion title="Can I switch between editions?">
You can switch from Community to Enterprise Edition by:
1. Updating your Docker image to `fosrl/ee-<version>`
2. Adding your license key to the configuration
3. Restarting the services
<Warning>
Switching back to Community Edition will disable Enterprise features and may require data migration.
</Warning>
</Accordion>
<Accordion title="Is Enterprise Edition opt-in?">
Enterprise Edition is fully opt-in. You can continue using the Community Edition.
<Check>
The Community Edition will always remain free and open source with full AGPL-3 compliance.
</Check>
</Accordion>
<Accordion title="What happens if my license expires?">
If your license expires or becomes invalid:
* Enterprise features will be disabled
* You can renew your license to restore Enterprise features
</Accordion>
<Accordion title="Can I use Enterprise Edition for personal projects?">
Individuals and small businesses under the revenue threshold can use Enterprise Edition for personal projects at no cost.
<Info>
Personal use is explicitly allowed under the free license terms.
</Info>
</Accordion>
<Accordion title="Are there special license terms for educational institutions, non-profits, or government organizations?">
**No exceptions - same license terms apply**
Educational institutions, non-profit organizations, and government entities are subject to the same license terms as all other organizations. There are no special exceptions or discounts for these groups.
<Info>
If you have questions about how your organization's revenue is calculated for licensing purposes, contact [sales@pangolin.net](mailto:sales@pangolin.net).
</Info>
</Accordion>
</AccordionGroup>
@@ -0,0 +1,129 @@
> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pangolin.net/llms.txt
> Use this file to discover all available pages before exploring further.
# How to Update
> Keep your Pangolin deployment up to date with the latest features and security patches
<div id="pangolin-toc-cta" className="pangolin-toc-cta-source">
<Card title="Try free on Pangolin Cloud" icon="cloud" href="https://app.pangolin.net/auth/signup" arrow="true" cta="Sign up free">
Fastest way to get started with Pangolin using the hosted control plane. No credit card required.
</Card>
</div>
Updating Pangolin is straightforward since it's a collection of Docker images. Simply pull the latest images and restart
the stack. Migration scripts run automatically to update your database and configuration files when needed.
## Before You Update
<Warning>
**Always backup your data before updating.** Copy your `config` directory to a safe location so you can roll back if needed.
</Warning>
<Tip>
**Recommended**: Update incrementally between major versions. For example, update from 1.0.0 → 1.1.0 → 1.2.0 instead of jumping directly from 1.0.0 → 1.2.0.
</Tip>
## Update Process
<Steps>
<Step title="Stop the stack">
Stop all running containers:
```bash theme={null}
sudo docker compose down
```
</Step>
<Step title="Check latest versions">
Find the latest version numbers:
* **Pangolin**: [GitHub Releases](https://github.com/fosrl/pangolin/releases)
* **Gerbil**: [GitHub Releases](https://github.com/fosrl/gerbil/releases)
* **Traefik**: [Docker Hub](https://hub.docker.com/_/traefik)
* **Badger**: [GitHub Releases](https://github.com/fosrl/badger/releases)
<Info>
Look for the latest stable release (not pre-release or beta versions).
</Info>
</Step>
<Step title="Update version numbers">
Edit your `docker-compose.yml` file and update the image versions:
```yaml title="docker-compose.yml" theme={null}
services:
pangolin:
image: fosrl/pangolin:1.7.3 # Update to latest version
# ... rest of config
gerbil:
image: fosrl/gerbil:1.2.1 # Update to latest version
# ... rest of config
traefik:
image: traefik:v3.4.0 # Update if needed
# ... rest of config
```
Increase the Badger version number in `config/traefik/traefik_config.yml`:
```yaml title="traefik_config.yml" theme={null}
experimental:
plugins:
badger:
moduleName: github.com/fosrl/badger
version: v1.3.0 # Update to latest version
```
<Warning>
Update each service you want to upgrade. You can update them individually or all at once.
</Warning>
</Step>
<Step title="Pull new images">
Download the updated Docker images:
```bash theme={null}
sudo docker compose pull
```
</Step>
<Step title="Start the stack">
Start the updated containers:
```bash theme={null}
sudo docker compose up -d
```
</Step>
<Step title="Monitor the update">
Watch the logs to ensure everything starts correctly:
```bash theme={null}
sudo docker compose logs -f
```
</Step>
<Step title="Verify functionality">
Test that everything is working:
1. Access your Pangolin dashboard
2. Check that all sites are accessible
3. Verify tunnel connections (if using Gerbil)
4. Test any custom configurations
<Check>
If everything works, your update is complete!
</Check>
</Step>
</Steps>
@@ -0,0 +1,372 @@
> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pangolin.net/llms.txt
> Use this file to discover all available pages before exploring further.
# Docker Compose
> Deploy Pangolin manually using Docker Compose without the automated installer
<div id="pangolin-toc-cta" className="pangolin-toc-cta-source">
<Card title="Try free on Pangolin Cloud" icon="cloud" href="https://app.pangolin.net/auth/signup" arrow="true" cta="Sign up free">
Fastest way to get started with Pangolin using the hosted control plane. No credit card required.
</Card>
</div>
This guide walks you through setting up Pangolin manually using Docker Compose without the automated installer. This
approach gives you full control over the configuration and deployment process.
This guide assumes you already have a Linux server with Docker and Docker Compose installed. If you don't, please refer
to the [official Docker documentation](https://docs.docker.com/get-docker/) for installation instructions. You must also
have root access to the server.
## Prerequisites
Checkout the [quick install guide](/self-host/quick-install) for more info regarding what is needed before you install
Pangolin.
## File Structure
Create the following directory structure for your Pangolin deployment:
```
.
├── config/
│ ├── config.yml (*)
│ ├── db/
│ │ └── db.sqlite
│ ├── key
│ ├── letsencrypt/
│ │ └── acme.json
│ ├── logs/
│ └── traefik/
│ ├── traefik_config.yml (*)
│ └── dynamic_config.yml (*)
└── docker-compose.yml (*)
```
<Info>
Files marked with `(*)` must be created manually. Volumes and other files are generated automatically by the services.
</Info>
<AccordionGroup>
<Accordion title="Configuration Files">
**`config/config.yml`**: Main Pangolin configuration file
* Contains all Pangolin settings and options
* See [Configuration Guide](/self-host/advanced/config-file) for details
**`config/traefik/traefik_config.yml`**: Traefik static configuration
* Global Traefik settings and entry points
* SSL certificate resolver configuration
**`config/traefik/dynamic_config.yml`**: Traefik dynamic configuration
* HTTP routers and services for Pangolin
* Load balancer and middleware configuration
</Accordion>
<Accordion title="Generated Files">
**`config/db/db.sqlite`**: SQLite database file
* Created automatically on first startup
* Contains all Pangolin data and settings
**`config/key`**: Private key file
* Generated by Gerbil service
* Used for WireGuard tunnel encryption
**`config/letsencrypt/acme.json`**: SSL certificate storage
* Managed by Traefik
* Contains Let's Encrypt certificates
</Accordion>
<Accordion title="Docker Files">
**`docker-compose.yml`**: Service definitions
* Defines Pangolin, Gerbil, and Traefik services
* Network configuration and volume mounts
* Health checks and dependencies
</Accordion>
</AccordionGroup>
<Steps>
<Step title="Create configuration directory">
```bash theme={null}
mkdir -p config/traefik config/db config/letsencrypt config/logs
```
</Step>
<Step title="Create configuration files">
Create the main configuration files (see below):
* `docker-compose.yml` (in project root)
* `config/traefik/traefik_config.yml`
* `config/traefik/dynamic_config.yml`
* `config/config.yml`
</Step>
<Step title="Update domain and email">
Edit the configuration files to replace:
* `pangolin.example.com` with your actual domain
* `admin@example.com` with your email address
<Warning>
Ensure your domain DNS is properly configured to point to your server's IP address.
</Warning>
</Step>
</Steps>
## Starting the Stack
<Steps>
<Step title="Start the services">
```bash theme={null}
sudo docker compose up -d
```
</Step>
<Step title="Monitor startup">
```bash theme={null}
sudo docker compose logs -f
```
</Step>
<Step title="Verify services">
```bash theme={null}
sudo docker compose ps
```
All services should show "Up" status after a few minutes.
</Step>
<Step title="Access the dashboard">
Navigate to `https://your-domain.com/auth/initial-setup` to complete the initial setup.
<Check>
The dashboard should load with SSL certificate automatically configured.
</Check>
</Step>
</Steps>
## Docker Compose Configuration
Create `docker-compose.yml` in your project root:
```yaml title="docker-compose.yml" theme={null}
name: pangolin
services:
pangolin:
image: docker.io/fosrl/pangolin:latest # https://github.com/fosrl/pangolin/releases
container_name: pangolin
restart: unless-stopped
volumes:
- ./config:/app/config
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3001/api/v1/"]
interval: "10s"
timeout: "10s"
retries: 15
gerbil:
image: docker.io/fosrl/gerbil:latest # https://github.com/fosrl/gerbil/releases
container_name: gerbil
restart: unless-stopped
depends_on:
pangolin:
condition: service_healthy
command:
- --reachableAt=http://gerbil:3004
- --generateAndSaveKeyTo=/var/config/key
- --remoteConfig=http://pangolin:3001/api/v1/
volumes:
- ./config/:/var/config
cap_add:
- NET_ADMIN
- SYS_MODULE
ports:
- 51820:51820/udp
- 21820:21820/udp
- 443:443
- 80:80
traefik:
image: docker.io/traefik:v3.6
container_name: traefik
restart: unless-stopped
network_mode: service:gerbil # Ports appear on the gerbil service
depends_on:
pangolin:
condition: service_healthy
command:
- --configFile=/etc/traefik/traefik_config.yml
volumes:
- ./config/traefik:/etc/traefik:ro # Volume to store the Traefik configuration
- ./config/letsencrypt:/letsencrypt # Volume to store the Let's Encrypt certificates
- ./config/traefik/logs:/var/log/traefik # Volume to store Traefik logs
networks:
default:
driver: bridge
name: pangolin
#enable_ipv6: true # activate if your system supports IPv6
```
## Traefik Static Configuration
Create `config/traefik/traefik_config.yml`:
```yaml title="config/traefik/traefik_config.yml" theme={null}
api:
insecure: true
dashboard: true
providers:
http:
endpoint: "http://pangolin:3001/api/v1/traefik-config"
pollInterval: "5s"
file:
filename: "/etc/traefik/dynamic_config.yml"
experimental:
plugins:
badger:
moduleName: "github.com/fosrl/badger"
version: "v1.3.1"
log:
level: "INFO"
format: "common"
maxSize: 100
maxBackups: 3
maxAge: 3
compress: true
certificatesResolvers:
letsencrypt:
acme:
httpChallenge:
entryPoint: web
email: "admin@example.com" # REPLACE WITH YOUR EMAIL
storage: "/letsencrypt/acme.json"
caServer: "https://acme-v02.api.letsencrypt.org/directory"
entryPoints:
web:
address: ":80"
websecure:
address: ":443"
transport:
respondingTimeouts:
readTimeout: "30m"
http:
tls:
certResolver: "letsencrypt"
encodedCharacters:
allowEncodedSlash: true
allowEncodedQuestionMark: true
serversTransport:
insecureSkipVerify: true
ping:
entryPoint: "web"
```
## Traefik Dynamic Configuration
Create `config/traefik/dynamic_config.yml`:
```yaml title="config/traefik/dynamic_config.yml" theme={null}
http:
middlewares:
badger:
plugin:
badger:
disableForwardAuth: true
redirect-to-https:
redirectScheme:
scheme: https
routers:
# HTTP to HTTPS redirect router
main-app-router-redirect:
rule: "Host(`pangolin.example.com`)" # REPLACE WITH YOUR DOMAIN
service: next-service
entryPoints:
- web
middlewares:
- redirect-to-https
- badger
# Next.js router (handles everything except API and WebSocket paths)
next-router:
rule: "Host(`pangolin.example.com`) && !PathPrefix(`/api/v1`)" # REPLACE WITH YOUR DOMAIN
service: next-service
entryPoints:
- websecure
middlewares:
- badger
tls:
certResolver: letsencrypt
# API router (handles /api/v1 paths)
api-router:
rule: "Host(`pangolin.example.com`) && PathPrefix(`/api/v1`)" # REPLACE WITH YOUR DOMAIN
service: api-service
entryPoints:
- websecure
middlewares:
- badger
tls:
certResolver: letsencrypt
# WebSocket router
ws-router:
rule: "Host(`pangolin.example.com`)" # REPLACE WITH YOUR DOMAIN
service: api-service
entryPoints:
- websecure
middlewares:
- badger
tls:
certResolver: letsencrypt
services:
next-service:
loadBalancer:
servers:
- url: "http://pangolin:3002" # Next.js server
api-service:
loadBalancer:
servers:
- url: "http://pangolin:3000" # API/WebSocket server
tcp:
serversTransports:
pp-transport-v1:
proxyProtocol:
version: 1
pp-transport-v2:
proxyProtocol:
version: 2
```
## Pangolin Configuration
Create `config/config.yml` with your Pangolin settings. See the [configuration guide](/self-host/advanced/config-file)
for detailed options and examples.
@@ -0,0 +1,421 @@
> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pangolin.net/llms.txt
> Use this file to discover all available pages before exploring further.
# Unraid Deployment
> Deploy Pangolin on Unraid for local reverse proxy and tunneling
<div id="pangolin-toc-cta" className="pangolin-toc-cta-source">
<Card title="Try free on Pangolin Cloud" icon="cloud" href="https://app.pangolin.net/auth/signup" arrow="true" cta="Sign up free">
Fastest way to get started with Pangolin using the hosted control plane. No credit card required.
</Card>
</div>
## Overview
This guide explains how to use Pangolin and Traefik as a local reverse proxy without Gerbil and its tunneling features.
The second (optional) part will expand on this and show how to enable tunneling by setting up Gerbil.
All containers are available in the Unraid Community Apps (CA) store. If you're not familiar with Unraid, you can find
more information on their [website](https://unraid.net/).
This installation has a lot of moving parts and is a bit non-standard for Unraid because Pangolin and its components
were designed to run as micro-services on a VPS in tunneling mode. However, some may want to use "Local" reverse
proxying on their Unraid server or use their Unraid server as a tunnel controller with Gerbil. For either of these use
cases, follow the steps outlined in this guide.
## Prerequisites
* A working Unraid server.
* A domain name with access to configure DNS and the ability to port forward on your network.
* The networking is the same as for the VPS, just on your local network, so please refer
to [networking page](/self-host/dns-and-networking) for more info.
## Create a Docker Network
Before starting, create a new docker network on Unraid. This will simplify things, and allow the containers to
communicate with each other via their container names. If you already have a network, there is no need to create another
one.
1. Open the web terminal in Unraid.
2. Run the following command:
<Info>
You can use any name you want for the network. We will use `mynetwork` in this guide.
</Info>
```bash theme={null}
docker network create mynetwork
```
For more info on this, see this [tutorial by IBRACORP](https://www.youtube.com/watch?v=7fzBDCI8O2w).
## 1. Setup Pangolin and Traefik
This first part will enable Pangolin to work in "Local" reverse proxy mode. Newt and WireGuard will **not** be able to
be used after finishing this first part. However, if you want to use those features, you still need to follow this first
part of the tutorial because we show how to set up Pangolin and Traefik first.
### Install and Setup Pangolin
#### 1. Create the Config Files
Pangolin uses a yaml file for configuration. If this is not present on start up, the container will throw an error and
exit.
Create a `config.yml` file in the `config` folder.
See the [Configuration](/self-host/advanced/config-file) section for what to put in this file.
```
pangolin/
├─ config/
│ ├─ config.yml
```
#### 2. Install Pangolin via the CA Store
#### 3. Configure Pangolin
Set the network to the one you created earlier.
<Frame caption="Pangolin configuration settings in Unraid">
<img src="https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/pangolin_config.png?fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=82c2ed95ef94b607605cca6863d5bbcd" alt="Pangolin configuration settings in Unraid" data-og-width="2660" width="2660" data-og-height="1318" height="1318" data-path="images/pangolin_config.png" data-optimize="true" data-opv="3" srcset="https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/pangolin_config.png?w=280&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=35fd7218d017780ca8db9979e709edca 280w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/pangolin_config.png?w=560&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=72d221df2af89585529fcd1439b7e830 560w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/pangolin_config.png?w=840&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=21d11e383cb9c0cb2f90aa523912210d 840w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/pangolin_config.png?w=1100&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=589e32bcb38347dd37ef461859218e21 1100w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/pangolin_config.png?w=1650&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=6efe30c3135a93871f2c9ad59ff5750a 1650w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/pangolin_config.png?w=2500&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=55eec73364fc83cbcccc8ab8184b2f65 2500w" />
</Frame>
**Ports:**
Due to the way Pangolin was designed to work with docker compose and a config file, the way it handles ports is a little
different as compared to other popular Unraid containers. For all host ports:
The host ports, container ports, and ports in the config should match for simplicity. This is because the Pangolin
config also has ports in it. If you decide to use a non-default port, you would need to edit the port in the template
and the config file.
For example, to change the port for the WebUI:
* Click edit on the port
* Set the "Container Port" to the new port you want to use
* Set the "Host Port" to the new port you want to use
* Edit Pangolin's config file and set `server.next_port` to the new port you want to use
#### 4. Start the Pangolin Container
<Warning>
Pangolin will not start without a config file. If you have not created the config file or the config file is invalid, the container will throw an error and exit.
</Warning>
#### 5. Log in to the dashboard
After successful installation:
1. Complete the initial admin user setup via the dashboard at `https://<your-domain>/auth/initial-setup`
2. You can log in using the admin email and password you provided
3. Create your first "Local" site for local reverse proxying
### Install and Setup Traefik
Before starting with Traefik, shut down the Pangolin container.
#### 1. Create the Config Files
Update the appdata path with new files for Traefik. At this point there may be some extra files generated by Pangolin.
```
pangolin/
├─ config/
│ ├─ config.yml
│ ├─ letsencrypt/
│ ├─ traefik/
│ │ ├─ dynamic_config.yml
│ │ ├─ traefik_config.yml
```
**`pangolin/config/traefik/traefik_config.yml`:**
```yaml title="pangolin/config/traefik/traefik_config.yml theme={null}
api:
insecure: true
dashboard: true
providers:
http:
endpoint: "http://pangolin:3001/api/v1/traefik-config"
pollInterval: "5s"
file:
filename: "/etc/traefik/dynamic_config.yml"
experimental:
plugins:
badger:
moduleName: "github.com/fosrl/badger"
version: "v1.3.0"
log:
level: "INFO"
format: "common"
certificatesResolvers:
letsencrypt:
acme:
httpChallenge:
entryPoint: web
email: admin@example.com # REPLACE THIS WITH YOUR EMAIL
storage: "/letsencrypt/acme.json"
caServer: "https://acme-v02.api.letsencrypt.org/directory"
entryPoints:
web:
address: ":80"
websecure:
address: ":443"
transport:
respondingTimeouts:
readTimeout: "30m"
http:
tls:
certResolver: "letsencrypt"
serversTransport:
insecureSkipVerify: true
```
**`pangolin/config/traefik/dynamic_config.yml`:**
The dynamic configuration file is where you define the HTTP routers and services for the Pangolin frontend and backend.
Below is an example configuration for a Next.js frontend and an API backend.
The domain you enter here is what will be used to access the main Pangolin dashboard. Make sure you have the DNS set up
correctly for this domain. Point it to the IP address of the server running Pangolin.
```yaml title="pangolin/config/traefik/dynamic_config.yml" theme={null}
http:
middlewares:
badger:
plugin:
badger:
disableForwardAuth: true
redirect-to-https:
redirectScheme:
scheme: https
routers:
# HTTP to HTTPS redirect router
main-app-router-redirect:
rule: "Host(`pangolin.example.com`)" # REPLACE THIS WITH YOUR DOMAIN
service: next-service
entryPoints:
- web
middlewares:
- redirect-to-https
- badger
# Next.js router (handles everything except API and WebSocket paths)
next-router:
rule: "Host(`pangolin.example.com`) && !PathPrefix(`/api/v1`)" # REPLACE THIS WITH YOUR DOMAIN
service: next-service
entryPoints:
- websecure
middlewares:
- badger
tls:
certResolver: letsencrypt
# API router (handles /api/v1 paths)
api-router:
rule: "Host(`pangolin.example.com`) && PathPrefix(`/api/v1`)" # REPLACE THIS WITH YOUR DOMAIN
service: api-service
entryPoints:
- websecure
middlewares:
- badger
tls:
certResolver: letsencrypt
# WebSocket router
ws-router:
rule: "Host(`pangolin.example.com`)" # REPLACE THIS WITH YOUR DOMAIN
service: api-service
entryPoints:
- websecure
middlewares:
- badger
tls:
certResolver: letsencrypt
services:
next-service:
loadBalancer:
servers:
- url: "http://pangolin:3002" # Next.js server
api-service:
loadBalancer:
servers:
- url: "http://pangolin:3000" # API/WebSocket server
```
#### 2. Install Traefik via the CA Store
This section will use the Traefik template from the "IBRACORP" repository. If you already have a Traefik installation
running, you should manually configure your Traefik config to work with Pangolin.
<Frame caption="Traefik repository selection in Community Apps">
<img src="https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/traefik_repo.png?fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=4cf656c02b423be2793fe2c1d744ef08" width="400" alt="Traefik repository selection in Community Apps" data-og-width="836" data-og-height="600" data-path="images/traefik_repo.png" data-optimize="true" data-opv="3" srcset="https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/traefik_repo.png?w=280&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=5fad4a6d55cf78260027ba03c0578343 280w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/traefik_repo.png?w=560&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=08c19916403b2e4eac80d6bd097b2d26 560w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/traefik_repo.png?w=840&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=f84482b7a84601de8e415244f663e3eb 840w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/traefik_repo.png?w=1100&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=88d8f8c01b829cdb05c9881d73e13cff 1100w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/traefik_repo.png?w=1650&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=921cd181659de37dcf26cf103c8c7392 1650w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/traefik_repo.png?w=2500&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=7f81d7c4c888f16ea49afbfc181b69f3 2500w" />
</Frame>
#### 3. Configure Traefik
<Frame caption="Traefik configuration settings in Unraid">
<img src="https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/traefik_config.png?fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=17d9c3ce28f805cb4969fe30e1a972a4" alt="Traefik configuration settings in Unraid" data-og-width="2560" width="2560" data-og-height="2400" height="2400" data-path="images/traefik_config.png" data-optimize="true" data-opv="3" srcset="https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/traefik_config.png?w=280&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=721083ebeeb0b3070ae28b7a2c762138 280w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/traefik_config.png?w=560&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=bfa0d45b6b2f5d59a8c305c302184d4b 560w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/traefik_config.png?w=840&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=cb53166c942cfb21a60519967afeba78 840w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/traefik_config.png?w=1100&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=101e742b4056ff8cba5530250fb4d173 1100w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/traefik_config.png?w=1650&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=7b00179786fe15ca73b011bd07c87bb5 1650w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/traefik_config.png?w=2500&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=184cfd96dc9c0ae6106598861be3bfa8 2500w" />
</Frame>
<Info>
Please refer to the official Traefik docs for more information on the Traefik configuration beyond this guide.
</Info>
**Match your config to the one above. You will have to remove some of the default variables in the template that are not
needed. You can always add them back if you need them later.**
**Network Type:**
Set the network type to the one you created earlier.
**Post Arguments:**
Tell Traefik where the config file is located by adding the following to the "Post Arguments" field. This is not the
host path, but the path inside the container.
```bash theme={null}
--configFile=/etc/traefik/traefik_config.yml
```
**Config Folder:**
If you're using the Traefik config generated by Pangolin, point this to the same appdata path as Pangolin, but append
`/traefik`, like this: `<appdata>/config/traefik`.
**Lets Encrypt (Host Path 2 in screenshot):**
Traefik will store the certification information here. You can make this path anywhere you want. For simplicity, we're
placing it in the same config path at `<appdata>/config/letsencrypt`.
**Ports:**
You will need to port forward the https and http ports listed in the config on your network's router.
#### 4. Port Forwarding
You will need to port forward the ports you set in the Traefik config on your network's router. This is so that Traefik
can receive traffic from the internet. You should forward 443 to the https port and 80 to the http port you set in the
Traefik config.
## 2. Add Gerbil for Tunneling (Optional)
<Info>
If you do not want to use the tunneling feature of Pangolin and only want to use it as a local reverse proxy, you can stop here.
</Info>
Before setting up Gerbil, shut down Traefik and Pangolin.
If you plan to use tunneling features of Pangolin with Newt or WireGuard, you will need to add Gerbil to the stack.
Gerbil is the tunnel controller for Pangolin and is used to manage the tunnels between the Pangolin server and the
client.
Luckily, adding Gerbil is fairly easy.
The important concept to understand going forward, is we need to network Traefik through Gerbil. All Traefik traffic
goes through the Gerbil container and exits.
#### 1. Install Gerbil via the CA Store
#### 2. Configure Gerbil
Set the network to the one you created earlier.
<Frame caption="Gerbil configuration settings in Unraid">
<img src="https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/gerbil_config.png?fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=aa621c43a60288ca8af10fc2df3acc68" alt="Gerbil configuration settings in Unraid" data-og-width="2428" width="2428" data-og-height="1400" height="1400" data-path="images/gerbil_config.png" data-optimize="true" data-opv="3" srcset="https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/gerbil_config.png?w=280&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=d6cdc205135d248a2e7c9b3fd53289c0 280w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/gerbil_config.png?w=560&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=472e756ededa6cc1c7b47b8d16f44caa 560w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/gerbil_config.png?w=840&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=7520f4b8a3cfd9991a85fbf75b7580c8 840w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/gerbil_config.png?w=1100&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=a86fc01b7abb45e72c2747d7b090ddf1 1100w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/gerbil_config.png?w=1650&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=10d282b22314729afd7e2fd380c40b62 1650w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/gerbil_config.png?w=2500&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=3b662c5cb21fcb65d76b20560a999b91 2500w" />
</Frame>
**Important things to consider:**
**Internal Communication:**
Anywhere you see `http://pangolin:3001` must match. The hostname should be the name of the Pangolin container on the
docker network you're using. This is because it is routed using the internal docker DNS address. The port must also
match the port you have set for the internal port in Pangolin. These defaults will work unless you changed these values
earlier when setting up Pangolin.
**WireGuard Port:**
<Warning>
You **must** use the default port of `51822` for WireGuard in the Gerbil container. Using any other port may cause connection issues that are difficult to debug.
Make sure this is also reflected in your Pangolin `config.yml`:
```yml theme={null}
gerbil:
start_port: 51822
```
See [this GitHub issue comment](https://github.com/fosrl/pangolin/issues/227#issuecomment-2781608815) for more details.
</Warning>
The port you use for WireGuard must also match what you set the port to in the Pangolin config. By default we use a
slightly different port than the standard WireGuard port to avoid conflicts with the built in WireGuard server in
Unraid.
**HTTP and HTTPS Ports:**
You must open these ports because Traefik will be routed through Gerbil. These ports should match the ports you set in
the Traefik config earlier. In the next step, we will set the network mode for Traefik which will close the ports on the
Traefik side, and prevent conflicts. Before doing this, if you start the Traefik container at the same time as the
Gerbil container with the same ports mapped to the host, you will get an error.
#### 3. Network Traefik Through Gerbil
As discussed earlier we need to network Traefik through Gerbil. This is pretty easy. We will do all of this in the
Traefik container settings.
Toggle advanced settings, and add the following to the "Extra Parameters" section.
```bash theme={null}
--net=container:Gerbil
```
Then, set "Network Type" to "None".
<Frame caption="Traefik networking configuration through Gerbil">
<img src="https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/traefik_networking.png?fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=789d989c26d6996eed6a573ac88a415d" alt="Traefik networking configuration through Gerbil" data-og-width="1840" width="1840" data-og-height="380" height="380" data-path="images/traefik_networking.png" data-optimize="true" data-opv="3" srcset="https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/traefik_networking.png?w=280&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=c41160a0a7bd3da0b0d5287cffd8ecaf 280w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/traefik_networking.png?w=560&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=6d67ef4a3501693616b83c33a6c9a50d 560w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/traefik_networking.png?w=840&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=7745b9983bc54245dc269ea73b8333c2 840w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/traefik_networking.png?w=1100&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=7de530b5b9ad061f385d673fb2e85349 1100w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/traefik_networking.png?w=1650&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=9bfe596ac027d7539d33ce511885e8d1 1650w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/traefik_networking.png?w=2500&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=8a0b3661cb97b152766ad15e8b8c555e 2500w" />
</Frame>
#### 4. Start the stack
We recommend to start the whole stack in the following order:
1. Pangolin
2. Gerbil
3. Traefik
#### 5. Port Forwarding
You will need to port forward the WireGuard port you set in the Gerbil config on your network's router. This is so that
the client can connect to the server.
#### 6. Verify Tunnels are Functional
Your logs for Gerbil should look something like this:
<Info>
You probably won't have the peer connection messages but in general, you should see the WireGuard interface being started.
</Info>
<Frame caption="Gerbil logs showing WireGuard interface startup">
<img src="https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/gerbil_logs.png?fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=51b8f7e56caee9e986dfa8557acb4cd9" alt="Gerbil logs showing WireGuard interface startup" data-og-width="1085" width="1085" data-og-height="212" height="212" data-path="images/gerbil_logs.png" data-optimize="true" data-opv="3" srcset="https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/gerbil_logs.png?w=280&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=94a0ef8103c4c87081ad1280fe62e419 280w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/gerbil_logs.png?w=560&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=62ba262d2bd3d54177461a17490956e8 560w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/gerbil_logs.png?w=840&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=ba0418c4c54a25e16cd6b72a7cc49fb5 840w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/gerbil_logs.png?w=1100&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=b4f6adb01f84f7adb153fa5aaa0997af 1100w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/gerbil_logs.png?w=1650&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=1240163eaf35b46aebb7f70b3e197052 1650w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/gerbil_logs.png?w=2500&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=4298b8ccd8b21edaa9ef801b47073d2f 2500w" />
</Frame>
Log back into the Pangolin dashboard and create a new site with Newt or basic WireGuard. Copy the credentials to your
client and connect. You should see the tunnel status change to "Online" after a few moments if the connection is
successful. Remember to also monitor the logs on the client and server.
@@ -0,0 +1,164 @@
> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pangolin.net/llms.txt
> Use this file to discover all available pages before exploring further.
# Quick Install Guide
> Deploy your own fully self-hosted instance of Pangolin Community Edition
<div id="pangolin-toc-cta" className="pangolin-toc-cta-source">
<Card title="Try free on Pangolin Cloud" icon="cloud" href="https://app.pangolin.net/auth/signup" arrow="true" cta="Sign up free">
Fastest way to get started with Pangolin using the hosted control plane. No credit card required.
</Card>
</div>
<iframe className="w-full aspect-video rounded-xl" src="https://www.youtube.com/embed/0upWrqkJPy8?si=q0D-uR1IHuddaqeT" title="Pangolin Quick Install Guide" frameBorder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen />
## Prerequisites
Before you begin, ensure you have:
* **Linux server** with root access and public IP address
* **Domain name** pointing to your server's IP address for the dashboard
* **Email address** for Let's Encrypt SSL certificates and admin log in
* **Open ports on firewall** for 80 (TCP), 443 (TCP), 51820 (UDP), and 21820 (UDP for clients)
<Tip>
**Recommended**: Ubuntu 20.04+ or Debian 11+ for best compatibility and performance.
</Tip>
## Choose Your Server
Need help choosing? See our [complete VPS guide](/self-host/choosing-a-vps) for suggestions.
## DNS & Networking
Before installing Pangolin, ensure you've set up DNS for your domain(s) and opened the required port on your firewall.
See our guide on [DNS & networking](/self-host/dns-and-networking) for more information.
## Installation Process
<Steps>
<Step title="Download the installer">
Connect to your server via SSH and download the installer:
```bash theme={null}
curl -fsSL https://static.pangolin.net/get-installer.sh | bash
```
The installer supports both AMD64 (x86\_64) and ARM64 architectures.
</Step>
<Step title="Run the installer">
Execute the installer with root privileges:
```bash theme={null}
sudo ./installer
```
The installer places all files in the current directory. Move the installer to your desired installation directory before running it.
</Step>
<Step title="Configure basic settings">
The installer will prompt you for essential configuration:
* **Edition**: Choose Community Edition or [Enterprise Edition](/self-host/enterprise-edition). Review the edition differences before continuing.
* **Base Domain**: Enter your root domain without subdomains (e.g., `example.com`)
* **Dashboard Domain**: Press Enter to accept the default `pangolin.example.com` or enter a custom domain
* **Let's Encrypt Email**: Provide an email for SSL certificates and admin login
* **Tunneling**: Choose whether to install Gerbil for tunneled connections (default: yes). You can run Pangolin without tunneling. It will function as a standard reverse proxy.
</Step>
<Step title="Configure email (optional)">
<Tip>
Email functionality is optional and can be added later.
</Tip>
Choose whether to enable SMTP email functionality:
* **Default**: No (recommended for initial setup)
* **If enabled**: You'll need SMTP server details (host, port, username, password)
</Step>
<Step title="Start installation">
Confirm that you want to install and start the containers:
* The installer will pull Docker images (pangolin, gerbil, traefik)
* Containers will be started automatically
* This process takes 2-3 minutes depending on your internet connection
You'll see progress indicators as each container is pulled and started.
</Step>
<Step title="Install CrowdSec (optional)">
The installer will ask if you want to install CrowdSec for additional security:
* **Default**: No (recommended for initial setup)
* **If enabled**: You'll need to confirm you're willing to manage CrowdSec configuration
<Warning>
CrowdSec adds complexity and requires manual configuration for optimal security. Only enable if you're comfortable managing it.
</Warning>
<Info>
CrowdSec can be installed later if needed. The basic installation provides sufficient security for most use cases.
</Info>
</Step>
</Steps>
## Post-Installation Setup
Once installation completes successfully, you'll see:
```
Installation complete!
To complete the initial setup, please visit:
https://pangolin.example.com/auth/initial-setup
```
<Steps>
<Step title="Access the dashboard">
Navigate to the URL shown in the installer output:
```
https://<your-dashboard-domain>/auth/initial-setup
```
<Check>
The dashboard should load with SSL certificate automatically configured. It might take a few minutes for the first cert to validate, so don't worry if the browser throws an insecure warning.
</Check>
</Step>
<Step title="Create admin account">
Complete the initial admin user setup:
* Enter your admin email address
* Set a strong password
* Verify your email (if email is configured)
<Warning>
Use a strong, unique password for your admin account. This account has full system access.
</Warning>
</Step>
<Step title="Create your first organization">
After logging in:
1. Enter organization name and description
2. Click "Create Organization"
<Check>
You're now ready to start adding applications and configuring your reverse proxy!
</Check>
</Step>
</Steps>
@@ -0,0 +1,155 @@
> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pangolin.net/llms.txt
> Use this file to discover all available pages before exploring further.
# Supporter Program
> Support Pangolin development and remove UI elements with a supporter key
<div id="pangolin-toc-cta" className="pangolin-toc-cta-source">
<Card title="Try free on Pangolin Cloud" icon="cloud" href="https://app.pangolin.net/auth/signup" arrow="true" cta="Sign up free">
Fastest way to get started with Pangolin using the hosted control plane. No credit card required.
</Card>
</div>
Pangolin self-hosted will always be free and open source, but maintaining the project takes time and resources. The
supporter program helps fund ongoing development — including bug fixes, new features, and community support.
<Note>
**Business & Enterprise Users:** For larger organizations or teams requiring advanced features, consider our self-serve enterprise license and [Enterprise Edition](/self-host/enterprise-edition). [Learn more](https://pangolin.net/pricing?hosting=self-host)
</Note>
## Supporter Tiers
<Tabs>
<Tab title="Limited Supporter ($25)">
**Perfect for small teams**
* **User limit**: 5 or fewer users
* **Support button**: Removed from UI
* **Usage**: Unlimited servers and installations
* **Upgrade**: Available to Full Supporter
<Warning>
Once you add your 6th user, the support button will return. Remove a user or upgrade to Full Supporter to hide it again.
</Warning>
</Tab>
<Tab title="Full Supporter ($95)">
**Perfect for larger teams**
* **User limit**: Unlimited users
* **Support button**: Permanently removed
* **Usage**: Unlimited servers and installations
* **Best value**: For growing teams
<Check>
The support button and other marks will never return, regardless of user count.
</Check>
</Tab>
</Tabs>
<Frame caption="Supporter tier comparison showing Limited vs Full Supporter benefits">
<img src="https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/supporter-tiers.png?fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=9189003609b69821e87194670136f72f" alt="Supporter tier comparison showing Limited vs Full Supporter benefits" data-og-width="5110" width="5110" data-og-height="2650" height="2650" data-path="images/supporter-tiers.png" data-optimize="true" data-opv="3" srcset="https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/supporter-tiers.png?w=280&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=c3e5099a5e2e89ddf1ba18b5744d29be 280w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/supporter-tiers.png?w=560&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=4b7e9f9607b6293b74a60a67b602da0f 560w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/supporter-tiers.png?w=840&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=20e087bee3d236a0f0f3c4200a4b8c79 840w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/supporter-tiers.png?w=1100&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=d81755a386b1878d21b8d103f26af400 1100w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/supporter-tiers.png?w=1650&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=3aea36045d8be256b628dbc88ce91347 1650w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/supporter-tiers.png?w=2500&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=189f52cfcad91390a188419c27ad239d 2500w" />
</Frame>
## How to Get Your Supporter Key
<Steps>
<Step title="Purchase a tier">
Go to our [GitHub Sponsors page](https://github.com/sponsors/fosrl) and purchase either:
* **Limited Supporter**: \$25 one-time
* **Full Supporter**: \$95 one-time
</Step>
<Step title="Get your key">
After purchase, visit [supporters.fossorial.io](https://supporters.fossorial.io) and:
1. Log in with your GitHub account
2. Copy your supporter key
</Step>
<Step title="Redeem in Pangolin">
In your Pangolin dashboard:
1. Click the supporter button
2. Enter your supporter key
3. Click "Redeem"
</Step>
</Steps>
<Frame caption="Pangolin supporter key redemption interface">
<img src="https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/redeem-key.png?fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=09685b9c96df9c51afdecbaa4826bd6e" alt="Pangolin supporter key redemption interface" data-og-width="1644" width="1644" data-og-height="1550" height="1550" data-path="images/redeem-key.png" data-optimize="true" data-opv="3" srcset="https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/redeem-key.png?w=280&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=d38a565c2c1c4208faf1abe17f0e9f70 280w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/redeem-key.png?w=560&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=2fb9a7c10febb10f006170bcdc8a607b 560w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/redeem-key.png?w=840&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=553787d00bc2325d70b48a08d6153aac 840w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/redeem-key.png?w=1100&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=0fd602e797b9758191d42e8538c39f32 1100w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/redeem-key.png?w=1650&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=9a56af8d967f396ade2e23fc8801880c 1650w, https://mintcdn.com/fossorial/u-2SUNWyK_LJL3sU/images/redeem-key.png?w=2500&fit=max&auto=format&n=u-2SUNWyK_LJL3sU&q=85&s=311ac06fa047941bfbb0d662642947ee 2500w" />
</Frame>
## Frequently Asked Questions
<AccordionGroup>
<Accordion title="Can we use Paypal or other payment methods?">
Github sponsors does not currently support other payment methods. We hope to add more options in the future.
</Accordion>
<Accordion title="How many servers can I use my key on?">
**Unlimited usage**
You can use your supporter key on as many servers and installations as you want. There are no restrictions on the number of deployments.
</Accordion>
<Accordion title="Can I upgrade my tier?">
**Yes, but requires new purchase**
To upgrade from Limited to Full Supporter:
1. Purchase the Full Supporter (\$95) tier on GitHub
2. Your account will be automatically upgraded
3. Restart your Pangolin server to update the status
<Warning>
Due to GitHub's tier system, you must purchase the higher tier even if you already have the lower one. This results in an extra donation, which we appreciate!
</Warning>
</Accordion>
<Accordion title="Can I hide the button without paying?">
**Temporary hiding available**
You can click "Hide for 7 days" at the bottom of the supporter dialog to temporarily hide the button without purchasing a supporter key.
</Accordion>
<Accordion title="What if I buy the same tier again?">
**Thanks for the extra donation!**
You can only obtain one supporter key per tier. Additional purchases of the same tier won't change your key, but we appreciate the extra support!
</Accordion>
<Accordion title="Can I get a refund?">
**No refunds available**
GitHub Sponsors does not allow us to refund donations. Please make sure you're comfortable supporting the project before purchasing a tier.
</Accordion>
<Accordion title="What happens if I exceed my user limit?">
**Limited Supporter restrictions**
If you have a Limited Supporter key and add your 6th user:
* The support button will return to the UI
* You can either remove a user or upgrade to Full Supporter
* Your key remains valid for other installations
<Info>
Full Supporter keys have no user limits.
</Info>
</Accordion>
</AccordionGroup>