mirror of
https://github.com/fosrl/docs-v2.git
synced 2026-09-27 16:29:07 +02:00
port mintlify to fumadocs
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
---
|
||||
title: "Cloudflare Proxy"
|
||||
---
|
||||
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
|
||||
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
|
||||
curl ifconfig.io
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Update configuration">
|
||||
Add the IP to your `config.yml`:
|
||||
|
||||
```yaml
|
||||
gerbil:
|
||||
base_endpoint: "104.21.16.1" # Replace with your actual IP
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Restart services">
|
||||
Restart Pangolin to apply the changes:
|
||||
|
||||
```bash
|
||||
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"
|
||||
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
|
||||
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 from sites or clients, ensure that websockets are enabled in Cloudflare:
|
||||
|
||||
<Frame>
|
||||
<img src="/images/cf_websocket_box.png" alt="Cloudflare dashboard WebSockets setting toggled on" width="600"/>
|
||||
</Frame>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,184 @@
|
||||
---
|
||||
title: "Internal CLI (pangctl)"
|
||||
description: "Command-line tool for managing your Pangolin instance"
|
||||
---
|
||||
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
|
||||
docker exec -it pangolin pangctl <command>
|
||||
```
|
||||
|
||||
## Available Commands
|
||||
|
||||
To see all available commands:
|
||||
|
||||
```bash
|
||||
docker exec -it pangolin pangctl --help
|
||||
```
|
||||
|
||||
## Set Admin Credentials
|
||||
|
||||
Set or reset admin credentials for your Pangolin instance:
|
||||
|
||||
```bash
|
||||
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>
|
||||
|
||||
## Set Server Admin
|
||||
|
||||
Add or remove server admin status for a user by email address:
|
||||
|
||||
```bash
|
||||
docker exec -it pangolin pangctl set-server-admin --email "admin@example.com"
|
||||
```
|
||||
|
||||
To remove server admin status:
|
||||
|
||||
```bash
|
||||
docker exec -it pangolin pangctl set-server-admin --email "admin@example.com" --remove
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
- `--email` (required): User email address
|
||||
- `--remove` (optional, default: `false`): Remove server admin status from the user
|
||||
|
||||
<Warning>
|
||||
At least one server admin must always exist. The command fails if you try to remove server admin status from the last remaining server admin.
|
||||
</Warning>
|
||||
|
||||
## Clear Exit Nodes
|
||||
|
||||
Clear all exit nodes from the database:
|
||||
|
||||
```bash
|
||||
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
|
||||
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>
|
||||
|
||||
## Disable User 2FA
|
||||
|
||||
Disable two-factor authentication for a user by email address. Sets `twoFactorEnabled` to false and clears the user's 2FA secret:
|
||||
|
||||
```bash
|
||||
docker exec -it pangolin pangctl disable-user-2fa --email "user@example.com"
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
- `--email` (required): User email address
|
||||
|
||||
<Warning>
|
||||
This command disables 2FA for the specified user and clears their stored 2FA secret. The user can re-enable 2FA from their account settings after signing in.
|
||||
</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
|
||||
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
|
||||
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
|
||||
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>
|
||||
|
||||
## Generate Org CA Keys
|
||||
|
||||
Generate an SSH CA public/private key pair for an organization and store them in the database. The private key is encrypted with the server secret.
|
||||
|
||||
```bash
|
||||
docker exec -it pangolin pangctl generate-org-ca-keys --orgId "org-123"
|
||||
```
|
||||
|
||||
## Clear Certificates
|
||||
|
||||
Clear all certificates from the database to be reinserted by the server when syncing from acme.json files or using Pangolin DNS.
|
||||
|
||||
```bash
|
||||
docker exec -it pangolin pangctl clear-certificates
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
- `--orgId` (required): The organization ID
|
||||
- `--secret` (optional): Server secret used to encrypt the CA private key. If omitted, the secret is read from the config file (`config.yml` or `config.yaml` in the config directory).
|
||||
- `--force` (optional, default: `false`): Overwrite existing CA keys for the organization if they already exist
|
||||
|
||||
<Warning>
|
||||
If the organization already has CA keys, the command fails unless you pass `--force`. Using `--force` overwrites the existing keys; ensure you have a backup or understand the impact before overwriting.
|
||||
</Warning>
|
||||
@@ -0,0 +1,143 @@
|
||||
---
|
||||
title: "Database Options"
|
||||
description: "Configure SQLite or PostgreSQL database for Pangolin"
|
||||
---
|
||||
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"
|
||||
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"
|
||||
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>
|
||||
|
||||
### Read Replicas
|
||||
|
||||
Pangolin can distribute read queries across one or more PostgreSQL read replicas while always sending writes to the primary database. This is useful for scaling read-heavy workloads.
|
||||
|
||||
<Note>
|
||||
Replicas are chosen at random for each read query (not round-robin). Writes (`insert`, `update`, `delete`) always go to the primary database. A small number of time-sensitive reads (where the app must see its own recent writes) are also routed directly to the primary database regardless of replicas being configured.
|
||||
</Note>
|
||||
|
||||
#### Using the Configuration File
|
||||
|
||||
Add a `replicas` array under `postgres` in your `config.yml`, with one entry per replica:
|
||||
|
||||
```yaml title="config.yml"
|
||||
postgres:
|
||||
connection_string: postgresql://<user>:<password>@<primary-host>:<port>/<database>
|
||||
replicas:
|
||||
- connection_string: postgresql://<user>:<password>@<replica-host-1>:<port>/<database>
|
||||
- connection_string: postgresql://<user>:<password>@<replica-host-2>:<port>/<database>
|
||||
```
|
||||
|
||||
#### Using Environment Variables
|
||||
|
||||
You can instead provide replica connection strings with the `POSTGRES_REPLICA_CONNECTION_STRINGS` environment variable, as a comma-separated list. This must be used together with `POSTGRES_CONNECTION_STRING` for the primary database - the two env vars replace the entire `postgres.connection_string` / `postgres.replicas` config as a unit.
|
||||
|
||||
```bash title=".env"
|
||||
POSTGRES_CONNECTION_STRING=postgresql://<user>:<password>@<primary-host>:<port>/<database>
|
||||
POSTGRES_REPLICA_CONNECTION_STRINGS=postgresql://<user>:<password>@<replica-host-1>:<port>/<database>,postgresql://<user>:<password>@<replica-host-2>:<port>/<database>
|
||||
```
|
||||
|
||||
```yaml title="docker-compose.yml"
|
||||
services:
|
||||
pangolin:
|
||||
image: fosrl/pangolin:postgresql-latest # Don't use latest in production
|
||||
environment:
|
||||
POSTGRES_CONNECTION_STRING: postgresql://<user>:<password>@<primary-host>:<port>/<database>
|
||||
POSTGRES_REPLICA_CONNECTION_STRINGS: "postgresql://<user>:<password>@<replica-host-1>:<port>/<database>,postgresql://<user>:<password>@<replica-host-2>:<port>/<database>"
|
||||
```
|
||||
|
||||
<Note>
|
||||
The same pattern applies to the optional dedicated logs database: `postgres_logs.replicas` in the config file, or the `POSTGRES_LOGS_REPLICA_CONNECTION_STRINGS` environment variable (comma-separated) alongside `POSTGRES_LOGS_CONNECTION_STRING`.
|
||||
</Note>
|
||||
|
||||
See the [`postgres.replicas` reference](/self-host/advanced/config-file#database-configuration) for the full config schema, and the [Environment Variables reference](/self-host/advanced/config-file#environment-variables) for all supported variables.
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
---
|
||||
title: "Enable ASN Lookup"
|
||||
description: "Configuration requirements to enable ASN lookup features in Pangolin"
|
||||
---
|
||||
|
||||
Pangolin uses an ASN database to map request IP addresses to autonomous systems, such as cloud providers, residential ISPs, VPN providers, and corporate networks. That ASN data powers ASN access rules and ASN blocking patterns.
|
||||
|
||||
To enable ASN lookup features in Pangolin self-hosted, download the MaxMind ASN database, place it in the `config/` directory, and point Pangolin at the database 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>
|
||||
You can automate this process with a MaxMind Docker container. See the [GeoLite2 Automation community guide](/self-host/community-guides/geolite2automation) for an example.
|
||||
</Tip>
|
||||
|
||||
## Install with the Installer
|
||||
|
||||
You can use the installer to download and place the database for you. Download the latest installer:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://static.pangolin.net/get-installer.sh | bash
|
||||
```
|
||||
|
||||
Then run the installer again:
|
||||
|
||||
```bash
|
||||
./installer
|
||||
```
|
||||
|
||||
## Manual Installation
|
||||
|
||||
<Steps>
|
||||
<Step title="Download and extract the ASN database">
|
||||
Download and extract the GeoLite2 ASN database using the following commands:
|
||||
|
||||
```bash
|
||||
# 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 `config/config.yml` to include the following entry:
|
||||
|
||||
```yaml
|
||||
server:
|
||||
maxmind_asn_path: "./config/GeoLite2-ASN.mmdb"
|
||||
```
|
||||
</Step>
|
||||
<Step title="Restart Pangolin">
|
||||
Restart your Pangolin instance to apply the changes:
|
||||
|
||||
```bash
|
||||
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.
|
||||
|
||||
<Note>
|
||||
After the ASN lookup database is enabled, use [access control rules](/manage/access-control/rules) or the [ASN Blocking](/manage/asnblocking) guide to create blocking or allow rules.
|
||||
</Note>
|
||||
@@ -0,0 +1,73 @@
|
||||
---
|
||||
title: "Enable Geo-location"
|
||||
description: "Configuration requirements to enable geolocation features in Pangolin"
|
||||
---
|
||||
|
||||
Pangolin uses a GeoIP database to map request IP addresses to approximate locations. That location data powers multiple features, including country and region access rules, geo-blocking patterns, and analytics.
|
||||
|
||||
To enable geo-location features in Pangolin self-hosted, download a MaxMind GeoIP database, place it in the `config/` directory, and point Pangolin at the database 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>
|
||||
You can automate this process with a MaxMind Docker container. See the [GeoLite2 Automation community guide](/self-host/community-guides/geolite2automation) for an example.
|
||||
</Tip>
|
||||
|
||||
## Install with the Installer
|
||||
|
||||
You can use the installer to download and place the database for you. Download the latest installer:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://static.pangolin.net/get-installer.sh | bash
|
||||
```
|
||||
|
||||
Then run the installer again:
|
||||
|
||||
```bash
|
||||
./installer
|
||||
```
|
||||
|
||||
## Manual Installation
|
||||
|
||||
<Steps>
|
||||
<Step title="Download and extract the GeoIP database">
|
||||
Download and extract the GeoLite2 Country database using the following commands:
|
||||
|
||||
```bash
|
||||
# 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 `config/config.yml` to include the following entry:
|
||||
|
||||
```yaml
|
||||
server:
|
||||
maxmind_db_path: "./config/GeoLite2-Country.mmdb"
|
||||
```
|
||||
</Step>
|
||||
<Step title="Restart Pangolin">
|
||||
Restart your Pangolin instance to apply the changes:
|
||||
|
||||
```bash
|
||||
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.
|
||||
|
||||
<Note>
|
||||
After the geo-location database is enabled, use [access control rules](/manage/access-control/rules) or the [Geo-blocking](/manage/geoblocking) guide to create blocking or allow rules.
|
||||
</Note>
|
||||
@@ -0,0 +1,85 @@
|
||||
---
|
||||
title: "Increasing Site and Client Capacity"
|
||||
description: "Understand the Gerbil subnet hierarchy and how to raise the number of sites and clients an exit node can serve"
|
||||
---
|
||||
|
||||
Every Gerbil exit node hands out its own persistent WireGuard subnet, and every site or client that connects to that exit node gets its own smaller subnet carved out of it. By default this hierarchy only leaves room for a limited number of sites and clients per exit node. If you're running a large deployment and are hitting that ceiling, you can raise it by changing three related settings in `config.yml`.
|
||||
|
||||
## How the subnet hierarchy works
|
||||
|
||||
There are three [`gerbil`](/self-host/advanced/config-file#gerbil-tunnel-controller) settings that work together, each nested inside the one before it:
|
||||
|
||||
1. **`subnet_group`** - The overall CIDR range that exit node subnets are carved from. This is the outermost container.
|
||||
2. **`block_size`** - The size of the subnet an exit node reserves for itself out of `subnet_group` when it registers. This is the persistent subnet for that exit node.
|
||||
3. **`site_block_size`** - The size of the subnet each site or client reserves for itself out of its exit node's `block_size` block when it connects.
|
||||
|
||||
In other words: `subnet_group` must be large enough to contain many `block_size` blocks (one per exit node), and each `block_size` block must be large enough to contain many `site_block_size` blocks (one per site or client).
|
||||
|
||||
With the defaults, this looks like:
|
||||
|
||||
```yaml title="config.yml"
|
||||
gerbil:
|
||||
subnet_group: "100.89.137.0/20" # 4,096 addresses total
|
||||
block_size: 24 # 256 addresses per exit node
|
||||
site_block_size: 32 # 1 address per site/client
|
||||
```
|
||||
|
||||
- A `/20` `subnet_group` holds 16 non-overlapping `/24` blocks, so it can support up to **16 exit nodes**.
|
||||
- A `/24` `block_size` holds 64 non-overlapping `/30` blocks, so each exit node can support up to **64 sites and clients**.
|
||||
|
||||
<Note>
|
||||
Smaller numbers after the slash mean *more* addresses (a `/22` is bigger than a `/24`). Increasing a block size means moving to a smaller number, and it always shrinks how many of the next-larger container it can fit into - which is why growing `block_size` usually means you also need to grow `subnet_group`.
|
||||
</Note>
|
||||
|
||||
## Increasing the number of sites and clients per exit node
|
||||
|
||||
If your exit nodes are running out of room for sites and clients, increase `block_size` so each exit node reserves a bigger subnet. Because a bigger `block_size` block takes up more of `subnet_group`, you should also grow `subnet_group` at the same time so it can still fit as many exit nodes as you need.
|
||||
|
||||
For example, to go from 64 sites/clients per exit node to 1,024, and keep room for 16 exit nodes:
|
||||
|
||||
```yaml title="config.yml"
|
||||
gerbil:
|
||||
subnet_group: "100.64.0.0/16" # widened to fit more /22 blocks
|
||||
block_size: 22 # 1,024 addresses per exit node (256 sites/clients * 4)
|
||||
site_block_size: 32 # unchanged - 1 address per site/client
|
||||
```
|
||||
|
||||
<Tip>
|
||||
Pick CGNAT range addresses (`100.64.0.0/10`) for `subnet_group` to avoid conflicting with typical private networks, the same as the default.
|
||||
</Tip>
|
||||
|
||||
You can also raise `site_block_size` (e.g. from `/30` to `/29` or `/28`) if individual sites need more addresses for heavy WireGuard usage, but doing so reduces how many sites/clients fit in each exit node's block, so weigh that trade-off against your capacity needs.
|
||||
|
||||
## Applying the change
|
||||
|
||||
Changing any of `subnet_group`, `block_size`, or `site_block_size` changes the addressing scheme for every exit node, site, and client, so existing exit node records need to be cleared out and re-created against the new ranges.
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step title="Update config.yml">
|
||||
Edit the `gerbil.subnet_group`, `gerbil.block_size`, and/or `gerbil.site_block_size` values on every node in your deployment (they must match everywhere).
|
||||
</Step>
|
||||
|
||||
<Step title="Clear exit nodes from the database">
|
||||
Use `pangctl` to remove existing exit node records so they get re-created using the new ranges:
|
||||
|
||||
```bash
|
||||
docker exec -it pangolin pangctl clear-exit-nodes
|
||||
```
|
||||
|
||||
See [Clear Exit Nodes](/self-host/advanced/container-cli-tool#clear-exit-nodes) for details.
|
||||
</Step>
|
||||
|
||||
<Step title="Restart the full stack">
|
||||
Restart every container in your Pangolin stack (Pangolin, Gerbil, Traefik, etc.) so the exit node re-registers with the new subnet settings.
|
||||
</Step>
|
||||
|
||||
<Step title="Restart sites and clients">
|
||||
Existing Newt sites and Olm clients may need to be restarted to pick up new addresses from their exit node and reconnect.
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
<Warning>
|
||||
Clearing exit nodes and changing the subnet hierarchy re-addresses every site and client connected through them. Plan for a maintenance window, since sites and clients will disconnect until they reconnect with their new address.
|
||||
</Warning>
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
title: "Enable Integration API"
|
||||
description: "Enable and configure the Integration API for external access"
|
||||
---
|
||||
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"
|
||||
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"
|
||||
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"
|
||||
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="/images/swagger.png" alt="Swagger UI Preview"/>
|
||||
</Frame>
|
||||
|
||||
<Note>
|
||||
The Integration API will be accessible at `https://api.example.com/v1` for external applications.
|
||||
</Note>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,100 @@
|
||||
---
|
||||
title: "Database Options"
|
||||
description: "Configure SQLite or PostgreSQL database for Pangolin"
|
||||
---
|
||||
## Overview
|
||||
|
||||
> Choose between SQLite (default) or PostgreSQL for your database
|
||||
|
||||
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="postgres">
|
||||
- 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"
|
||||
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"
|
||||
name: pangolin
|
||||
services:
|
||||
pangolin:
|
||||
image: fosrl/pangolin:postgresql-latest
|
||||
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
|
||||
|
||||
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,508 @@
|
||||
---
|
||||
title: "Private Configuration File"
|
||||
description: "Configure advanced Pangolin settings using the privateConfig.yml file for enterprise features"
|
||||
---
|
||||
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"
|
||||
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
|
||||
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
|
||||
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
|
||||
server:
|
||||
encryption_key_path: "./config/encryption.pem"
|
||||
```
|
||||
|
||||
<Warning>
|
||||
The `encryption_key_path` must point to a valid RSA key file. Generate one using:
|
||||
```bash
|
||||
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
|
||||
redis:
|
||||
host: "redis.example.com"
|
||||
```
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="port" type="number" required>
|
||||
Redis server port (1-65535).
|
||||
|
||||
```yaml
|
||||
redis:
|
||||
port: 6379
|
||||
```
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="password" type="string">
|
||||
Redis authentication password.
|
||||
|
||||
**Environment Variable**: `REDIS_PASSWORD` (or `REDIS_PASSWORD_FILE` to read the value from a file — see [Reading secrets from a file](/self-host/advanced/config-file#reading-secrets-from-a-file-_file-suffix))
|
||||
|
||||
```yaml
|
||||
redis:
|
||||
password: "your-secure-password"
|
||||
```
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="db" type="number" default="0">
|
||||
Redis database number (0-15 typically).
|
||||
|
||||
```yaml
|
||||
redis:
|
||||
db: 0
|
||||
```
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="replicas" type="array">
|
||||
Array of read replica configurations for high-availability deployments.
|
||||
|
||||
```yaml
|
||||
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>
|
||||
|
||||
### DNS Server Configuration
|
||||
|
||||
<ResponseField name="dns" type="object">
|
||||
Configuration for Pangolin's built-in authoritative DNS nameserver. This lets Pangolin answer DNS queries directly for domains delegated to it (via CNAME or NS delegation), issue ACME DNS-01 challenge responses, and resolve site tunnel subnet addresses.
|
||||
|
||||
<Expandable title="properties">
|
||||
<ResponseField name="enabled" type="boolean" default="false">
|
||||
Enables the authoritative DNS server. When `false` or omitted, no DNS listener is started.
|
||||
|
||||
```yaml
|
||||
dns:
|
||||
enabled: true
|
||||
```
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="listen_port" type="number" default="53">
|
||||
UDP port the authoritative DNS server listens on.
|
||||
|
||||
```yaml
|
||||
dns:
|
||||
listen_port: 53
|
||||
```
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="nameserver_name" type="string" required>
|
||||
The FQDN Pangolin advertises as itself when acting as a nameserver. Used as the `mname` in SOA responses and included in the NS record set returned for zones it is authoritative for. This is the hostname you point your domain's nameservers at when using NS-based domain delegation (e.g. `ns1.pangolin-ns.net`).
|
||||
|
||||
```yaml
|
||||
dns:
|
||||
nameserver_name: "ns1.pangolin-ns.net"
|
||||
```
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="cname_extension" type="string" required>
|
||||
The domain suffix used for single-domain CNAME delegation. When an org adds a domain using the CNAME delegation type, Pangolin generates `{domainId}.{cname_extension}` and `_acme-challenge.{domainId}.{cname_extension}` targets to point your records at.
|
||||
|
||||
```yaml
|
||||
dns:
|
||||
cname_extension: "cname.pangolin.net"
|
||||
```
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="site_extension" type="string">
|
||||
Suffix used to resolve a site's WireGuard tunnel subnet address by DNS. A query for `{newtId}.{site_extension}` resolves to the tunnel subnet IP of the site running the Newt agent with that ID. This is used for site-to-cloud networking.
|
||||
|
||||
```yaml
|
||||
dns:
|
||||
site_extension: "site.pangolin.net"
|
||||
```
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="cname_alternate_extensions" type="array" default="[]">
|
||||
Additional CNAME suffixes (besides `cname_extension`) that are treated the same way. Useful when Pangolin should respond to more than one CNAME delegation domain, such as for white-label/multi-brand deployments.
|
||||
|
||||
```yaml
|
||||
dns:
|
||||
cname_alternate_extensions:
|
||||
- "cname.example.com"
|
||||
```
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="alternate_nameservers" type="array" default="[]">
|
||||
Additional nameserver hostnames appended after `nameserver_name` in the NS record set Pangolin returns for a zone, and in the NS records shown when using NS-based domain delegation (e.g. `ns2.pangolin-ns.net`, `ns3.pangolin-ns.net`).
|
||||
|
||||
```yaml
|
||||
dns:
|
||||
alternate_nameservers:
|
||||
- "ns2.pangolin-ns.net"
|
||||
- "ns3.pangolin-ns.net"
|
||||
```
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="rate_limit" type="object">
|
||||
Per-source-IP rate limiting for DNS queries. Queries exceeding these limits are refused.
|
||||
|
||||
```yaml
|
||||
dns:
|
||||
rate_limit:
|
||||
enabled: true
|
||||
window_ms: 60000
|
||||
max_requests: 1200
|
||||
max_requests_per_query_type: 600
|
||||
```
|
||||
|
||||
<Expandable title="properties">
|
||||
<ResponseField name="enabled" type="boolean" default="true">
|
||||
Enables DNS query rate limiting.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="window_ms" type="number" default="60000">
|
||||
The time window, in milliseconds, over which query counts are measured.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="max_requests" type="number" default="1200">
|
||||
Maximum total DNS queries allowed per source IP within `window_ms`.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="max_requests_per_query_type" type="number" default="600">
|
||||
Maximum queries allowed per source IP, per DNS record type (A, TXT, NS, etc.), within `window_ms`.
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="static_records" type="array" default="[]">
|
||||
Hardcoded DNS answers served by the authoritative DNS server, checked before database-backed lookups. Useful for fixed entries such as domain verification TXT records.
|
||||
|
||||
```yaml
|
||||
dns:
|
||||
static_records:
|
||||
- domain: "example.com"
|
||||
type: "TXT"
|
||||
value: "v=spf1 include:_spf.example.com ~all"
|
||||
ttl: 300
|
||||
```
|
||||
|
||||
<Expandable title="properties">
|
||||
<ResponseField name="domain" type="string" required>
|
||||
The domain name to match (case-insensitive).
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="type" type="string" required>
|
||||
The DNS record type. One of `TXT`, `CNAME`, `A`, or `NS`.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="value" type="string" required>
|
||||
The value returned for this record.
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="ttl" type="number" default="300">
|
||||
Time-to-live, in seconds, for the returned record.
|
||||
</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
|
||||
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
|
||||
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
|
||||
flags:
|
||||
enable_redis: true
|
||||
```
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="use_pangolin_dns" type="boolean" default="false">
|
||||
Allow creating domains using CNAME and NS.
|
||||
|
||||
```yaml
|
||||
flags:
|
||||
use_pangolin_dns: true
|
||||
```
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
### ACME Certificate Configuration
|
||||
|
||||
<ResponseField name="acme" type="object">
|
||||
Configuration for Pangolin's self-hosted ACME client, which issues and renews TLS certificates directly using DNS-01 challenges served by Pangolin's own [authoritative DNS server](#dns-server-configuration), as an alternative to relying on Traefik's built-in ACME resolver.
|
||||
|
||||
<Expandable title="properties">
|
||||
<ResponseField name="cert_mode" type="string" default="traefik">
|
||||
Controls who is responsible for obtaining and renewing TLS certificates.
|
||||
|
||||
Possible values:
|
||||
- `traefik`: (default) Traefik's built-in ACME resolver handles certificate issuance and renewal itself.
|
||||
- `pangolin`: Pangolin issues and manages certificates itself via its self-hosted ACME client, using DNS-01 challenges. Requires `dns.enabled` and `acme.enable_acme_client` to both be `true`. ENSURE ONLY ONE NODE IN A CLUSTER HAS THE `acme.enable_acme_client` FLAG ENABLED, OTHERWISE MULTIPLE NODES WILL TRY TO ISSUE CERTIFICATES SIMULTANEOUSLY.
|
||||
|
||||
```yaml
|
||||
acme:
|
||||
cert_mode: "pangolin"
|
||||
```
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="enable_acme_client" type="boolean" default="false">
|
||||
Enable the self-hosted ACME client and its certificate issuance/renewal jobs. Must be `true`, along with `cert_mode: "pangolin"`, for the certificate manager to start.
|
||||
|
||||
```yaml
|
||||
acme:
|
||||
enable_acme_client: true
|
||||
```
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="contact_email" type="string" required>
|
||||
Email address registered with the ACME account, used by the CA for expiry and policy notices.
|
||||
|
||||
```yaml
|
||||
acme:
|
||||
contact_email: "admin@example.com"
|
||||
```
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="acme_directory_url" type="string" default="https://acme-v02.api.letsencrypt.org/directory">
|
||||
The ACME server directory URL Pangolin's client talks to.
|
||||
|
||||
```yaml
|
||||
acme:
|
||||
acme_directory_url: "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
```
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="acme_account_key_path" type="string" default="./config/account.key">
|
||||
Filesystem path where the ACME account's private key is stored. Generated automatically on first run if it doesn't exist.
|
||||
|
||||
```yaml
|
||||
acme:
|
||||
acme_account_key_path: "./config/account.key"
|
||||
```
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="challenge_ttl_ms" type="number" default="300000">
|
||||
How long, in milliseconds, a DNS-01 challenge TXT record is considered valid and served by the authoritative DNS server before expiring.
|
||||
|
||||
```yaml
|
||||
acme:
|
||||
challenge_ttl_ms: 300000
|
||||
```
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="renewal_check_interval_ms" type="number" default="3600000">
|
||||
How often, in milliseconds, Pangolin checks for certificates approaching expiry and renews them.
|
||||
|
||||
```yaml
|
||||
acme:
|
||||
renewal_check_interval_ms: 3600000
|
||||
```
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="new_cert_check_interval_ms" type="number" default="5000">
|
||||
How often, in milliseconds, Pangolin checks for newly-needed certificates and issues them. Set much lower than `renewal_check_interval_ms` since new certificates are user-facing and blocking.
|
||||
|
||||
```yaml
|
||||
acme:
|
||||
new_cert_check_interval_ms: 5000
|
||||
```
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="acme_requests_per_second" type="number" default="15">
|
||||
Shared rate limit on outbound calls Pangolin's client makes directly to the ACME server (create order, get authorizations, verify challenge, finalize, get certificate). Kept under Let's Encrypt's ~20 req/s limit.
|
||||
|
||||
```yaml
|
||||
acme:
|
||||
acme_requests_per_second: 15
|
||||
```
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="dns_check_interval_ms" type="number" default="60000">
|
||||
How often, in milliseconds, Pangolin checks pending (not-yet-verified) domains' DNS records against live DNS to flip them to verified once the records are in place.
|
||||
|
||||
```yaml
|
||||
acme:
|
||||
dns_check_interval_ms: 60000
|
||||
```
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="domain_reverification_interval_ms" type="number" default="3600000">
|
||||
How often, in milliseconds, Pangolin runs a periodic pass re-checking already-verified domains, to catch removing or changing their DNS records after the fact.
|
||||
|
||||
```yaml
|
||||
acme:
|
||||
domain_reverification_interval_ms: 3600000
|
||||
```
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="domain_reverification_window_ms" type="number" default="259200000">
|
||||
Minimum age, in milliseconds, a verified domain's last check must have before it becomes eligible for reverification. A given domain is reverified at most roughly this often, not on every reverification pass.
|
||||
|
||||
```yaml
|
||||
acme:
|
||||
domain_reverification_window_ms: 259200000
|
||||
```
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="domain_reverification_batch_size" type="number" default="20">
|
||||
Maximum number of verified domains reverified per reverification pass, to bound database and DNS load per tick.
|
||||
|
||||
```yaml
|
||||
acme:
|
||||
domain_reverification_batch_size: 20
|
||||
```
|
||||
</ResponseField>
|
||||
|
||||
<ResponseField name="dns_resolvers" type="array" default='["8.8.8.8", "1.1.1.1", "9.9.9.9", "208.67.222.222"]'>
|
||||
Upstream public DNS resolvers used to perform live lookups against real DNS (not Pangolin's own authoritative server) when validating or reverifying domain records. Resolvers are rotated across on each attempt so a single resolver's cache or propagation lag doesn't wrongly fail a check.
|
||||
|
||||
```yaml
|
||||
acme:
|
||||
dns_resolvers:
|
||||
- "8.8.8.8"
|
||||
- "1.1.1.1"
|
||||
```
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
### Branding Configuration
|
||||
|
||||
Please refer to the [branding configuration documentation](/manage/branding).
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Some configuration values can be set using environment variables for enhanced security:
|
||||
|
||||
| Name | Variable | Config | Supports `_FILE` |
|
||||
|------|----------|--------|:---:|
|
||||
| Redis Password | `REDIS_PASSWORD` | `redis.password` | YES |
|
||||
|
||||
Any variable marked "Supports `_FILE`" can also be set as `<VARIABLE>_FILE`, pointing to a file on disk whose (trimmed) contents are used as the value instead — see [Reading secrets from a file](/self-host/advanced/config-file#reading-secrets-from-a-file-_file-suffix) in the main configuration file docs for details and a Docker Compose example.
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
title: "Container Timezone"
|
||||
description: "Configure the timezone for Pangolin, Gerbil, and Traefik containers to match your local time."
|
||||
---
|
||||
By default, Docker containers report logs and timestamps in **UTC**. If you want the containers and their log output to use your local timezone, you need to set the timezone in both the container environment and mount the host timezone files.
|
||||
|
||||
## Updating your `docker-compose.yml`
|
||||
|
||||
Add the following to your `pangolin`, `gerbil`, and `traefik` services in `docker-compose.yml`:
|
||||
|
||||
```yaml title="docker-compose.yml"
|
||||
services:
|
||||
pangolin:
|
||||
environment:
|
||||
- TZ=America/New_York # Set your local timezone
|
||||
volumes:
|
||||
- /etc/localtime:/etc/localtime:ro # Sync host timezone
|
||||
- /etc/timezone:/etc/timezone:ro # Optional: some apps read this file
|
||||
|
||||
gerbil:
|
||||
volumes:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
|
||||
traefik:
|
||||
environment:
|
||||
- TZ=America/New_York
|
||||
volumes:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
```
|
||||
### Notes
|
||||
|
||||
- **Environment variable `TZ`** ensures most applications inside the container use the correct local timezone.
|
||||
- **`/etc/localtime` volume** ensures that system utilities (e.g., `date`) inside the container show the correct time.
|
||||
- **`/etc/timezone` volume** is optional, but some scripts and apps on Debian-based images read it to determine the timezone.
|
||||
- Logs generated by the containers (including Traefik and Gerbil) will now reflect your local time instead of UTC.
|
||||
|
||||
<Warning>
|
||||
Make sure that the host system has the correct timezone configured, as the containers will reference these host files.
|
||||
</Warning>
|
||||
@@ -0,0 +1,119 @@
|
||||
---
|
||||
title: "Traefik Access Log Rotation"
|
||||
description: "How to manage and rotate Traefik access logs when CrowdSec is installed"
|
||||
---
|
||||
|
||||
When CrowdSec is installed, Traefik access logging is enabled automatically so CrowdSec can analyze traffic. This means `config/traefik/logs/access.log` will grow indefinitely without log rotation in place.
|
||||
|
||||
<Note>
|
||||
The default Pangolin install (without CrowdSec) does not enable access
|
||||
logging, so this only applies if you have CrowdSec installed.
|
||||
</Note>
|
||||
|
||||
## How it works
|
||||
|
||||
The CrowdSec installer enables Traefik's `accessLog` block and mounts `./config/traefik/logs/` into the container at `/var/log/traefik/`. CrowdSec reads that log via its `acquis.d/traefik.yaml` acquisition config.
|
||||
|
||||
Without rotation, that file grows forever. The fix is `logrotate` with `copytruncate` — it copies the log file and truncates the original in place, so Traefik never needs to be restarted or sent a signal.
|
||||
|
||||
## Automatic setup (installer v1.x+)
|
||||
|
||||
If you installed CrowdSec using a recent version of the Pangolin installer, logrotate is configured automatically at `/etc/logrotate.d/pangolin-traefik`. You can verify it's there:
|
||||
|
||||
```bash
|
||||
cat /etc/logrotate.d/pangolin-traefik
|
||||
```
|
||||
|
||||
You should see something like:
|
||||
|
||||
```
|
||||
/opt/pangolin/config/traefik/logs/access.log {
|
||||
daily
|
||||
rotate 7
|
||||
compress
|
||||
delaycompress
|
||||
missingok
|
||||
notifempty
|
||||
copytruncate
|
||||
}
|
||||
```
|
||||
|
||||
## Manual setup
|
||||
|
||||
If you installed CrowdSec before automatic log rotation was added, set it up manually:
|
||||
|
||||
<Steps>
|
||||
<Step title="Create the logrotate config">
|
||||
Replace `/opt/pangolin` with your actual Pangolin install directory if it differs.
|
||||
|
||||
```bash
|
||||
sudo tee /etc/logrotate.d/pangolin-traefik > /dev/null <<'EOF'
|
||||
/opt/pangolin/config/traefik/logs/access.log {
|
||||
daily
|
||||
rotate 7
|
||||
compress
|
||||
delaycompress
|
||||
missingok
|
||||
notifempty
|
||||
copytruncate
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
</Step>
|
||||
<Step title="Test the configuration">
|
||||
Do a dry run to confirm logrotate picks it up without errors:
|
||||
|
||||
```bash
|
||||
sudo logrotate --debug /etc/logrotate.d/pangolin-traefik
|
||||
```
|
||||
|
||||
No errors means you're good. You can also force a rotation immediately to verify end-to-end:
|
||||
|
||||
```bash
|
||||
sudo logrotate --force /etc/logrotate.d/pangolin-traefik
|
||||
```
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Customizing retention
|
||||
|
||||
The defaults (daily rotation, 7 compressed copies) work for most setups. To adjust:
|
||||
|
||||
| Option | What it does |
|
||||
| --------------- | -------------------------------------------------------------------------------------- |
|
||||
| `daily` | Rotate once per day. Use `weekly` or `monthly` if preferred. |
|
||||
| `rotate 7` | Keep 7 rotated files before deleting the oldest. |
|
||||
| `compress` | Gzip rotated files to save disk space. |
|
||||
| `delaycompress` | Skip compressing the most recent rotated file (useful if something still has it open). |
|
||||
|
||||
For example, to keep 30 days of compressed weekly logs:
|
||||
|
||||
```
|
||||
/opt/pangolin/config/traefik/logs/access.log {
|
||||
weekly
|
||||
rotate 30
|
||||
compress
|
||||
delaycompress
|
||||
missingok
|
||||
notifempty
|
||||
copytruncate
|
||||
}
|
||||
```
|
||||
|
||||
## Verifying rotation is working
|
||||
|
||||
Check that rotated files are appearing in the logs directory:
|
||||
|
||||
```bash
|
||||
ls -lh /opt/pangolin/config/traefik/logs/
|
||||
```
|
||||
|
||||
After the first rotation you should see files like `access.log.1` and `access.log.2.gz` alongside the active `access.log`.
|
||||
|
||||
To see when logrotate last ran and whether it succeeded:
|
||||
|
||||
```bash
|
||||
cat /var/lib/logrotate/status | grep pangolin
|
||||
```
|
||||
@@ -0,0 +1,274 @@
|
||||
---
|
||||
title: "Wildcard Domains"
|
||||
description: "Configure wildcard TLS certificates with Traefik DNS-01 challenges"
|
||||
---
|
||||
|
||||
Wildcard certificates let one certificate cover every first-level subdomain of a domain, such as `*.example.com`. They are useful when you create many resources under the same base domain because Traefik does not need to request a new certificate for every resource hostname.
|
||||
|
||||
Traefik is the reverse proxy in the self-hosted Pangolin stack. It receives HTTPS traffic, requests certificates from Let's Encrypt, and routes requests to Pangolin resources. A Traefik certificate resolver is the named block of Traefik configuration that tells Traefik how to request certificates.
|
||||
|
||||
<Warning>
|
||||
Wildcard certificates require a DNS-01 challenge. You must control the domain's DNS records and have API credentials for a DNS provider supported by Traefik.
|
||||
</Warning>
|
||||
|
||||
<Note>
|
||||
Let's Encrypt only issues wildcard certificates through DNS-01 challenges. See the [Traefik ACME documentation](https://doc.traefik.io/traefik/https/acme/) and [Lego DNS provider list](https://go-acme.github.io/lego/dns/) for provider-specific options.
|
||||
</Note>
|
||||
|
||||
## How Wildcards Work
|
||||
|
||||
- `*.example.com` covers `app.example.com`, `api.example.com`, and `blog.example.com`.
|
||||
- `*.example.com` does not cover `app.internal.example.com`; that needs `*.internal.example.com`.
|
||||
- A wildcard certificate can reduce Let's Encrypt rate limit pressure because many resource hostnames can reuse the same certificate.
|
||||
|
||||
Pangolin can prefer wildcard certificates when it generates Traefik router configuration. For example, if you have resources at `blog.example.com` and `api.example.com`, Pangolin can ask Traefik to request `*.example.com` instead of separate certificates for each hostname.
|
||||
|
||||
## Benefits
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="Single Certificate" icon="certificate">
|
||||
Secure many subdomains under the same base domain with one certificate.
|
||||
</Card>
|
||||
|
||||
<Card title="Instant Subdomains" icon="bolt">
|
||||
New resource subdomains can use the existing wildcard certificate instead of waiting for a new certificate request.
|
||||
</Card>
|
||||
|
||||
<Card title="Rate Limit Friendly" icon="shield">
|
||||
Fewer certificate requests can help reduce the chance of hitting Let's Encrypt rate limits.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Choose a Resolver Strategy
|
||||
|
||||
Most installs start with one Traefik certificate resolver named `letsencrypt` that uses HTTP-01:
|
||||
|
||||
```yaml title="traefik_config.yml"
|
||||
certificatesResolvers:
|
||||
letsencrypt:
|
||||
acme:
|
||||
httpChallenge:
|
||||
entryPoint: web
|
||||
email: "admin@example.com"
|
||||
storage: "/letsencrypt/acme.json"
|
||||
caServer: "https://acme-v02.api.letsencrypt.org/directory"
|
||||
```
|
||||
|
||||
HTTP-01 proves domain ownership by serving a challenge over port `80`. DNS-01 proves domain ownership by creating a temporary DNS record through your DNS provider. Wildcard certificates require DNS-01.
|
||||
|
||||
For wildcard certificates, you have two good options. Most users should replace the existing `letsencrypt` resolver with DNS-01; add a second resolver only if you know you need both HTTP-01 and DNS-01.
|
||||
|
||||
| Strategy | When to use it |
|
||||
| --- | --- |
|
||||
| Replace `letsencrypt` with DNS-01 | Simplest option. Use this if all certificates can be issued through your DNS provider. |
|
||||
| Add a second resolver | Use this if you want to keep HTTP-01 for some routers and use DNS-01 only for wildcard domains. |
|
||||
|
||||
Traefik does not automatically apply a resolver just because it exists. Each router must reference the resolver with `tls.certResolver`, and Pangolin's `cert_resolver` setting must match the Traefik resolver name.
|
||||
|
||||
<Note>
|
||||
In the Pangolin dashboard, `default` uses Pangolin's configured `traefik.cert_resolver` value. In a standard install, that default value is `letsencrypt`.
|
||||
</Note>
|
||||
|
||||
## Configure DNS-01 Wildcards
|
||||
|
||||
<Steps>
|
||||
<Step title="Stop the stack">
|
||||
Stop Pangolin before editing Traefik and Pangolin configuration.
|
||||
|
||||
```bash
|
||||
sudo docker compose down
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Update the Traefik resolver">
|
||||
Replace the default HTTP-01 resolver with a DNS-01 resolver. This example uses Cloudflare.
|
||||
|
||||
```yaml title="config/traefik/traefik_config.yml" {4-6}
|
||||
certificatesResolvers:
|
||||
letsencrypt:
|
||||
acme:
|
||||
dnsChallenge:
|
||||
provider: "cloudflare"
|
||||
# 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"
|
||||
```
|
||||
|
||||
<Note>
|
||||
The resolver name is the key under `certificatesResolvers`. In this example it is `letsencrypt`, so Pangolin's `cert_resolver` and any Traefik `tls.certResolver` values must also use `letsencrypt`.
|
||||
</Note>
|
||||
</Step>
|
||||
|
||||
<Step title="Add DNS provider credentials">
|
||||
Add the environment variables required by your DNS provider to the `traefik` service. Cloudflare requires an API token with `Zone:Read` and `DNS:Edit` permissions for every zone Traefik needs to solve challenges for.
|
||||
|
||||
```yaml title="docker-compose.yml" {11-12}
|
||||
traefik:
|
||||
image: docker.io/traefik:v3.7
|
||||
container_name: traefik
|
||||
restart: unless-stopped
|
||||
network_mode: service:gerbil
|
||||
depends_on:
|
||||
pangolin:
|
||||
condition: service_healthy
|
||||
command:
|
||||
- --configFile=/etc/traefik/traefik_config.yml
|
||||
environment:
|
||||
CLOUDFLARE_DNS_API_TOKEN: "your-cloudflare-api-token" # REPLACE
|
||||
volumes:
|
||||
- ./config/traefik:/etc/traefik:ro
|
||||
- ./config/letsencrypt:/letsencrypt
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Tell Pangolin to prefer wildcard certificates">
|
||||
Set `prefer_wildcard_cert: true` for the domain in `config/config.yml`.
|
||||
|
||||
```yaml title="config/config.yml" {4}
|
||||
domains:
|
||||
domain1:
|
||||
base_domain: "example.com"
|
||||
prefer_wildcard_cert: true
|
||||
cert_resolver: "letsencrypt"
|
||||
```
|
||||
|
||||
If you manage domains through the Pangolin dashboard instead, restart Pangolin and enable wildcard preference on the domain there. The dashboard also lets you set the domain's certificate resolver; it must match the resolver name in Traefik.
|
||||
</Step>
|
||||
|
||||
<Step title="Restart the stack">
|
||||
Start the stack and watch Traefik logs. You should see Traefik create DNS challenge records through your provider.
|
||||
|
||||
```bash
|
||||
sudo docker compose up -d
|
||||
sudo docker compose logs -f traefik
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Multiple Certificate Resolvers
|
||||
|
||||
You can define more than one Traefik certificate resolver. This is useful when you want to keep HTTP-01 available as the default resolver, but use a DNS-01 resolver for wildcard domains.
|
||||
|
||||
```yaml title="config/traefik/traefik_config.yml"
|
||||
certificatesResolvers:
|
||||
letsencrypt:
|
||||
acme:
|
||||
httpChallenge:
|
||||
entryPoint: web
|
||||
email: "admin@example.com"
|
||||
storage: "/letsencrypt/acme-http.json"
|
||||
caServer: "https://acme-v02.api.letsencrypt.org/directory"
|
||||
|
||||
letsencrypt-dns:
|
||||
acme:
|
||||
dnsChallenge:
|
||||
provider: "cloudflare"
|
||||
email: "admin@example.com"
|
||||
storage: "/letsencrypt/acme-dns.json"
|
||||
caServer: "https://acme-v02.api.letsencrypt.org/directory"
|
||||
```
|
||||
|
||||
Then point the wildcard domain at the DNS resolver. You can do this in `config/config.yml` for file-managed domains:
|
||||
|
||||
```yaml title="config/config.yml" {5-6}
|
||||
domains:
|
||||
domain1:
|
||||
base_domain: "example.com"
|
||||
prefer_wildcard_cert: true
|
||||
cert_resolver: "letsencrypt-dns"
|
||||
```
|
||||
|
||||
If you want UI-created domains to use the DNS resolver by default, set the Traefik defaults too:
|
||||
|
||||
```yaml title="config/config.yml"
|
||||
traefik:
|
||||
cert_resolver: "letsencrypt-dns"
|
||||
prefer_wildcard_cert: true
|
||||
```
|
||||
|
||||
For dashboard-managed domains, open the domain settings in Pangolin and set the certificate resolver to the custom Traefik resolver name, such as `letsencrypt-dns`. Enable wildcard preference on the same domain if you want Pangolin to request wildcard certificates for resources under that domain.
|
||||
|
||||
<Note>
|
||||
If you split ACME storage across multiple files, configure Pangolin's private `acme.acme_json_path` setting as the directory that contains them, for example `config/letsencrypt`. Pangolin will scan the directory for ACME JSON files, including nested files. See [ACME configuration](/self-host/advanced/private-config-file#acme-configuration).
|
||||
</Note>
|
||||
|
||||
## Dashboard Certificate
|
||||
|
||||
The `prefer_wildcard_cert` setting affects resource routers generated by Pangolin. If you also want Traefik to request a wildcard certificate for the Pangolin dashboard router, add the wildcard domain to the dashboard router's `tls.domains` list in `config/traefik/dynamic_config.yml`.
|
||||
|
||||
```yaml title="config/traefik/dynamic_config.yml" {8-12}
|
||||
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"
|
||||
```
|
||||
|
||||
If you use a second resolver, set `certResolver` to that resolver name, such as `letsencrypt-dns`.
|
||||
|
||||
## Verify It Works
|
||||
|
||||
<Tip>
|
||||
If Traefik already issued certificates with the old resolver, clear the old certificates before testing so Traefik requests them again. Remove the relevant ACME storage file, or use `pangctl clear-certs` if Pangolin has already synced stale certificates.
|
||||
</Tip>
|
||||
|
||||
<Steps>
|
||||
<Step title="Create or open a resource">
|
||||
Create a resource on an unused subdomain such as `test.example.com`, or open an existing resource under the same base domain.
|
||||
</Step>
|
||||
|
||||
<Step title="Check Traefik logs">
|
||||
Traefik should use the DNS-01 resolver and should not need a separate certificate for every resource hostname after the wildcard certificate exists.
|
||||
|
||||
```bash
|
||||
sudo docker compose logs traefik
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Inspect ACME storage">
|
||||
Check the ACME storage file in `config/letsencrypt`. The certificate domain should include a wildcard SAN such as `*.example.com`.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
```json {6}
|
||||
{
|
||||
"Certificates": [
|
||||
{
|
||||
"domain": {
|
||||
"main": "example.com",
|
||||
"sans": ["*.example.com"]
|
||||
},
|
||||
"certificate": "...",
|
||||
"key": "...",
|
||||
"Store": "default"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Wildcard certificate is not created">
|
||||
Confirm the DNS provider is correct, the provider environment variables are present on the `traefik` service, and the API token has permission to edit DNS records for the zone.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Traefik still uses HTTP-01">
|
||||
Check the resolver name. The router's `tls.certResolver` and Pangolin's `cert_resolver` must match the DNS-01 resolver name exactly.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Old certificates are still served">
|
||||
Clear old certificates so Traefik can request them again. You can remove the relevant ACME storage file, or use `pangctl clear-certs` if Pangolin has already synced stale certificates.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="DNS challenge times out">
|
||||
Review Traefik debug logs, confirm DNS propagation is working, and check whether your DNS provider requires additional propagation delay or custom resolvers. If your DNS provider has a firewall, make sure it allows DNS traffic, typically UDP on port `53`.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
title: "Without Tunneling"
|
||||
description: "Use Pangolin as a local reverse proxy without Gerbil tunneling"
|
||||
---
|
||||
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,74 @@
|
||||
---
|
||||
title: "Choosing a VPS"
|
||||
description: "Compare hosting options and find the best VPS for your Pangolin deployment"
|
||||
---
|
||||
|
||||
Pangolin generally requires minimal resources to run effectively. A basic VPS with **1 vCPU, 2GB RAM, and 8GB SSD** is sufficient for most deployments.
|
||||
|
||||
<Note>
|
||||
If you choose a VPS with only 1GB RAM, you may need to create swap space to avoid memory pressure during installation, updates, or periods of higher traffic.
|
||||
</Note>
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Recommended Specs">
|
||||
- **CPU**: 2 vCPU
|
||||
- **RAM**: 2GB
|
||||
- **Storage**: 20GB SSD
|
||||
</Card>
|
||||
<Card title="More Users or Sites">
|
||||
- **CPU**: 4 vCPU
|
||||
- **RAM**: 4GB
|
||||
- **Storage**: 40GB SSD
|
||||
</Card>
|
||||
<Card title="Large Deployment">
|
||||
- **CPU**: 8 vCPU
|
||||
- **RAM**: 8GB
|
||||
- **Storage**: 80GB SSD
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Provider Options
|
||||
|
||||
Most general-purpose VPS providers will work well for Pangolin. Look for a provider with reliable uptime, a data center near your users, clear bandwidth limits, snapshots or backups, and the ability to open inbound TCP ports 80 and 443 plus the WireGuard UDP ports configured during installation.
|
||||
|
||||
<Info>
|
||||
If you prefer a guided deployment, Pangolin is also available on the <a href="https://marketplace.digitalocean.com/apps/pangolin-ce-1?refcode=edf0480eeb81">DigitalOcean Marketplace</a>. The marketplace image can create a droplet with Pangolin pre-installed and firewall rules configured.
|
||||
</Info>
|
||||
|
||||
<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 examples**: DigitalOcean, Vultr, Linode, RackNerd
|
||||
- **Europe examples**: Hetzner, OVHcloud, UpCloud
|
||||
- **Asia Pacific examples**: Vultr, Linode, DigitalOcean
|
||||
- **Global examples**: 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,549 @@
|
||||
---
|
||||
title: "Deploy a Cluster"
|
||||
description: "Step-by-step walkthrough for deploying a two-node highly available Pangolin cluster"
|
||||
---
|
||||
|
||||
<Note>
|
||||
Clustering is only available in [Enterprise Edition](/self-host/enterprise-edition).
|
||||
</Note>
|
||||
|
||||
This guide walks through deploying a minimal two-node Pangolin cluster: two Pangolin nodes behind a load balancer, sharing a PostgreSQL database and a Valkey (Redis) server. Read [Understanding Clustering](/self-host/clustering/understanding-clustering) for the architecture and [Requirements](/self-host/clustering/requirements) for the hosts, ports, and DNS records you need before starting.
|
||||
|
||||
<Card icon="github" arrow="true" cta="View reference configuration" href="https://github.com/fosrl/pangolin/tree/main/config/ha-reference">
|
||||
The complete, working set of files used in this guide lives in the Pangolin repository at [`config/ha-reference`](https://github.com/fosrl/pangolin/tree/main/config/ha-reference). Clone it as a starting point instead of assembling files by hand.
|
||||
</Card>
|
||||
|
||||
<Warning>
|
||||
`server.secret` in `config.yml` must be **identical on every node in the cluster**. It's used to encrypt sensitive data, including the certificates stored in PostgreSQL - if nodes have different secrets, they won't be able to read each other's data.
|
||||
</Warning>
|
||||
|
||||
Throughout this guide, replace the following placeholders with your own values:
|
||||
|
||||
| Placeholder | Description |
|
||||
| --- | --- |
|
||||
| `NODE1_EXTERNAL_IP` / `NODE2_EXTERNAL_IP` | Public static IP of each Pangolin node |
|
||||
| `NODE1_INTERNAL_IP` / `NODE2_INTERNAL_IP` | Internal IP each node uses to address the other |
|
||||
| `POSTGRES_INTERNAL_HOST` | Address of your shared PostgreSQL server |
|
||||
| `POSTGRES_USERNAME` / `POSTGRES_PASSWORD` | Credentials for that PostgreSQL server |
|
||||
| `REDIS_INTERNAL_HOST` | Address of your shared Redis-compatible server |
|
||||
| `CONTACT_EMAIL` | Email address used for Let's Encrypt ACME registration |
|
||||
| `SECRET` | Shared server secret - identical on every node |
|
||||
| `LOAD_BALANCER_IP` | IP address of the load balancer in front of the cluster |
|
||||
| `pangolin.example.com` | Your dashboard domain - DNS points at the load balancer, not at either node |
|
||||
|
||||
You need a domain for the Pangolin UI and API (`pangolin.example.com` in this guide), pointed at your load balancer. **The load balancer is responsible for TLS on this domain** - terminate HTTPS there and forward plain HTTP to the nodes' dashboard port. The nodes' built-in ACME client only issues certificates for resource domains under the delegated nameserver zone, not for the dashboard domain itself. See [Requirements](/self-host/clustering/requirements#dashboard-domain).
|
||||
|
||||
<Steps>
|
||||
|
||||
<Step title="Provision the shared database">
|
||||
Stand up a PostgreSQL server and a Redis-compatible server that both nodes can reach. They don't need to run together, or even on a dedicated third host - use whatever you already run, including managed cloud offerings. The only hard requirement is that the Redis-compatible server supports **pub/sub**.
|
||||
|
||||
For a simple self-hosted starting point:
|
||||
|
||||
```yaml title="docker-compose.yml"
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:17
|
||||
container_name: postgres
|
||||
environment:
|
||||
POSTGRES_DB: postgres # Default database name
|
||||
POSTGRES_USER: postgres # Default user
|
||||
POSTGRES_PASSWORD: password # Default password (change for production!)
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
ports:
|
||||
- "5432:5432"
|
||||
restart: always
|
||||
|
||||
redis:
|
||||
image: redis:latest
|
||||
container_name: redis
|
||||
ports:
|
||||
- "6379:6379"
|
||||
restart: always
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Change the default PostgreSQL password before running this in production. See [Database Options](/self-host/advanced/database-options) for general PostgreSQL configuration.
|
||||
</Warning>
|
||||
</Step>
|
||||
|
||||
<Step title="Lay out each node's config directory">
|
||||
On each of Node 1 and Node 2, create the following directory structure:
|
||||
|
||||
```
|
||||
node/
|
||||
├── docker-compose.yml
|
||||
└── config/
|
||||
├── config.yml
|
||||
├── privateConfig.yml
|
||||
├── certificates/ # empty, Pangolin/Traefik populate this
|
||||
├── dynamic/
|
||||
│ └── dynamic_config.yml
|
||||
└── traefik/
|
||||
└── traefik_config.yml
|
||||
```
|
||||
|
||||
`config/certificates` and `config/dynamic` are shared volumes between the `pangolin` and `traefik` containers - Pangolin writes router configuration and certificates there for Traefik to read, since Traefik can only load certificates from files, not from the Pangolin API. This is `traefik.file_mode` in `config.yml`, covered below.
|
||||
</Step>
|
||||
|
||||
<Step title="Write docker-compose.yml">
|
||||
Both nodes run the same three containers: `pangolin`, `gerbil`, and `traefik`. Gerbil owns the host networking (WireGuard, relay, DNS, resource ports), and Traefik joins its network namespace so its ports appear alongside Gerbil's.
|
||||
|
||||
Each node's `--reachableAt` flag must point at **that node's own** internal address, and `--trusted-upstreams` lists the external IPs of every node in the cluster so Gerbil accepts proxied connections from them.
|
||||
|
||||
```yaml tab="Node 1"
|
||||
name: pangolin
|
||||
services:
|
||||
pangolin:
|
||||
image: docker.io/fosrl/pangolin:ee-latest
|
||||
container_name: pangolin
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./config:/app/config
|
||||
- ./config/certificates:/var/certificates
|
||||
- ./config/dynamic:/var/dynamic
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3001/api/v1/"]
|
||||
interval: "10s"
|
||||
timeout: "10s"
|
||||
retries: 15
|
||||
|
||||
gerbil:
|
||||
image: docker.io/fosrl/gerbil:latest
|
||||
container_name: gerbil
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
pangolin:
|
||||
condition: service_healthy
|
||||
command:
|
||||
- --reachableAt=http://<NODE1_INTERNAL_IP>:3004
|
||||
- --generateAndSaveKeyTo=/var/config/key
|
||||
- --remoteConfig=http://pangolin:3001/api/v1/
|
||||
- --trusted-upstreams=<NODE1_EXTERNAL_IP>,<NODE2_EXTERNAL_IP> # All trusted nodes in the cluster
|
||||
volumes:
|
||||
- ./config/:/var/config
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
- SYS_MODULE
|
||||
ports:
|
||||
- 51820:51820/udp # wireguard
|
||||
- 21820:21820/udp # relay
|
||||
- 53:53/udp # DNS
|
||||
- 443:8443 # resources
|
||||
- 80:80 # web
|
||||
- 3004:3004 # gerbil api
|
||||
- 3000:3000 # Pangolin UI
|
||||
|
||||
traefik:
|
||||
image: docker.io/traefik:v3.7.11
|
||||
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
|
||||
- ./config/traefik/logs:/var/log/traefik
|
||||
- ./config/certificates:/var/certificates:ro
|
||||
- ./config/dynamic:/var/dynamic:ro
|
||||
|
||||
networks:
|
||||
default:
|
||||
driver: bridge
|
||||
name: pangolin
|
||||
```
|
||||
|
||||
```yaml tab="Node 2"
|
||||
name: pangolin
|
||||
services:
|
||||
pangolin:
|
||||
image: docker.io/fosrl/pangolin:ee-latest
|
||||
container_name: pangolin
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./config:/app/config
|
||||
- ./config/certificates:/var/certificates
|
||||
- ./config/dynamic:/var/dynamic
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3001/api/v1/"]
|
||||
interval: "10s"
|
||||
timeout: "10s"
|
||||
retries: 15
|
||||
|
||||
gerbil:
|
||||
image: docker.io/fosrl/gerbil:latest
|
||||
container_name: gerbil
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
pangolin:
|
||||
condition: service_healthy
|
||||
command:
|
||||
- --reachableAt=http://<NODE2_INTERNAL_IP>:3004
|
||||
- --generateAndSaveKeyTo=/var/config/key
|
||||
- --remoteConfig=http://pangolin:3001/api/v1/
|
||||
- --trusted-upstreams=<NODE1_EXTERNAL_IP>,<NODE2_EXTERNAL_IP> # All trusted nodes in the cluster
|
||||
volumes:
|
||||
- ./config/:/var/config
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
- SYS_MODULE
|
||||
ports:
|
||||
- 51820:51820/udp # wireguard
|
||||
- 21820:21820/udp # relay
|
||||
- 53:53/udp # DNS
|
||||
- 443:8443 # resources
|
||||
- 80:80 # web
|
||||
- 3004:3004 # gerbil api
|
||||
- 3000:3000 # Pangolin UI
|
||||
|
||||
traefik:
|
||||
image: docker.io/traefik:v3.7.11
|
||||
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
|
||||
- ./config/traefik/logs:/var/log/traefik
|
||||
- ./config/certificates:/var/certificates:ro
|
||||
- ./config/dynamic:/var/dynamic:ro
|
||||
|
||||
networks:
|
||||
default:
|
||||
driver: bridge
|
||||
name: pangolin
|
||||
```
|
||||
|
||||
<Tip>
|
||||
If Pangolin can't reach the local Gerbil at the address in `--reachableAt` (a loopback issue), override it in `privateConfig.yml` - see [Troubleshooting](#troubleshooting) below.
|
||||
</Tip>
|
||||
</Step>
|
||||
|
||||
<Step title="Write config.yml">
|
||||
`config.yml` holds the settings that legitimately differ per node - `gerbil.base_endpoint` and `gerbil.exit_node_name` - alongside the shared PostgreSQL connection and site-type restrictions. In clustered deployments, only Pangolin Sites are supported, so local and basic WireGuard sites are disabled.
|
||||
|
||||
Set `app.dashboard_url` and `server.cors.origins` to your dashboard domain (the one pointed at your load balancer, not at either node) - both must match on every node.
|
||||
|
||||
```yaml tab="Node 1"
|
||||
gerbil:
|
||||
start_port: 51820
|
||||
base_endpoint: "<NODE1_EXTERNAL_IP>"
|
||||
exit_node_name: "node1"
|
||||
|
||||
app:
|
||||
dashboard_url: "https://pangolin.example.com"
|
||||
log_level: "info"
|
||||
|
||||
postgres:
|
||||
connection_string: postgresql://<POSTGRES_USERNAME>:<POSTGRES_PASSWORD>@<POSTGRES_INTERNAL_HOST>:5432/postgres
|
||||
|
||||
traefik:
|
||||
site_types: ["newt"] # Wireguard and local sites are not supported in clustering
|
||||
file_mode: true # Pangolin will generate and save yaml files in a shared volume
|
||||
|
||||
server:
|
||||
secret: "<SECRET>" # Must be identical on every node
|
||||
cors:
|
||||
origins: ["https://pangolin.example.com"]
|
||||
methods: ["GET", "POST", "PUT", "DELETE", "PATCH"]
|
||||
allowed_headers: ["X-CSRF-Token", "Content-Type"]
|
||||
credentials: false
|
||||
maxmind_db_path: "./config/GeoLite2-Country.mmdb" # Download and place into the config dir
|
||||
maxmind_asn_path: "./config/GeoLite2-ASN.mmdb"
|
||||
|
||||
flags:
|
||||
require_email_verification: false
|
||||
disable_signup_without_invite: true
|
||||
disable_user_create_org: false
|
||||
allow_raw_resources: false
|
||||
enable_acme_cert_sync: false
|
||||
disable_local_sites: true
|
||||
disable_basic_wireguard_sites: true
|
||||
disable_config_managed_domains: true
|
||||
```
|
||||
|
||||
```yaml tab="Node 2"
|
||||
gerbil:
|
||||
start_port: 51820
|
||||
base_endpoint: "<NODE2_EXTERNAL_IP>"
|
||||
exit_node_name: "node2"
|
||||
|
||||
app:
|
||||
dashboard_url: "https://pangolin.example.com"
|
||||
log_level: "info"
|
||||
|
||||
postgres:
|
||||
connection_string: postgresql://<POSTGRES_USERNAME>:<POSTGRES_PASSWORD>@<POSTGRES_INTERNAL_HOST>:5432/postgres
|
||||
|
||||
traefik:
|
||||
site_types: ["newt"] # Wireguard and local sites are not supported in clustering
|
||||
file_mode: true # Pangolin will generate and save yaml files in a shared volume
|
||||
|
||||
server:
|
||||
secret: "<SECRET>" # Must be identical on every node
|
||||
cors:
|
||||
origins: ["https://pangolin.example.com"]
|
||||
methods: ["GET", "POST", "PUT", "DELETE", "PATCH"]
|
||||
allowed_headers: ["X-CSRF-Token", "Content-Type"]
|
||||
credentials: false
|
||||
maxmind_db_path: "./config/GeoLite2-Country.mmdb" # Download and place into the config dir
|
||||
maxmind_asn_path: "./config/GeoLite2-ASN.mmdb"
|
||||
|
||||
flags:
|
||||
require_email_verification: false
|
||||
disable_signup_without_invite: true
|
||||
disable_user_create_org: false
|
||||
allow_raw_resources: false
|
||||
enable_acme_cert_sync: false
|
||||
disable_local_sites: true
|
||||
disable_basic_wireguard_sites: true
|
||||
disable_config_managed_domains: true
|
||||
```
|
||||
|
||||
See the [full configuration reference](/self-host/advanced/config-file) for every available option.
|
||||
</Step>
|
||||
|
||||
<Step title="Write privateConfig.yml">
|
||||
`privateConfig.yml` enables Redis-backed cluster sync and Pangolin's built-in DNS and ACME client. **Only one node** - Node 1 in this example - should have `acme.enable_acme_client` set to `true`. That node issues and renews certificates via DNS-01 challenges and stores them encrypted in PostgreSQL; every other node reads the same certificates from the database.
|
||||
|
||||
<Warning>
|
||||
Do not enable `acme.enable_acme_client` on more than one node. Multiple nodes issuing certificates simultaneously will conflict with each other.
|
||||
</Warning>
|
||||
|
||||
```yaml tab="Node 1 (ACME client enabled)"
|
||||
app:
|
||||
region: "region1"
|
||||
identity_provider_mode: "org"
|
||||
redis:
|
||||
host: "<REDIS_INTERNAL_HOST>"
|
||||
port: 6379
|
||||
flags:
|
||||
enable_redis: true
|
||||
use_pangolin_dns: true
|
||||
acme:
|
||||
cert_mode: "pangolin"
|
||||
contact_email: "<CONTACT_EMAIL>"
|
||||
enable_acme_client: true
|
||||
dns:
|
||||
enabled: true
|
||||
nameserver_name: "ns.example.com"
|
||||
cname_extension: "cname.example.com"
|
||||
site_extension: "site.example.com" # Optional
|
||||
```
|
||||
|
||||
```yaml tab="Node 2 (ACME client disabled)"
|
||||
app:
|
||||
region: "region1"
|
||||
identity_provider_mode: "org"
|
||||
redis:
|
||||
host: "<REDIS_INTERNAL_HOST>"
|
||||
port: 6379
|
||||
flags:
|
||||
enable_redis: true
|
||||
use_pangolin_dns: true
|
||||
acme:
|
||||
cert_mode: "pangolin"
|
||||
dns:
|
||||
enabled: true
|
||||
nameserver_name: "ns.example.com"
|
||||
cname_extension: "cname.example.com"
|
||||
site_extension: "site.example.com" # Optional
|
||||
```
|
||||
|
||||
See the [private configuration reference](/self-host/advanced/private-config-file) for every available option.
|
||||
</Step>
|
||||
|
||||
<Step title="Write the Traefik configuration">
|
||||
`traefik/traefik_config.yml` and `dynamic/dynamic_config.yml` are identical on every node - copy them as-is. Traefik loads router and certificate configuration from the shared `dynamic` volume (`file_mode`) instead of Pangolin's API, and exposes a `:53/udp` DNS entry point that forwards to Pangolin's built-in DNS server.
|
||||
|
||||
```yaml title="config/traefik/traefik_config.yml"
|
||||
providers:
|
||||
file:
|
||||
directory: "/var/dynamic"
|
||||
watch: true
|
||||
|
||||
experimental:
|
||||
plugins:
|
||||
badger:
|
||||
moduleName: "github.com/fosrl/badger"
|
||||
version: "v1.7.0"
|
||||
|
||||
log:
|
||||
level: "INFO"
|
||||
format: "common"
|
||||
maxSize: 100
|
||||
maxBackups: 3
|
||||
maxAge: 3
|
||||
compress: true
|
||||
|
||||
entryPoints:
|
||||
web:
|
||||
address: ":80"
|
||||
websecure:
|
||||
address: ":443"
|
||||
proxyProtocol: # We trust gerbil upstream
|
||||
trustedIPs:
|
||||
- 0.0.0.0/0
|
||||
- ::1/128
|
||||
transport:
|
||||
respondingTimeouts:
|
||||
readTimeout: "30m"
|
||||
http:
|
||||
encodedCharacters:
|
||||
allowEncodedSlash: true
|
||||
allowEncodedQuestionMark: true
|
||||
dashboard:
|
||||
address: ":3000"
|
||||
dns:
|
||||
address: ":53/udp"
|
||||
|
||||
serversTransport:
|
||||
insecureSkipVerify: true
|
||||
|
||||
ping:
|
||||
entryPoint: "web"
|
||||
```
|
||||
|
||||
```yaml title="config/dynamic/dynamic_config.yml"
|
||||
http:
|
||||
middlewares:
|
||||
badger:
|
||||
plugin:
|
||||
badger:
|
||||
disableForwardAuth: true
|
||||
|
||||
routers:
|
||||
# Next.js router (handles everything except API and WebSocket paths)
|
||||
next-router:
|
||||
rule: "!PathPrefix(`/api/v1`)"
|
||||
service: next-service
|
||||
entryPoints:
|
||||
- dashboard
|
||||
middlewares:
|
||||
- badger
|
||||
|
||||
# API router (handles /api/v1 paths)
|
||||
api-router:
|
||||
rule: "PathPrefix(`/api/v1`)"
|
||||
service: api-service
|
||||
entryPoints:
|
||||
- dashboard
|
||||
middlewares:
|
||||
- badger
|
||||
|
||||
# WebSocket router
|
||||
ws-router:
|
||||
rule: "PathPrefix(`/`)"
|
||||
service: api-service
|
||||
entryPoints:
|
||||
- dashboard
|
||||
middlewares:
|
||||
- badger
|
||||
|
||||
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
|
||||
|
||||
udp:
|
||||
routers:
|
||||
dns-router:
|
||||
entryPoints:
|
||||
- dns
|
||||
service: dns-service
|
||||
|
||||
services:
|
||||
dns-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- address: "pangolin:53"
|
||||
```
|
||||
|
||||
This is also where you place the MaxMind databases referenced in `config.yml` - download `GeoLite2-Country.mmdb` and `GeoLite2-ASN.mmdb` into each node's `config/` directory. See [Enable Geo-location](/self-host/advanced/enable-geolocation) and [Enable ASN Lookup](/self-host/advanced/enable-asn-lookup).
|
||||
</Step>
|
||||
|
||||
<Step title="Point your load balancer at both nodes">
|
||||
Configure your load balancer - a cloud load balancer or a self-hosted one such as Traefik - to:
|
||||
|
||||
- Route TCP `3000` to both nodes for your Pangolin domain. For example `pangolin.example.com` should resolve to the load balancer, which routes to either node's `:3000` port.
|
||||
- Route UDP `53` to both nodes for DNS. For example `ns.example.com` should resolve to the load balancer, which routes to either node's `:53/udp` port.
|
||||
- Health-check the `:80/ping` endpoint on each node, and stop routing to a node that fails it. `:3000/api/v1/` can also be monitored for the Pangolin UI and API
|
||||
- **Terminate TLS for the dashboard domain** (`pangolin.example.com`) at the load balancer, then forward plain HTTP to `:3000` on the nodes. Obtain and renew that certificate through the load balancer itself (a cloud provider's managed certificate, its own ACME client, etc.) - the nodes' built-in ACME client only covers resource domains, not the dashboard domain
|
||||
|
||||
This is what makes the cluster appear as a single, consistent domain to users, and what drives failover when a node goes down.
|
||||
</Step>
|
||||
|
||||
<Step title="Start the cluster">
|
||||
Bring the database up first, then start **one** Pangolin node - it initializes the database and prints an init token to its logs.
|
||||
|
||||
```bash
|
||||
# On the database host
|
||||
docker compose up -d
|
||||
|
||||
# On Node 1
|
||||
docker compose up -d
|
||||
docker compose logs -f pangolin
|
||||
```
|
||||
|
||||
Use the init token from the logs to visit the dashboard and create the first user. Once Node 1 is healthy and you've logged in, bring up Node 2:
|
||||
|
||||
```bash
|
||||
# On Node 2
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Repeat the Node 2 steps for any additional nodes, incrementing the `exit_node_name` and IP placeholders for each.
|
||||
</Step>
|
||||
|
||||
<Step title="Verify the cluster">
|
||||
- Confirm both nodes report healthy: `curl http://<NODE1_EXTERNAL_IP>/ping` and the same for Node 2
|
||||
- Confirm DNS delegation resolves: `dig @ns.example.com ns.example.com`
|
||||
- Confirm the dashboard is reachable at your `dashboard_url` through the load balancer
|
||||
- Confirm you can create a site and it will report connected
|
||||
- Create a resource, ensure the certificate generates, and is accessible
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Gerbil loopback addressing
|
||||
|
||||
If Pangolin can't reach the local Gerbil instance at the IP configured in `--reachableAt`, force it to address the Docker container directly instead:
|
||||
|
||||
```yaml title="privateConfig.yml"
|
||||
gerbil:
|
||||
local_exit_node_reachable_at: "http://gerbil:3004"
|
||||
```
|
||||
|
||||
## Reference Configuration
|
||||
|
||||
<Card icon="github" arrow="true" cta="View reference configuration" href="https://github.com/fosrl/pangolin/tree/main/config/ha-reference">
|
||||
A complete, working two-node reference configuration on GitHub - including both nodes' Docker Compose and config files - that you can clone and adapt: [github.com/fosrl/pangolin/tree/main/config/ha-reference](https://github.com/fosrl/pangolin/tree/main/config/ha-reference)
|
||||
</Card>
|
||||
@@ -0,0 +1,110 @@
|
||||
---
|
||||
title: "Clustering Requirements"
|
||||
description: "Hosts, networking, and DNS delegation needed before deploying a Pangolin cluster"
|
||||
---
|
||||
|
||||
<Note>
|
||||
Clustering is only available in [Enterprise Edition](/self-host/enterprise-edition).
|
||||
</Note>
|
||||
|
||||
Review these requirements before you start deploying. They cover the hosts you need, the ports that must be open, and the DNS records you need to delegate ahead of time. For background on why each piece exists, see [Understanding Clustering](/self-host/clustering/understanding-clustering).
|
||||
|
||||
## Hosts
|
||||
|
||||
You need a minimum of **three hosts**:
|
||||
|
||||
- **Node 1** - runs Pangolin, Gerbil, and Traefik
|
||||
- **Node 2** - runs Pangolin, Gerbil, and Traefik
|
||||
- **A database host** - runs PostgreSQL and a Redis-compatible server (Valkey, Redis, etc.)
|
||||
|
||||
<Tip>
|
||||
The database host doesn't need to be a dedicated instance. You can run PostgreSQL and Redis however you like - a managed cloud database, an existing cluster, etc. - as long as both nodes can reach them. The only hard requirement is that the Redis-compatible server supports **pub/sub**.
|
||||
</Tip>
|
||||
|
||||
You can add more Pangolin nodes beyond two for additional capacity or regional distribution. The two-node topology in this guide is the minimum for high availability.
|
||||
|
||||
For sizing information, see [Choosing a VPS](/self-host/choosing-a-vps) - the same sizing from single Pangolin node deployments applies to each node in a cluster.
|
||||
|
||||
## Networking
|
||||
|
||||
- **Node 1 and Node 2 each need a public, static IP address**, reachable from the internet
|
||||
- **Node 1 and Node 2 need to be able to address each other** over an internal network
|
||||
- **You must provide your own HA load balancer** in front of both nodes. It needs to terminate HTTPS for the Pangolin UI and accept UDP port 53 for DNS and route to both nodes
|
||||
|
||||
### Dashboard Domain
|
||||
|
||||
You also need a domain for the Pangolin UI and API itself (e.g. `pangolin.example.com`) - this is separate from the nameserver domain above, which is the nameserver to resolve resource DNS.
|
||||
|
||||
Point this domain's DNS record at your **load balancer**, not at either node directly. The load balancer is also responsible for obtaining and serving the TLS certificate for this domain - the nodes' built-in ACME client only issues certificates for resource domains under the delegated nameserver zone, not for the dashboard domain. Terminate TLS at the load balancer and forward plain HTTP to the nodes.
|
||||
|
||||
You must also set this domain as `app.dashboard_url` and add it to `server.cors.origins` in every node's `config.yml`. See [Deploy a Cluster](/self-host/clustering/deploy-a-cluster).
|
||||
|
||||
### Required Ports
|
||||
|
||||
Configure the following firewall rules on each Pangolin node.
|
||||
|
||||
**Inbound**
|
||||
|
||||
| Type | Protocol | Port range | Source | Description |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| HTTP | TCP | 80 | 0.0.0.0/0 | Ping and redirects |
|
||||
| HTTPS | TCP | 443 | 0.0.0.0/0 | Pangolin public resources |
|
||||
| Custom UDP | UDP | 21820 | 0.0.0.0/0 | WireGuard relay port |
|
||||
| Custom UDP | UDP | 51820 | 0.0.0.0/0 | WireGuard port |
|
||||
| DNS (UDP) | UDP | 53 | Load balancer | DNS |
|
||||
| HTTP | TCP | 3000 | Load balancer | Pangolin dashboard UI and API |
|
||||
| Custom TCP | TCP | 3004 | Self + all other nodes | Gerbil node API |
|
||||
|
||||
**Outbound**
|
||||
|
||||
| Type | Protocol | Port range | Destination | Description |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| All traffic | All | All | 0.0.0.0/0 | Allow all outbound |
|
||||
|
||||
## DNS Delegation
|
||||
|
||||
Pangolin's built-in DNS server needs to be delegated authority for a nameserver subdomain. Point an NS record at your load balancer, then optionally delegate additional subdomains through it.
|
||||
|
||||
<Steps>
|
||||
<Step title="Create the nameserver record">
|
||||
Create an A record pointing your chosen nameserver hostname at your load balancer's IP. The examples in this guide use `ns.example.com` - substitute your own domain or subdomain.
|
||||
|
||||
| Name | Type | Value |
|
||||
| --- | --- | --- |
|
||||
| `ns.example.com` | A | `<LOAD_BALANCER_IP>` |
|
||||
</Step>
|
||||
<Step title="Delegate CNAME-based domains (optional)">
|
||||
If you want to support CNAME delegation for resource domains, delegate a subdomain to your nameserver.
|
||||
|
||||
| Name | Type | Value |
|
||||
| --- | --- | --- |
|
||||
| `cname.example.com` | NS | `ns.example.com` |
|
||||
</Step>
|
||||
<Step title="Delegate site-to-cloud resolution (optional)">
|
||||
If you want to support site-to-cloud networking - resolving a site's tunnel address by DNS from within a cloud environment - delegate another subdomain the same way.
|
||||
|
||||
| Name | Type | Value |
|
||||
| --- | --- | --- |
|
||||
| `site.example.com` | NS | `ns.example.com` |
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
These three hostnames map directly to the `dns` section of `privateConfig.yml`, covered in [Deploy a Cluster](/self-host/clustering/deploy-a-cluster):
|
||||
|
||||
```yaml
|
||||
dns:
|
||||
enabled: true
|
||||
nameserver_name: "ns.example.com"
|
||||
cname_extension: "cname.example.com"
|
||||
site_extension: "site.example.com" # Optional
|
||||
```
|
||||
|
||||
## Other Requirements
|
||||
|
||||
- **Contact email** for Let's Encrypt ACME registration
|
||||
- **GeoIP databases** - download and keep up to date the MaxMind `GeoLite2-Country.mmdb` and `GeoLite2-ASN.mmdb` databases, placed in each node's `config/` directory. See [Enable Geo-location](/self-host/advanced/enable-geolocation) and [Enable ASN Lookup](/self-host/advanced/enable-asn-lookup)
|
||||
- **Site type support** - in clustered deployments, only Pangolin Sites are supported. Local sites and basic WireGuard sites are not supported
|
||||
|
||||
<Card title="Deploy a Cluster" href="/self-host/clustering/deploy-a-cluster" icon="server">
|
||||
Once these requirements are met, follow the full deployment walkthrough.
|
||||
</Card>
|
||||
@@ -0,0 +1,139 @@
|
||||
---
|
||||
title: "Understanding Clustering"
|
||||
description: "Architecture and concepts behind running Pangolin as a highly available cluster"
|
||||
---
|
||||
|
||||
<Note>
|
||||
Clustering is only available in [Enterprise Edition](/self-host/enterprise-edition).
|
||||
</Note>
|
||||
|
||||
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.
|
||||
|
||||
In a clustered configuration, multiple Pangolin instances operate together, sharing state through a PostgreSQL database and a Valkey (Redis) server. Each instance independently serves user requests, resolves DNS, manages authentication, and coordinates with its own Gerbil instance to support thousands of sites across your organization.
|
||||
|
||||
## Architecture
|
||||
|
||||
A Pangolin cluster consists of several coordinated components that work together to provide high availability and seamless failover.
|
||||
|
||||
<Frame>
|
||||
<img
|
||||
src="/images/ha-deployment-diagram.png"
|
||||
alt="Diagram showing the cluster deployment"
|
||||
/>
|
||||
</Frame>
|
||||
|
||||
### Pangolin Instances
|
||||
|
||||
**Purpose**: Serve the web UI and API, resolve DNS, issue and renew TLS certificates, and coordinate cluster state.
|
||||
|
||||
**How It Works**:
|
||||
- Multiple Pangolin instances run simultaneously across different nodes, one per node
|
||||
- Each instance can independently handle user authentication and requests
|
||||
- All instances share state through the PostgreSQL database and Valkey
|
||||
- Each instance embeds a DNS server used for resource resolution and ACME DNS-01 challenges
|
||||
- Only one instance in the cluster should be configured as the ACME client. It issues and renews certificates and stores them encrypted in PostgreSQL. Every other instance reads the same certificates from the database
|
||||
|
||||
**High Availability**: A load balancer sits in front of all Pangolin instances. If any instance goes down, the load balancer automatically routes traffic to healthy nodes, ensuring the UI, API, and DNS remain accessible from the same domain without interruption.
|
||||
|
||||
### PostgreSQL Database
|
||||
|
||||
**Purpose**: Store all persistent cluster state in a centralized, shared database.
|
||||
|
||||
**How It Works**:
|
||||
- All Pangolin instances connect to the same shared PostgreSQL database
|
||||
- Stores user accounts, site configurations, resources, access policies, and organizational settings
|
||||
- Certificates are stored encrypted in the database for security
|
||||
- Changes made through any instance are immediately available cluster-wide
|
||||
|
||||
**High Availability**: Database replication and backup strategies ensure data persistence and availability across the cluster. See [Database Options](/self-host/advanced/database-options) for general PostgreSQL configuration.
|
||||
|
||||
### Valkey (Redis)
|
||||
|
||||
**Purpose**: Provide real-time state synchronization between cluster nodes.
|
||||
|
||||
**How It Works**:
|
||||
- Pub/sub messaging handles cross node messaging for websocket command control
|
||||
- Handles caching for the cluster
|
||||
- Any Redis-compatible server works, as long as it supports pub/sub
|
||||
|
||||
**High Availability**: Ensures that session and connection information remains available even when individual nodes fail.
|
||||
|
||||
### Traefik Instances
|
||||
|
||||
**Purpose**: Route HTTP/HTTPS traffic to resources and terminate TLS connections.
|
||||
|
||||
**How It Works**:
|
||||
- Each cluster node runs its own Traefik instance
|
||||
- Pangolin writes router configuration and certificates to a shared volume with Traefik (`file_mode`) instead of Traefik scraping the Pangolin API directly, since Traefik can only load certificates from files
|
||||
- Each node's Pangolin instance pulls the certificate from the database to that shared volume so its local Traefik can read it
|
||||
- Sits behind Gerbil, which runs an SNI proxy for traffic routing
|
||||
|
||||
**High Availability**: Multiple Traefik instances ensure traffic routing continues even if individual nodes fail.
|
||||
|
||||
### Gerbil Instances
|
||||
|
||||
**Purpose**: Manage WireGuard tunnels to site connectors and route traffic between cluster nodes.
|
||||
|
||||
**How It Works**:
|
||||
- Each Pangolin instance runs alongside its own Gerbil tunnel manager
|
||||
- Handles WireGuard VPN connections from Pangolin Site connectors
|
||||
- Site connectors can establish tunnels to any available Gerbil instance
|
||||
- Every Gerbil instance is made aware of the other trusted nodes in the cluster
|
||||
- When a request lands on the node that isn't holding the relevant tunnel, Gerbil routes it to the correct node instead of dropping it - this covers the case where DNS caching sends a client to the "wrong" node
|
||||
|
||||
**High Availability**: Distributed tunnel management ensures connectivity remains available even if individual Gerbil instances fail, with automatic cross-node failover.
|
||||
|
||||
### Load Balancer
|
||||
|
||||
**Purpose**: Distribute incoming traffic across healthy Pangolin instances.
|
||||
|
||||
**How It Works**:
|
||||
- Sits in front of all cluster nodes, fronting the dashboard/API port, HTTP/HTTPS resource ports, and DNS
|
||||
- Monitors instance health (the `/ping` endpoint) and routes traffic only to available nodes
|
||||
- Ensures all traffic reaches the cluster through a single, consistent domain
|
||||
- Does NOT handle resource routing - these are directed to the correct node directly by the DNS server
|
||||
|
||||
**High Availability**: Essential for continuous access to the Pangolin UI, API, and DNS regardless of individual instance failures. **You must provide your own HA load balancer** in front of the cluster - this can be a cloud load balancer or a self-hosted one.
|
||||
|
||||
## Traffic Flow
|
||||
|
||||
Understanding how requests flow through the cluster helps clarify how these components work together:
|
||||
|
||||
1. **User access**: Users access the Pangolin UI/API through the load balancer, which routes to any healthy Pangolin instance
|
||||
2. **Resource requests**: When accessing a resource, DNS resolves to the node that the site is connected to, and the request is routed to that node's Gerbil instance
|
||||
3. **Cross-node routing**: If DNS caching or the load balancer points to a node that isn't holding the relevant tunnel, Gerbil routes the request to the correct node
|
||||
4. **Tunnel routing**: Gerbil receives the request and forwards it to the local Traefik instance
|
||||
5. **TLS termination**: Traefik terminates TLS using the certificate synced to its shared volume, then proxies the request to the site connector's tunnel
|
||||
|
||||
## How Failover Works
|
||||
|
||||
When a node fails, the load balancer stops routing to it and traffic continues to flow through the remaining healthy nodes. This keeps the API, UI, and DNS running without interruption.
|
||||
|
||||
The sites connected to the failed node will being detecting ping failures and will initiate a reconnection requests to connect to a different online node. Once connected, the DNS will update the resource resolution to point to the new node, and traffic will continue without user disruption. There may be brief periods of downtime while the site connector detects the failure and reconnects to a healthy node, but this is typically only a few seconds.
|
||||
|
||||
## 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.
|
||||
|
||||
**Simplified Infrastructure**: DNS resolution and certificate management are built into Pangolin itself, so there's no separate DNS or certificate-issuing service to deploy, scale, or keep highly available on top of the cluster.
|
||||
|
||||
**Dynamic Failover**: Automatic traffic routing between nodes and the load balancer ensures resources remain accessible when nodes fail.
|
||||
|
||||
## Enterprise Support
|
||||
|
||||
Clustered deployments require careful planning around database replication, Valkey configuration, network topology, DNS delegation, and monitoring. For organizations interested in clustering for high availability or regional distribution, please [contact our enterprise team](https://pangolin.net/contact) to discuss your requirements and receive implementation guidance support. A support contract is not required for deployment.
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Requirements" href="/self-host/clustering/requirements" icon="list-check">
|
||||
Review the hosts, network, and DNS delegation a cluster needs before you deploy.
|
||||
</Card>
|
||||
<Card title="Deploy a Cluster" href="/self-host/clustering/deploy-a-cluster" icon="server">
|
||||
Follow a complete walkthrough for standing up a two-node cluster.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,213 @@
|
||||
---
|
||||
title: "CrowdSec"
|
||||
---
|
||||
|
||||
<Note>
|
||||
To install Crowdsec with the offical installer, start it with the `--crowdsec` flag. This will prompt for a Crowdsec install at the end of the process.
|
||||
</Note>
|
||||
|
||||
<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.
|
||||
|
||||
<Tip>
|
||||
Enabling CrowdSec turns on Traefik access logging so CrowdSec can analyze traffic. This means `config/traefik/logs/access.log` will grow over time. If you want to set up log rotation, see the [Traefik Access Log Rotation](/self-host/advanced/traefik-log-rotation) guide.
|
||||
</Tip>
|
||||
|
||||
## 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
|
||||
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
|
||||
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
|
||||
iptables -A INPUT -j LOG --log-prefix "iptables: "
|
||||
```
|
||||
|
||||
Update the `docker-compose.yml` file as follows:
|
||||
|
||||
```yaml
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
docker exec -it crowdsec cscli bouncers add vps-firewall
|
||||
```
|
||||
|
||||
4. Copy the displayed API key and insert it into the bouncer's configuration file:
|
||||
|
||||
```bash
|
||||
nano /etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml
|
||||
```
|
||||
|
||||
5. Restart the firewall bouncer:
|
||||
|
||||
```bash
|
||||
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
|
||||
service:
|
||||
crowdsec:
|
||||
ports:
|
||||
- 6060:6060 # Metrics port
|
||||
- 8080:8080 # Local API port
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Docker’s 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
|
||||
docker exec crowdsec cscli metrics
|
||||
```
|
||||
|
||||
The output should look like this:
|
||||
|
||||
```bash
|
||||
+------------------------------------------------------------------+
|
||||
| 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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
docker exec crowdsec cscli decisions add --ip <YOUR IP> -d 1m --type ban
|
||||
```
|
||||
|
||||
To trigger a captcha challenge:
|
||||
|
||||
```bash
|
||||
docker exec crowdsec cscli decisions add --ip <YOUR IP> -d 1m --type captcha
|
||||
```
|
||||
@@ -0,0 +1,73 @@
|
||||
---
|
||||
title: "GeoBlock"
|
||||
---
|
||||
|
||||
<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>
|
||||
|
||||
<Warning>
|
||||
Legacy guide. Pangolin now supports native geo-blocking. If you previously installed this plugin, follow [Remove GeoBlock Plugin](/self-host/community-guides/remove-geoblock-plugin) before enabling native geo-blocking.
|
||||
</Warning>
|
||||
|
||||
GeoBlock is a Traefik middleware that uses IP-based geolocation to allow or block traffic from specific countries. It helps enhance security and access control by restricting unwanted or potentially harmful connections based on geographic regions.
|
||||
|
||||
## Installation
|
||||
|
||||
To integrate GeoBlock into your Traefik setup, follow the steps below:
|
||||
|
||||
1. Add the following configuration to your `/config/traefik/traefik_config.yml` file:
|
||||
|
||||
```yaml
|
||||
entryPoints:
|
||||
websecure:
|
||||
http:
|
||||
middlewares:
|
||||
- geoblock@file
|
||||
|
||||
experimental:
|
||||
plugins:
|
||||
geoblock:
|
||||
moduleName: github.com/PascalMinder/geoblock
|
||||
version: v0.3.2
|
||||
```
|
||||
|
||||
2. Add the following configuration to your `/config/traefik/dynamic_config.yml` file. Setting `blackListMode: false` enables GeoBlock in whitelist mode, allowing only the specified countries. Remember to add the appropriate countries when traveling. A list of country codes can be found in the [documentation](https://github.com/PascalMinder/geoblock#full-plugin-sample-configuration).
|
||||
|
||||
```yaml
|
||||
http:
|
||||
middlewares:
|
||||
geoblock:
|
||||
plugin:
|
||||
geoblock:
|
||||
silentStartUp: false
|
||||
allowLocalRequests: true
|
||||
logLocalRequests: false # change to true to see logs and verify if it is working
|
||||
logAllowedRequests: false # change to true to see logs and verify if it is working
|
||||
logApiRequests: false # change to true to see logs and verify if it is working
|
||||
api: "https://get.geojs.io/v1/ip/country/{ip}"
|
||||
apiTimeoutMs: 500
|
||||
cacheSize: 25
|
||||
forceMonthlyUpdate: true
|
||||
allowUnknownCountries: false
|
||||
unknownCountryApiResponse: "nil"
|
||||
blackListMode: false
|
||||
countries:
|
||||
- DE # add/replace with your country code
|
||||
```
|
||||
|
||||
3. Restart Traefik to apply the changes:
|
||||
|
||||
```bash
|
||||
docker restart traefik
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
To monitor GeoBlock activities in the Traefik logs, enable logging by setting the following options to `true`:
|
||||
|
||||
```yaml
|
||||
logLocalRequests: true
|
||||
logAllowedRequests: true
|
||||
logApiRequests: true
|
||||
```
|
||||
@@ -0,0 +1,112 @@
|
||||
---
|
||||
title: "GeoLite2 Automation"
|
||||
description: "A simple automation to download & update your GeoLite2 databases with geoipupdate"
|
||||
---
|
||||
|
||||
<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 and update the `GeoLite2-Country` and `GeoLite2-ASN` databases from MaxMind. Pangolin uses these databases for geo-location features such as country or region rules, geo-blocking, analytics, and ASN blocking. It uses MaxMind's [geoipupdate](https://github.com/maxmind/geoipupdate/tree/main) Docker container to do 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="/images/maxmind_manage-license-keys.jpeg" alt="Maxmind's Manage license keys page" />
|
||||
</Frame>
|
||||
|
||||
Give your new key a name. E.g. `Pangolin`.
|
||||
|
||||
<Frame caption="Choose a name for the key">
|
||||
<img src="/images/maxmind_create-key-page.jpeg" alt="Maxmind's key creation page" />
|
||||
</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="/images/maxmind_key-created.jpeg" alt="The key got created successfully" />
|
||||
</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
|
||||
cd pangolin
|
||||
```
|
||||
Shut down Pangolin with:
|
||||
```bash
|
||||
docker compose down
|
||||
```
|
||||
Open `docker-compose.yml` with your favorite text editor.
|
||||
E.g. nano:
|
||||
```bash
|
||||
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
|
||||
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
|
||||
cd config
|
||||
```
|
||||
|
||||
Add these lines to the `server` object
|
||||
|
||||
```yaml
|
||||
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
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Et voilà, you are now able to define country rules and ASN rules for your resources! 🏁
|
||||
|
||||
btw: you can use these exact databases for your Traefik dashboard too -> [Community Guide](/self-host/community-guides/traefiklogsdashboard)
|
||||
@@ -0,0 +1,142 @@
|
||||
---
|
||||
title: "Home Assistant Add-on"
|
||||
---
|
||||
|
||||
<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
|
||||
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
|
||||
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
|
||||
|
||||
<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
|
||||
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
|
||||
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,200 @@
|
||||
---
|
||||
title: "Metrics"
|
||||
---
|
||||
|
||||
<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
|
||||
service:
|
||||
gerbil:
|
||||
ports:
|
||||
- 8082:8082
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Docker’s 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
|
||||
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
|
||||
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
|
||||
service:
|
||||
crowdsec:
|
||||
ports:
|
||||
- 6060:6060
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Docker’s 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
|
||||
sudo docker restart crowdsec
|
||||
```
|
||||
|
||||
## Prometheus
|
||||
|
||||
1. Create a new Prometheus container or add it to `docker-compose.yml` of Pangolin stack:
|
||||
|
||||
```yaml
|
||||
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>
|
||||
Docker’s 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
|
||||
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
|
||||
chown nobody:nogroup data
|
||||
```
|
||||
|
||||
4. Start the Prometheus container:
|
||||
|
||||
```bash
|
||||
sudo docker compose up -d
|
||||
```
|
||||
|
||||
## Grafana
|
||||
|
||||
1. Create a new Grafana container or add it to `docker-compose.yml` of Pangolin stack:
|
||||
|
||||
```yaml
|
||||
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>
|
||||
Docker’s 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
|
||||
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="/images/traefik_dashboard.png" alt="Traefik Dashboard"/>
|
||||
</Frame>
|
||||
|
||||
Template Import ID = 17346
|
||||
|
||||
https://grafana.com/grafana/dashboards/17346-traefik-official-standalone-dashboard/
|
||||
|
||||
#### Crowdsec
|
||||
|
||||
https://github.com/crowdsecurity/grafana-dashboards/tree/master
|
||||
@@ -0,0 +1,171 @@
|
||||
---
|
||||
title: "Middleware Manager"
|
||||
---
|
||||
|
||||
<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
|
||||
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
|
||||
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
|
||||
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
|
||||
providers:
|
||||
file:
|
||||
directory: "/rules"
|
||||
watch: true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
|
||||
## Step 4: Start Services
|
||||
|
||||
```bash
|
||||
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
|
||||
middlewares:
|
||||
- id: "rate-limit"
|
||||
type: "rateLimit"
|
||||
config:
|
||||
average: 100
|
||||
burst: 50
|
||||
```
|
||||
|
||||
### Security Headers
|
||||
|
||||
```yaml
|
||||
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 Traefik’s powerful middleware ecosystem.
|
||||
|
||||
* Start with simple configs → test thoroughly → expand gradually.
|
||||
* Use templates where possible.
|
||||
* Always validate in staging before production.
|
||||
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
---
|
||||
title: "Overview"
|
||||
---
|
||||
|
||||
<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 CrowdSec’s security engine to block malicious traffic in real time. It runs as middleware within a Traefik container and enforces decisions based on CrowdSec’s 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 client’s 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 geo-blocking. 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 geo-blocking.
|
||||
</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,66 @@
|
||||
---
|
||||
title: "Remove GeoBlock Plugin"
|
||||
---
|
||||
|
||||
<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 geo-blocking. If you previously installed the Traefik GeoBlock plugin, remove it before enabling native geo-blocking to avoid duplicate blocking or startup errors.
|
||||
|
||||
<Note>
|
||||
After cleanup, follow [Enable Geo-location](/self-host/advanced/enable-geolocation) to configure the geo-location database used by native geo-blocking 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
|
||||
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
|
||||
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
|
||||
http:
|
||||
middlewares:
|
||||
geoblock:
|
||||
plugin:
|
||||
geoblock:
|
||||
...
|
||||
```
|
||||
</Step>
|
||||
<Step title="Restart Traefik">
|
||||
Restart Traefik to apply the changes:
|
||||
|
||||
```bash
|
||||
docker restart traefik
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Next steps
|
||||
|
||||
Follow [Enable Geo-location](/self-host/advanced/enable-geolocation) to configure the geo-location database used by native geo-blocking in Pangolin.
|
||||
@@ -0,0 +1,62 @@
|
||||
---
|
||||
title: "Bypass Rules"
|
||||
description: "Community bypass rules for common self hosted apps"
|
||||
---
|
||||
|
||||
This table compiles paths that need to be allowed for various apps to work with Pangolin authentication.
|
||||
|
||||
| App | Required Bypass Rules |
|
||||
| -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **Media Management** | |
|
||||
| Radarr | `/api/*` |
|
||||
| Sonarr | `/api/*` |
|
||||
| Lidarr | `/api/*` |
|
||||
| **Media Servers** | |
|
||||
| Jellyfin (iOS) | `/system/info/public` |
|
||||
| Jellyfin (Roku) | `/System/Info/Public`<br />`/Users/AuthenticateByName`<br />`/Users/Public`<br />`/QuickConnect/Initiate`<br />`/QuickConnect/Connect`<br />`/Users/AuthenticateWithQuickConnect` |
|
||||
| Audiobookshelf | Audiobookshelf also supports `/audiobookshelf` by default. Each rule should also be applied to this path.<br />`/api/*`<br />`/login`<br />`/auth/*`<br />`/feed/*`<br />`/socket.io/`<br />`/status`<br />`/logout`<br />`/ping`<br />`/public/*`<br />The following is needed for public shares and is optional for clients:<br />`/share/*`<br />`/_nuxt/*.js`<br />`/_nuxt/fonts/*` |
|
||||
| **Management & Monitoring** | |
|
||||
| Tautulli | `/api/*` |
|
||||
| Harbour | `/api/*` |
|
||||
| Hoarder App | `/api/*` |
|
||||
| Uptime Kuma Manager | `/api/*`<br />`/socket.io/*` |
|
||||
| Beszel | `/api/beszel/agent-connect` |
|
||||
| MeshCentral | `/api/*`<br />`/meshrelay.ashx`<br />`/agent.ashx` |
|
||||
| **Security & Privacy** | |
|
||||
| AdGuard Home | `/api/*` |
|
||||
| Ente Auth | `*api*` |
|
||||
| Vaultwarden/Bitwarden | `/api/*`<br />`/identity/*`<br />`/wl/*`<br /> `/notifications/hub` required for websocket notifications enabled by default since v1.29.0 of Vaultwarden <br /> `/icons/*` For proper loading of Favicons (Optional) <br />Always Deny - Path - `/admin/*` |
|
||||
| **Cloud & Sync** | |
|
||||
| Nextcloud | `/` (Main interface)<br />`/index.php/*` (Core handler)<br />`/remote.php` (Remote access)<br />`/status.php` (Status checks)<br />`/ocs` (Collaboration Services API)<br />`/apps/*` (Applications)<br />`/remote.php/webdav/*` (WebDAV endpoint)<br />`/remote.php/dav/*` (CalDAV/CardDAV)<br />`/remote.php/caldav/*` (Calendar sync)<br />`/remote.php/carddav/*` (Contacts sync)<br />`/ocs/v1.php/*` (API endpoints)<br />`/ocs/v2.php/*` (API v2 endpoints)<br />`/login` (Authentication)<br />`/.well-known/*` (Service discovery)<br />`/.well-known/webfinger` (WebFinger protocol)<br />`/s/*` (Shared files/folders)<br />`/heartbeat` (Session keep-alive)<br />`/ocs-provider/*` (OCS capability discovery)<br />`/ocm-provider/*` (Federated sharing discovery)<br />`/push/*` (notify_push real-time notifications) |
|
||||
| Onlyoffice | `/cache/*`<br />`*/CommandService.ashx`<br />`*/converter/*`<br />`*/doc/*`<br />`*/downloadas/*`<br />`/downloadfile/*`<br />`*/fonts/*`<br />`/healthcheck`<br />`/methodology/*`<br />`*/plugins.json`<br />`*/sdkjs/*`<br />`*/sdkjs-plugins/*`<br />`*/themes.json`<br />`*/web-apps/*` |
|
||||
| **Photo Management** | |
|
||||
| Ente Photos | `*api*` |
|
||||
| Immich | `/api/*`<br />`/.well-known/immich` |
|
||||
| **File Management** | |
|
||||
| Filebrowser | `/static/*`<br />`/share/*` <br/> `/api/public/dl/*` <br/> `/api/public/share/*` |
|
||||
| **Notes & Knowledge Management** | |
|
||||
| Docmost | `/share/*`<br />`/api/*`<br />`/assets/index*/*`<br />`/icons/favicons-*`<br />Always Deny - Path - `/login/*` (optional)
|
||||
| Joplin Notes Server | `/api/*`<br />`/shares/*`<br />`/css/*`<br />`/images/*`<br />Always Deny - Path - `/login/*` (optional) |
|
||||
| Erugo | `/api/*`<br />`/shares/*`<br />`/build/*`<br />`/get-logo` |
|
||||
| Memos | `/api/*`<br />`/assets/*`<br />`/explore*`<br />`/memos.api.v1.*`<br />`/auth/callback*`<br />`/auth`<br />`/site.webmanifest`<br />`/logo.webp`<br />`/full-logo.webp`<br />`/android-chrome-192x192.png` |
|
||||
| Linkding | `/api/*`<br />`/bookmarks/*`<br />Always Deny - Path - `/admin/*` |
|
||||
| **Communication** | |
|
||||
| Matrix/Synapse (Clients) | `/_matrix/*`<br />`/_synapse/client/*` |
|
||||
| Matrix/Synapse (Federation) | `/_matrix/*` |
|
||||
| **Notifications** | |
|
||||
| Gotify | `/version`<br />`/message`<br />`/application`<br />`/client`<br />`/stream`<br />`/plugin`<br />`/health` |
|
||||
| **Home Automation** | |
|
||||
| Home Assistant | `/api/*`<br />`/auth/*`<br />`/frontend_latest/*`<br />`/lovelace/*`<br />`/static/*`<br />`/hacsfiles/*`<br />`/local/*`<br />`/manifest.json`<br />`/sw-modern.js` |
|
||||
| n8n | `/webhook-test/*/webhook`<br />`/webhook/*/webhook` |
|
||||
| **Project Management** | |
|
||||
| Jetbrains Youtrack | `/api/*`<br />`/hub/api/*`<br /> |
|
||||
| **Genealogy** | |
|
||||
| Gramps Web | `/api/*` |
|
||||
| **Analytics** | |
|
||||
| Liwan | `/script.js`<br /> `/api/send` |
|
||||
| Umami | `/script.js`<br /> `/api/send` |
|
||||
|
||||
<Note>
|
||||
These rules are examples and may need to be adjusted based on your specific
|
||||
app configuration and version.
|
||||
</Note>
|
||||
@@ -0,0 +1,214 @@
|
||||
---
|
||||
title: "Securing Traefik Post-Install"
|
||||
description: "Hardening steps after Traefik v3 install—split file provider rules, TLS options, security headers, HTTPS redirects, and EC384 ACME certificates."
|
||||
---
|
||||
|
||||
The steps I would take to secure Traefik and such post install.
|
||||
|
||||
Treat this as a template—adapt entrypoint names, paths, resolvers, and middleware references to your stack. Don't copy-paste the blocks wholesale without checking they match your setup.
|
||||
|
||||
A few I would recommend but this assumes you won't ever be doing http traffic.. (externally)
|
||||
|
||||
```yaml
|
||||
redirections: # Only caring for https based traffic.
|
||||
entryPoint:
|
||||
to: websecure # match your websecure entrypoint name.
|
||||
scheme: https
|
||||
permanent: true
|
||||
```
|
||||
|
||||
Into your traefik_dynamic though I would recommend actually splitting the files and using like,
|
||||
|
||||
```yaml
|
||||
providers:
|
||||
file:
|
||||
directory: "/rules" # Path inside the container; must match the mount target (e.g. ./rules:/rules:ro below).
|
||||
watch: true
|
||||
```
|
||||
|
||||
By default it's using a file path which makes adding more file like stuff annoying,
|
||||
|
||||
```yaml
|
||||
traefik:
|
||||
image: traefik:v3.7
|
||||
container_name: traefik
|
||||
network_mode: service:gerbil
|
||||
restart: always
|
||||
secrets:
|
||||
- cf_dns_api_token
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
volumes:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- ./rules:/rules:ro # This
|
||||
```
|
||||
|
||||
Then we go in the folder relative of the compose,
|
||||
|
||||
You will need to move your traefik dynamic into this folder. Depending how you have set it up I would also recommend to be using wildcard certs with dns validation if you aren't already.
|
||||
|
||||
Anyways, `/rules/tls.yml`
|
||||
|
||||
```yaml
|
||||
# yaml-language-server: $schema=https://json.schemastore.org/traefik-v3-file-provider.json
|
||||
|
||||
tls:
|
||||
options:
|
||||
default:
|
||||
minVersion: VersionTLS12
|
||||
maxVersion: VersionTLS13
|
||||
sniStrict: true # Clients without SNI or wrong hostname will fail; expected for strict TLS.
|
||||
# clientAuth:
|
||||
# # in PEM format. each file can contain multiple CAs.
|
||||
# caFiles:
|
||||
# - /certs/global/origin-pull-ca.pem
|
||||
# clientAuthType: RequireAndVerifyClientCert
|
||||
cipherSuites:
|
||||
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
|
||||
- TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
|
||||
- TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
|
||||
- TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
|
||||
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
|
||||
curvePreferences:
|
||||
- secp521r1
|
||||
- secp384r1
|
||||
- x25519
|
||||
```
|
||||
|
||||
`/rules/middlewares.yml`
|
||||
|
||||
Though would recommend to setup chains if you plan to have other middleware's to be aligned.. The above will score you an A+
|
||||
|
||||
```yaml
|
||||
http:
|
||||
middlewares:
|
||||
middlewares-secure-headers:
|
||||
headers:
|
||||
accessControlAllowMethods:
|
||||
- GET
|
||||
- OPTIONS
|
||||
- PUT
|
||||
hostsProxyHeaders:
|
||||
- "X-Forwarded-Host"
|
||||
#sslRedirect: true # Not used in Version 2.5
|
||||
stsSeconds: 63072000
|
||||
stsIncludeSubdomains: true
|
||||
stsPreload: true
|
||||
forceSTSHeader: true
|
||||
frameDeny: true #overwritten by customFrameOptionsValue
|
||||
customFrameOptionsValue: "SAMEORIGIN"
|
||||
contentTypeNosniff: true
|
||||
browserXssFilter: false
|
||||
customBrowserXSSValue: "0"
|
||||
referrerPolicy: "same-origin"
|
||||
contentSecurityPolicy: "upgrade-insecure-requests"
|
||||
customResponseHeaders:
|
||||
X-Robots-Tag: "none, noarchive, nosnippet, notranslate, noimageindex"
|
||||
X-powered-by: ""
|
||||
server: ""
|
||||
Permissions-Policy: "camera=(), microphone=(), geolocation=()"
|
||||
|
||||
chain-secure:
|
||||
chain:
|
||||
middlewares:
|
||||
- middlewares-secure-headers
|
||||
```
|
||||
|
||||
In your `traefik_config.yml`
|
||||
|
||||
```yaml
|
||||
ocsp: {} # Let Traefik check for ocsp over clients,
|
||||
|
||||
entryPoints:
|
||||
web:
|
||||
address: ":80"
|
||||
http:
|
||||
redirections: # Only caring for https based traffic.
|
||||
entryPoint:
|
||||
to: websecure
|
||||
scheme: https
|
||||
permanent: true
|
||||
# forwardedHeaders: # Don't need this unless you're using cloudflare infront or so
|
||||
# trustedIPs: *trustedIPs
|
||||
websecure:
|
||||
address: ":443"
|
||||
asDefault: true # Routers without an entryPoints list use websecure. Mostly useful for file entries an example shown at the very bottom.
|
||||
http3: # If you want to have QUIC you can have these just make sure to enable in the compose - 443:443/udp but if not you can skip this.
|
||||
advertisedPort: 443
|
||||
# transport: # Can lead to dos attacks if you're not careful.
|
||||
# respondingTimeouts:
|
||||
# readTimeout: "30m"
|
||||
http:
|
||||
middlewares:
|
||||
- middlewares-secure-headers@file # We don't include this on the web part as some stuff like HSTS and other headers aren't meant to be served on http at all and would go against spec like Fiesty duck's.
|
||||
tls:
|
||||
options: default # Can be named anything to align with the rules/tls.yml
|
||||
certResolver: dns # Match to your certResolver line in /config/traefik/traefik_config.yml
|
||||
|
||||
# forwardedHeaders: # Don't set this unless you know what you're doing.
|
||||
# trustedIPs: *trustedIPs # *trustedIPs must be a YAML anchor defined earlier in this file (e.g. Cloudflare IP ranges).
|
||||
|
||||
# proxyProtocol: # Don't set this unless you know what you're doing.
|
||||
# trustedIPs: *InternalIPs # *InternalIPs anchor—only enable if something in front actually sends PROXY protocol.
|
||||
```
|
||||
|
||||
Would also recommend to ask for EC384 certs which are more secure then RSA but also might limit older clients from connecting, You can do so via,
|
||||
|
||||
```yaml
|
||||
certificatesResolvers:
|
||||
dns:
|
||||
acme:
|
||||
storage: acme.json # Use a persistent volume path; file must be writable by Traefik and chmod'd to 600.
|
||||
keyType: 'EC384' # This asks LE for ECC certs over RSA, For reference a ECC384 is like asking for a RSA 7680-bit over like the normal RSA4096 or 2048's.
|
||||
dnsChallenge:
|
||||
provider: cloudflare # Change if you use another DNS host; provider must match your env/secrets setup.
|
||||
resolvers:
|
||||
- "1.1.1.1:53"
|
||||
- "1.0.0.1:53"
|
||||
```
|
||||
|
||||
## Pangolin dashboard (`/rules/pangolin.yml`)
|
||||
|
||||
Routers below use `chain-secure@file` from `/rules/middlewares.yml`.
|
||||
|
||||
```yaml
|
||||
http:
|
||||
routers:
|
||||
next-router:
|
||||
rule: >
|
||||
(Host(`pangolin.{{ env "DOMAIN1" }}`))
|
||||
service: next-service
|
||||
middlewares:
|
||||
- chain-secure@file
|
||||
priority: 10
|
||||
|
||||
api-router:
|
||||
rule: >
|
||||
(Host(`pangolin.{{ env "DOMAIN1" }}`) && PathPrefix(`/api/v1`))
|
||||
service: api-service
|
||||
middlewares:
|
||||
- chain-secure@file
|
||||
priority: 100
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
What's not shown is like DANE-443 (TLSA), RoFS which I will write later along with the apparmor/seccomp profiles.
|
||||
@@ -0,0 +1,289 @@
|
||||
---
|
||||
title: "Traefik Log Dashboard (v2 – Agent Architecture)"
|
||||
---
|
||||
|
||||
<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 you’re 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
|
||||
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
|
||||
# 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
|
||||
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
|
||||
# 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
|
||||
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
|
||||
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,212 @@
|
||||
---
|
||||
title: "DNS & Networking"
|
||||
description: "Configure your domain, DNS records, and network settings for Pangolin deployment"
|
||||
---
|
||||
|
||||
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 Pangolin Sites to establish tunnels to the proxy.
|
||||
</Card>
|
||||
|
||||
<Card title="UDP Port 21820">
|
||||
**Client Tunnels**
|
||||
|
||||
This is the default port for clients relaying through the server to Pangolin Sites. 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 [Docker’s 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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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,317 @@
|
||||
---
|
||||
title: "Enterprise Edition"
|
||||
description: "Learn about Enterprise Edition licensing, plans, and how to get started"
|
||||
---
|
||||
|
||||
When self-hosting Pangolin, you can run the **Community Edition** or the **Enterprise Edition**. Both editions provide the same core functionality. Enterprise Edition unlocks additional features with a license key on the `ee` Docker image.
|
||||
|
||||
<Check>
|
||||
Enterprise Edition is **free** for personal use and organizations with **less than $100,000 USD** gross annual revenue. You still need a valid license key to activate it.
|
||||
</Check>
|
||||
|
||||
<Warning>
|
||||
Organizations with **$100,000+ USD** gross annual revenue require a **paid commercial license** to use Enterprise Edition.
|
||||
</Warning>
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="Get a free license" icon="user" href="#get-a-free-license-personal-use">
|
||||
Personal use and small organizations under the revenue threshold.
|
||||
</Card>
|
||||
|
||||
<Card title="Compare features and plans" icon="table-list" href="https://pangolin.net/pricing#Self-Hosted-identity-and-access-management">
|
||||
Full feature comparison and plan tiers for self-hosted Pangolin.
|
||||
</Card>
|
||||
|
||||
<Card title="Purchase a license" icon="credit-card" href="/self-host/purchase-license-key">
|
||||
Paid commercial licenses for businesses above the revenue threshold.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Licensing Overview
|
||||
|
||||
Enterprise Edition is distributed under the **Fossorial Commercial License**. Your organization's gross annual revenue determines whether you qualify for a free license or need a paid one.
|
||||
|
||||
### Personal Use
|
||||
|
||||
Free for individuals and small businesses:
|
||||
|
||||
- **Revenue threshold**: Less than $100,000 USD gross annual revenue
|
||||
- **License cost**: Free
|
||||
- **Usage**: Personal and small business use allowed
|
||||
|
||||
You still need to apply for a valid license key to unlock Enterprise features, even with free licensing.
|
||||
|
||||
### Business Use
|
||||
|
||||
Larger businesses require a paid license:
|
||||
|
||||
- **Revenue threshold**: $100,000+ USD gross annual revenue
|
||||
- **License cost**: Paid license required — see [Self-Hosted pricing](https://pangolin.net/pricing#Self-Hosted) for tiers
|
||||
- **Usage**: Business use with commercial terms
|
||||
- **Trial**: Want to evaluate Enterprise Edition before buying? Contact [sales@pangolin.net](mailto:sales@pangolin.net) to request a free limited trial license.
|
||||
|
||||
Businesses exceeding the revenue threshold must purchase a commercial license to use Enterprise Edition.
|
||||
|
||||
## Enterprise Features and Plans
|
||||
|
||||
Enterprise Edition unlocks capabilities beyond Community Edition. Your license tier determines which features and limits apply.
|
||||
|
||||
<Card title="View full feature comparison" icon="table-list" href="https://pangolin.net/pricing#Self-Hosted-identity-and-access-management">
|
||||
The Self-Hosted pricing page is the source of truth for features, limits, and plan tiers.
|
||||
</Card>
|
||||
|
||||
<Tip>
|
||||
For setup instructions on a specific feature, search the docs or browse from the pricing page. Individual doc pages mark Enterprise-only features with notes linking back to this page.
|
||||
</Tip>
|
||||
|
||||
## Hiding Enterprise Features on Community Edition
|
||||
|
||||
On Community Edition, Enterprise-only capabilities may still appear in the dashboard but remain locked without the `ee` Docker image and a valid license key. To hide those UI elements entirely, set `disable_enterprise_features` under `flags` in your [`config.yml`](/self-host/advanced/config-file):
|
||||
|
||||
```yaml
|
||||
flags:
|
||||
disable_enterprise_features: true
|
||||
```
|
||||
|
||||
When enabled, Enterprise-only features are hidden from the UI. Restart the stack after updating the configuration file.
|
||||
|
||||
## Get a Free License (Personal Use)
|
||||
|
||||
<Steps>
|
||||
<Step title="Create an account">
|
||||
Visit [app.pangolin.net](https://app.pangolin.net) and create your account.
|
||||
</Step>
|
||||
|
||||
<Step title="Create an organization">
|
||||
After signing up you will be prompted to create an organization. This is required to apply for a license key.
|
||||
</Step>
|
||||
|
||||
<Step title="Complete the license application">
|
||||
Go to the **Licenses** section in your account dashboard and complete the license application form.
|
||||
|
||||
<Warning>
|
||||
Inaccurate representation is a violation of the license and will result in the license being revoked.
|
||||
</Warning>
|
||||
</Step>
|
||||
|
||||
<Step title="Receive your key">
|
||||
Once approved, you'll receive your license key immediately. Continue to [Activate Enterprise Edition](#activating-enterprise-edition) to use it on your server.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
Organizations above the revenue threshold should [purchase a commercial license](/self-host/purchase-license-key) instead. See [Self-Hosted pricing](https://pangolin.net/pricing#Self-Hosted) for paid tiers.
|
||||
|
||||
## Purchase a License (Business Use)
|
||||
|
||||
Businesses with $100,000+ USD gross annual revenue need a paid commercial license.
|
||||
|
||||
### Self-serve Purchase
|
||||
|
||||
You can buy a **Starter** or **Scale** license online at any time through [app.pangolin.net](https://app.pangolin.net). Follow the steps in [Purchase a license key](/self-host/purchase-license-key) to choose your tier, complete checkout, and receive your key immediately. Compare features and limits on the [Self-Hosted pricing page](https://pangolin.net/pricing#Self-Hosted-identity-and-access-management), then activate your key — see [Activate Enterprise Edition](#activating-enterprise-edition).
|
||||
|
||||
### Custom Licensing
|
||||
|
||||
Need more users, more sites, or special add-ons — such as compliance packages, SLA support, pay-by-invoice, or bank transfer? Contact [sales@pangolin.net](mailto:sales@pangolin.net) for a custom quote.
|
||||
|
||||
<Info>
|
||||
Not ready to purchase? Businesses can request a **free limited trial** of Enterprise Edition by emailing [sales@pangolin.net](mailto:sales@pangolin.net). Include your organization details and what you'd like to evaluate.
|
||||
</Info>
|
||||
|
||||
## Upgrade from Community Edition
|
||||
|
||||
If you're already running Community Edition and want Enterprise features:
|
||||
|
||||
<Steps>
|
||||
<Step title="Get a license key">
|
||||
Apply for a [free license](#get-a-free-license-personal-use) or [purchase a commercial license](/self-host/purchase-license-key), depending on your organization's revenue.
|
||||
</Step>
|
||||
|
||||
<Step title="Switch to the Enterprise image">
|
||||
Update your Docker Compose configuration:
|
||||
|
||||
```yaml
|
||||
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`).
|
||||
</Warning>
|
||||
</Step>
|
||||
|
||||
<Step title="Restart the stack">
|
||||
```bash
|
||||
sudo docker compose down && sudo docker compose up -d
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Activate your license key">
|
||||
Log in with server admin credentials, open the Server Admin panel, and go to the License section at `/admin/license`. Enter and activate your key.
|
||||
</Step>
|
||||
|
||||
<Step title="Verify activation">
|
||||
Confirm Enterprise Edition features are unlocked in your dashboard.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Activating Enterprise Edition
|
||||
|
||||
Use these steps if you're setting up Enterprise Edition for the first time (including after a fresh install with the `ee` image).
|
||||
|
||||
<Steps>
|
||||
<Step title="Use Enterprise Edition image">
|
||||
Your Docker Compose configuration must use the Enterprise Edition image:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
pangolin:
|
||||
image: fosrl/pangolin:ee-latest # Enterprise Edition
|
||||
# ... rest of configuration
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Restart the stack">
|
||||
```bash
|
||||
sudo docker compose down && sudo docker compose up -d
|
||||
```
|
||||
</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 navigate to the License section (`/admin/license`). Enter and activate the license key.
|
||||
|
||||
<Info>
|
||||
The license key should be provided exactly as received.
|
||||
</Info>
|
||||
</Step>
|
||||
|
||||
<Step title="Verify activation">
|
||||
Check your Pangolin dashboard to confirm Enterprise Edition features are unlocked.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
### Troubleshooting Activation
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Admin panel still shows Community Edition">
|
||||
You're likely running the Community Edition Docker image. Confirm your `docker-compose.yml` uses `fosrl/pangolin:ee-latest` (or a pinned version like `fosrl/pangolin:ee-1.14.1`), not `fosrl/pangolin:latest`. Restart the stack after changing the image:
|
||||
|
||||
```bash
|
||||
sudo docker compose down && sudo docker compose up -d
|
||||
```
|
||||
|
||||
Check container logs if the issue persists:
|
||||
|
||||
```bash
|
||||
sudo docker compose logs pangolin
|
||||
```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="No License section in the admin panel">
|
||||
The License section only appears when the Enterprise Edition image is running. Switch to the `ee` image and restart the stack — see the accordion above.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Enterprise settings are missing after activation">
|
||||
Some features require a valid activated license **and** additional configuration. For example, branding and certain identity provider settings need a [`privateConfig.yml`](/self-host/advanced/private-config-file) file mounted in your container. Verify your license is active and check the docs for the specific feature you're enabling.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## 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. A server is considered to be a single database instance.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I get a trial license?">
|
||||
Yes. Businesses that require a paid commercial license can request a **free limited trial** of Enterprise Edition by contacting [sales@pangolin.net](mailto:sales@pangolin.net). Include your organization details and which features you want to evaluate.
|
||||
|
||||
Trial licenses are intended for organizations above the personal-use revenue threshold that want to test Enterprise Edition before purchasing.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="What if I'm unsure about my license type?">
|
||||
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.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## FAQ
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="What features does Enterprise Edition include?">
|
||||
Enterprise Edition unlocks advanced features beyond Community Edition.
|
||||
|
||||
See the [Self-Hosted pricing page](https://pangolin.net/pricing#Self-Hosted-identity-and-access-management) for the full feature comparison — it is the source of truth for what each plan includes.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="What are Paid Features?">
|
||||
"Paid Features" refers to the advanced capabilities unlocked by Enterprise Edition with a valid license key. Personal and small-business users get a free license. Larger organizations purchase a paid license.
|
||||
|
||||
For the complete list, see [Self-Hosted pricing](https://pangolin.net/pricing#Self-Hosted).
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I use Enterprise Edition for personal projects?">
|
||||
Yes. Individuals and small businesses under the $100,000 USD revenue threshold can use Enterprise Edition for personal projects at no cost. [Apply for a free license](#get-a-free-license-personal-use) to get started.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Why would a business pay for self-hosted Enterprise Edition?">
|
||||
Paid tiers unlock features most organizations need at scale: external identity providers and RBAC, multi-organization support and branding, and custom limits with SIEM streaming and SLA support.
|
||||
|
||||
Compare tiers on the [Self-Hosted pricing page](https://pangolin.net/pricing#Self-Hosted) to find the right fit.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Is self-hosted Enterprise Edition the same as Pangolin Cloud?">
|
||||
No. Self-hosted Enterprise Edition runs on your own infrastructure with a license key on the `ee` Docker image. [Pangolin Cloud](https://app.pangolin.net/auth/signup) is a managed hosting option with its own pricing tab on the [pricing page](https://pangolin.net/pricing#Self-Hosted). Both offer advanced features, but the deployment model is different.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Is Enterprise Edition opt-in?">
|
||||
Yes. You can continue using the Community Edition indefinitely. Enterprise Edition requires switching to the `ee` Docker image and activating a license key.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I switch between editions?">
|
||||
Yes. Switching between Community and Enterprise Edition is a **container swap** — update the Docker image in your `docker-compose.yml` and restart the stack:
|
||||
|
||||
- **To Enterprise:** `fosrl/pangolin:ee-latest` (or a pinned `ee-<version>` tag), then activate your license key at `/admin/license`
|
||||
- **To Community:** `fosrl/pangolin:latest` (or a pinned community tag)
|
||||
|
||||
Community and Enterprise Edition share the same database schema, so there should be no data migration issues. You can freely switch between versions to test. Enterprise-only features are disabled when running the Community image.
|
||||
|
||||
<Tip>
|
||||
Always back up your database and configuration before switching editions, just in case.
|
||||
</Tip>
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I downgrade from Enterprise to Community Edition?">
|
||||
Yes. Downgrading is a simple container swap:
|
||||
|
||||
1. Change your Docker image from `fosrl/pangolin:ee-latest` to `fosrl/pangolin:latest` (or the matching community version tag)
|
||||
2. Restart the stack: `sudo docker compose down && sudo docker compose up -d`
|
||||
|
||||
Community and Enterprise Edition use the **same database schema**, so you should not run into data migration issues. You can freely switch between editions to test. Enterprise-only features will be disabled on the Community image, but your existing data remains intact.
|
||||
|
||||
<Tip>
|
||||
Always make a backup of your database and configuration before switching, just in case.
|
||||
</Tip>
|
||||
</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="Are there special license terms for educational institutions, non-profits, or government organizations?">
|
||||
No. 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.
|
||||
|
||||
<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>
|
||||
|
||||
## 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.
|
||||
@@ -0,0 +1,138 @@
|
||||
---
|
||||
title: "How to Update"
|
||||
description: "Keep your self-hosted Pangolin server up to date with the latest features and security patches"
|
||||
---
|
||||
|
||||
Updating Pangolin is straightforward since it's a collection of Docker images. Simply pull the latest images and restart the stack.
|
||||
|
||||
This page covers updating your self-hosted Pangolin server. To update sites and clients, see the respective guides:
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Update Sites" icon="plug" href="/manage/sites/update-site">
|
||||
Update sites to the latest version.
|
||||
</Card>
|
||||
<Card title="Update Clients" icon="desktop" href="/manage/clients/update-client">
|
||||
Update your installed client to the latest version.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Migration Scripts
|
||||
|
||||
When Pangolin starts and detects a version update, it runs migration scripts automatically to update your database and configuration files. Pangolin stores the last successfully run version in the database, so it knows which scripts still need to run. Scripts run in order, starting from the oldest unrun script through the latest.
|
||||
|
||||
These are commonly SQL schema updates, and sometimes data migrations.
|
||||
|
||||
If a release includes a Badger update, Pangolin also tries to update the Traefik config when it still matches the default Pangolin installer Traefik config. If Pangolin cannot apply that change, it fails silently so you can update Badger yourself.
|
||||
|
||||
A failed database migration blocks startup and prevents the server from running.
|
||||
|
||||
If you are using SQLite, Pangolin automatically creates a copy of the database file before a migration runs so you can roll back if needed. You can disable this by setting the `DISABLE_BACKUP_ON_MIGRATION` environment variable to `true`.
|
||||
|
||||
<Warning>
|
||||
Because migrations can change the database schema, downgrading is sometimes impossible and is not recommended. The database may become incompatible with older versions. Always back up your database before updating.
|
||||
</Warning>
|
||||
|
||||
## 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
|
||||
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://github.com/traefik/traefik/releases)
|
||||
- **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"
|
||||
services:
|
||||
pangolin:
|
||||
image: fosrl/pangolin:1.22.0 # Check GitHub Releases for latest version tag
|
||||
# ... rest of config
|
||||
|
||||
gerbil:
|
||||
image: fosrl/gerbil:1.5.1 # Check GitHub Releases for latest version tag
|
||||
# ... rest of config
|
||||
|
||||
traefik:
|
||||
image: traefik:v3.7.12 # Check GitHub Releases for latest version tag
|
||||
# ... rest of config
|
||||
```
|
||||
|
||||
Increase the Badger version number in `config/traefik/traefik_config.yml`:
|
||||
|
||||
```yaml title="traefik_config.yml"
|
||||
experimental:
|
||||
plugins:
|
||||
badger:
|
||||
moduleName: github.com/fosrl/badger
|
||||
version: v1.7.0 # Check GitHub Releases for latest version tag
|
||||
```
|
||||
|
||||
<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
|
||||
sudo docker compose pull
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Start the stack">
|
||||
Start the updated containers:
|
||||
|
||||
```bash
|
||||
sudo docker compose up -d
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Monitor the update">
|
||||
Watch the logs to ensure everything starts correctly:
|
||||
|
||||
```bash
|
||||
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,428 @@
|
||||
---
|
||||
title: "Docker Compose"
|
||||
description: "Deploy Pangolin manually using Docker Compose without the automated installer"
|
||||
---
|
||||
|
||||
This guide walks through a manual deployment using the same file layout the installer generates from `install/config/*` in the Pangolin source tree. Use it if you want the installer's defaults, but you want to create and maintain the files yourself.
|
||||
|
||||
This guide assumes you already have a Linux server with Docker and Docker Compose installed, plus root or sudo access.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Review the [quick install guide](/self-host/quick-install) and [DNS & networking](/self-host/dns-and-networking) first. At minimum you need:
|
||||
|
||||
- A public Linux server
|
||||
- A base domain such as `example.com`
|
||||
- A dashboard hostname such as `pangolin.example.com`
|
||||
- An email address for Let's Encrypt
|
||||
- TCP ports `80` and `443` open
|
||||
- UDP ports `51820` and `21820` open if you are using tunneling
|
||||
|
||||
<Tip>
|
||||
If you do not want tunneling, see [Without Tunneling](/self-host/advanced/without-tunneling). In that mode you will skip the `gerbil` service and expose Traefik directly.
|
||||
</Tip>
|
||||
|
||||
<Note>
|
||||
`base domain` is the parent domain you will attach resources to, such as `example.com`. `dashboard hostname` is the specific hostname for the Pangolin UI and API, such as `pangolin.example.com`.
|
||||
</Note>
|
||||
|
||||
## File Layout
|
||||
|
||||
Create the following project structure:
|
||||
|
||||
```text
|
||||
.
|
||||
├── docker-compose.yml
|
||||
└── config/
|
||||
├── config.yml
|
||||
├── db/
|
||||
├── letsencrypt/
|
||||
└── traefik/
|
||||
├── dynamic_config.yml
|
||||
├── logs/
|
||||
└── traefik_config.yml
|
||||
```
|
||||
|
||||
The following files are created later by the running services or added only when you enable optional features:
|
||||
|
||||
- `config/db/db.sqlite` is created by Pangolin on first startup.
|
||||
- `config/key` is created by Gerbil when tunneling is enabled.
|
||||
- `config/GeoLite2-Country.mmdb` is optional and only needed for [geo-blocking](/self-host/advanced/enable-geoblocking). It is not downloaded by the running services in a manual install; download it manually before enabling geo-blocking.
|
||||
|
||||
## Create the Directories
|
||||
|
||||
Create the project folders:
|
||||
|
||||
```bash
|
||||
mkdir -p config/db config/letsencrypt config/traefik/logs
|
||||
```
|
||||
|
||||
## Create the Configuration Files
|
||||
|
||||
<Steps>
|
||||
<Step title="Create docker-compose.yml">
|
||||
This file defines the Pangolin, Gerbil, and Traefik containers, their shared volumes, and the ports exposed on the host.
|
||||
|
||||
```yaml title="docker-compose.yml"
|
||||
name: pangolin
|
||||
services:
|
||||
pangolin:
|
||||
image: docker.io/fosrl/pangolin:latest
|
||||
container_name: pangolin
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./config:/app/config
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3001/api/v1/"]
|
||||
interval: "10s"
|
||||
timeout: "10s"
|
||||
retries: 15
|
||||
|
||||
gerbil:
|
||||
image: docker.io/fosrl/gerbil:latest
|
||||
container_name: gerbil
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
pangolin:
|
||||
condition: service_healthy
|
||||
command:
|
||||
- --reachableAt=http://gerbil:3004
|
||||
- --generateAndSaveKeyTo=/var/config/key
|
||||
- --remoteConfig=http://pangolin:3001/api/v1/
|
||||
volumes:
|
||||
- ./config/:/var/config
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
- SYS_MODULE
|
||||
ports:
|
||||
- 51820:51820/udp
|
||||
- 21820:21820/udp
|
||||
- 443:443
|
||||
# - 443:443/udp # Uncomment if you enable HTTP/3 in Traefik.
|
||||
- 80:80
|
||||
|
||||
traefik:
|
||||
image: docker.io/traefik:v3.7
|
||||
container_name: traefik
|
||||
restart: unless-stopped
|
||||
network_mode: service:gerbil
|
||||
depends_on:
|
||||
pangolin:
|
||||
condition: service_healthy
|
||||
command:
|
||||
- --configFile=/etc/traefik/traefik_config.yml
|
||||
volumes:
|
||||
- ./config/traefik:/etc/traefik:ro
|
||||
- ./config/letsencrypt:/letsencrypt
|
||||
- ./config/traefik/logs:/var/log/traefik
|
||||
|
||||
networks:
|
||||
default:
|
||||
driver: bridge
|
||||
name: pangolin
|
||||
# enable_ipv6: true
|
||||
```
|
||||
|
||||
<Note>
|
||||
This is the installer's default community layout with Gerbil enabled. If you want to pin releases instead of using `latest`, replace the image tags with the versions you intend to run.
|
||||
</Note>
|
||||
</Step>
|
||||
|
||||
<Step title="Create config/traefik/traefik_config.yml">
|
||||
This file configures Traefik's providers, Badger plugin, Let's Encrypt resolver, entry points, logs, and health check endpoint.
|
||||
|
||||
```yaml title="config/traefik/traefik_config.yml"
|
||||
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.4.0" # Check github.com/fosrl/badger for the latest release.
|
||||
|
||||
log:
|
||||
level: "INFO"
|
||||
format: "common"
|
||||
maxSize: 100
|
||||
maxBackups: 3
|
||||
maxAge: 3
|
||||
compress: true
|
||||
|
||||
certificatesResolvers:
|
||||
letsencrypt:
|
||||
acme:
|
||||
httpChallenge:
|
||||
entryPoint: web
|
||||
email: "admin@example.com" # REPLACE
|
||||
storage: "/letsencrypt/acme.json"
|
||||
caServer: "https://acme-v02.api.letsencrypt.org/directory"
|
||||
|
||||
entryPoints:
|
||||
web:
|
||||
address: ":80"
|
||||
websecure:
|
||||
address: ":443"
|
||||
transport:
|
||||
respondingTimeouts:
|
||||
readTimeout: "30m"
|
||||
# Uncomment to enable HTTP/3. You must also expose 443/udp in docker-compose.yml.
|
||||
# http3:
|
||||
# advertisedPort: 443
|
||||
http:
|
||||
tls:
|
||||
certResolver: "letsencrypt"
|
||||
encodedCharacters:
|
||||
allowEncodedSlash: true
|
||||
allowEncodedQuestionMark: true
|
||||
|
||||
serversTransport:
|
||||
insecureSkipVerify: true
|
||||
|
||||
ping:
|
||||
entryPoint: "web"
|
||||
```
|
||||
|
||||
<Note>
|
||||
Traefik stores Let's Encrypt certificates at `/letsencrypt/acme.json` inside the container. The Compose file mounts that path from `./config/letsencrypt`, so Traefik will create `config/letsencrypt/acme.json` when it needs certificate storage.
|
||||
</Note>
|
||||
</Step>
|
||||
|
||||
<Step title="Create config/traefik/dynamic_config.yml">
|
||||
This file defines the routers, middleware, and services that send dashboard, API, and WebSocket traffic to Pangolin.
|
||||
|
||||
```yaml title="config/traefik/dynamic_config.yml"
|
||||
http:
|
||||
middlewares:
|
||||
badger:
|
||||
plugin:
|
||||
badger:
|
||||
disableForwardAuth: true
|
||||
redirect-to-https:
|
||||
redirectScheme:
|
||||
scheme: https
|
||||
|
||||
routers:
|
||||
main-app-router-redirect:
|
||||
rule: "Host(`pangolin.example.com`)" # REPLACE
|
||||
service: next-service
|
||||
entryPoints:
|
||||
- web
|
||||
middlewares:
|
||||
- redirect-to-https
|
||||
- badger
|
||||
|
||||
next-router:
|
||||
rule: "Host(`pangolin.example.com`) && !PathPrefix(`/api/v1`)" # REPLACE
|
||||
service: next-service
|
||||
entryPoints:
|
||||
- websecure
|
||||
middlewares:
|
||||
- badger
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
|
||||
api-router:
|
||||
rule: "Host(`pangolin.example.com`) && PathPrefix(`/api/v1`)" # REPLACE
|
||||
service: api-service
|
||||
entryPoints:
|
||||
- websecure
|
||||
middlewares:
|
||||
- badger
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
|
||||
ws-router:
|
||||
rule: "Host(`pangolin.example.com`)" # REPLACE
|
||||
service: api-service
|
||||
entryPoints:
|
||||
- websecure
|
||||
middlewares:
|
||||
- badger
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
|
||||
services:
|
||||
next-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://pangolin:3002"
|
||||
|
||||
api-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://pangolin:3000"
|
||||
|
||||
tcp:
|
||||
serversTransports:
|
||||
pp-transport-v1:
|
||||
proxyProtocol:
|
||||
version: 1
|
||||
pp-transport-v2:
|
||||
proxyProtocol:
|
||||
version: 2
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Create config/config.yml">
|
||||
This file contains Pangolin's application settings, dashboard domain, base domain, CORS origin, and server secret.
|
||||
|
||||
```yaml title="config/config.yml"
|
||||
gerbil:
|
||||
start_port: 51820
|
||||
base_endpoint: "pangolin.example.com" # REPLACE WITH YOUR DASHBOARD DOMAIN
|
||||
|
||||
app:
|
||||
dashboard_url: "https://pangolin.example.com" # REPLACE WITH YOUR DASHBOARD DOMAIN
|
||||
log_level: "info"
|
||||
telemetry:
|
||||
anonymous_usage: true
|
||||
|
||||
domains:
|
||||
domain1:
|
||||
base_domain: "example.com" # REPLACE WITH YOUR BASE DOMAIN
|
||||
|
||||
server:
|
||||
secret: "replace-with-a-long-random-secret" # REPLACE WITH SECURE SECRET
|
||||
cors:
|
||||
origins: ["https://pangolin.example.com"] # REPLACE WITH YOUR DASHBOARD 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
|
||||
```
|
||||
|
||||
Replace these values before starting the stack:
|
||||
|
||||
- `pangolin.example.com` with your dashboard hostname
|
||||
- `example.com` with your base domain
|
||||
- `replace-with-a-long-random-secret` with a strong random secret
|
||||
- `admin@example.com` in `traefik_config.yml` with your Let's Encrypt email
|
||||
|
||||
Generate a secret with:
|
||||
|
||||
```bash
|
||||
openssl rand -hex 32
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Do not reuse a weak or short `server.secret`. If you need to rotate it later, use `pangctl rotate-server-secret`. See the [container CLI tool guide](/self-host/advanced/container-cli-tool#rotate-server-secret).
|
||||
</Warning>
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
### Optional Email Configuration
|
||||
|
||||
If you want Pangolin to send email, add this block to `config/config.yml` and set `flags.require_email_verification` to `true`:
|
||||
|
||||
```yaml title="config/config.yml"
|
||||
email:
|
||||
smtp_host: "smtp.example.com"
|
||||
smtp_port: 587
|
||||
smtp_user: "smtp-user"
|
||||
smtp_pass: "smtp-password"
|
||||
no_reply: "noreply@example.com"
|
||||
```
|
||||
|
||||
### Optional Geo-blocking Configuration
|
||||
|
||||
If you want geo-blocking, download the MaxMind database and add this line under `server`:
|
||||
|
||||
```yaml title="config/config.yml"
|
||||
server:
|
||||
maxmind_db_path: "./config/GeoLite2-Country.mmdb"
|
||||
```
|
||||
|
||||
See [Enable Geo-blocking](/self-host/advanced/enable-geoblocking) for the full process.
|
||||
|
||||
## Start the Stack
|
||||
|
||||
<Steps>
|
||||
<Step title="Start the services">
|
||||
```bash
|
||||
sudo docker compose up -d
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Watch the logs">
|
||||
```bash
|
||||
sudo docker compose logs -f pangolin traefik gerbil
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Verify the containers are healthy">
|
||||
```bash
|
||||
sudo docker compose ps
|
||||
```
|
||||
|
||||
`pangolin`, `traefik`, and `gerbil` should all report as running after the first startup finishes.
|
||||
</Step>
|
||||
|
||||
<Step title="Get the setup token from the Pangolin logs">
|
||||
Check the Pangolin container logs:
|
||||
|
||||
```bash
|
||||
sudo docker compose logs pangolin
|
||||
```
|
||||
|
||||
Pangolin prints a setup token to stdout on first boot. Copy that token before continuing.
|
||||
</Step>
|
||||
|
||||
<Step title="Open the initial setup page">
|
||||
Visit:
|
||||
|
||||
```text
|
||||
https://pangolin.example.com/auth/initial-setup
|
||||
```
|
||||
|
||||
Replace the hostname with your real dashboard domain, then use the setup token from the Pangolin logs to register the first admin account.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Verify the Setup
|
||||
|
||||
You should expect the following on a healthy first install:
|
||||
|
||||
- `docker compose ps` shows `pangolin`, `traefik`, and `gerbil` as running.
|
||||
- `docker compose logs pangolin` includes the one-time setup token for the first admin account.
|
||||
- Visiting `https://<your-dashboard-domain>/auth/initial-setup` loads the setup page.
|
||||
- `config/db/db.sqlite` exists after Pangolin starts.
|
||||
- `config/key` exists after Gerbil starts.
|
||||
|
||||
<Tip>
|
||||
The first Let's Encrypt certificate request can take a short while. If the page initially shows a certificate warning, wait a minute and refresh.
|
||||
</Tip>
|
||||
|
||||
## If Something Fails
|
||||
|
||||
- If the setup page does not load, confirm your DNS record points to the server and ports `80` and `443` are reachable.
|
||||
- If you cannot complete first-time signup, check `sudo docker compose logs pangolin` and copy the setup token printed by Pangolin.
|
||||
- If certificates are not issued, confirm `admin@example.com` was replaced and that nothing else is already bound to ports `80` or `443`.
|
||||
- If `pangolin` never becomes healthy, inspect `sudo docker compose logs -f pangolin`.
|
||||
- If tunneling does not work, inspect `sudo docker compose logs -f gerbil` and confirm UDP ports `51820` and `21820` are open.
|
||||
- If Traefik serves the wrong host, re-check every `pangolin.example.com` replacement in both Traefik files and `config/config.yml`.
|
||||
|
||||
## Without Tunneling
|
||||
|
||||
If you do not want Gerbil:
|
||||
|
||||
- Remove the `gerbil` service.
|
||||
- Remove `network_mode: service:gerbil` from `traefik`.
|
||||
- Add ports `80:80` and `443:443` directly to `traefik`.
|
||||
- Remove the `gerbil` block from `config/config.yml`.
|
||||
|
||||
That mode is covered in more detail in [Without Tunneling](/self-host/advanced/without-tunneling).
|
||||
@@ -0,0 +1,49 @@
|
||||
---
|
||||
title: "Choose an Installation Path"
|
||||
description: "Choose the Kubernetes deployment workflow for Pangolin and Sites (Newt)."
|
||||
---
|
||||
|
||||
Use this page to pick the right Kubernetes guide for your deployment workflow.
|
||||
|
||||
These guides assume you are already familiar with Kubernetes and the deployment tools listed below.
|
||||
|
||||
If you are new to Kubernetes, start with the [official Kubernetes learning resources](https://kubernetes.io/docs/tutorials/kubernetes-basics/) first. Then review the [Prerequisites](/self-host/manual/kubernetes/prerequisites) guide to check your cluster, tools, and setup.
|
||||
|
||||
## Installation paths
|
||||
|
||||
| Path | Use when | Start here |
|
||||
| --- | --- | --- |
|
||||
| Helm | You want the standard chart-based installation path for Pangolin or Sites (Newt). | [Helm Quick-Start](/self-host/manual/kubernetes/helm) |
|
||||
| Kustomize | You want manifest overlays, for example for environment-specific configuration, patches, or rendered manifests that can be reviewed before applying. | [Kustomize Quick-Start](/self-host/manual/kubernetes/kustomize) |
|
||||
| Argo CD | You already use Argo CD and want to deploy Pangolin or Sites (Newt) through a Kubernetes-native GitOps workflow. | [Argo CD Guide](/self-host/manual/kubernetes/gitops/argocd) |
|
||||
| Flux | You already use Flux and want to manage Pangolin or Sites (Newt) through `HelmRelease` or `Kustomization` resources. | [Flux Guide](/self-host/manual/kubernetes/gitops/flux) |
|
||||
| Helmfile | You want to manage multiple related Helm releases as one stack. | [Helmfile Guide](/self-host/manual/kubernetes/helmfile) |
|
||||
|
||||
## Recommended starting point
|
||||
|
||||
For most Kubernetes deployments, start with Helm. Use the GitOps guides only if Argo CD or Flux is already part of your deployment workflow.
|
||||
|
||||
Kustomize and Helmfile are useful when you need more control over manifests, overlays, or multiple coordinated releases.
|
||||
|
||||
## Next steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Prerequisites" href="/self-host/manual/kubernetes/prerequisites" icon="list-check">
|
||||
Review the required cluster, ingress, DNS, storage, and secret setup.
|
||||
</Card>
|
||||
<Card title="Helm Quick-Start" href="/self-host/manual/kubernetes/helm" icon="box">
|
||||
Install Pangolin or Sites (Newt) with the standard chart-based workflow.
|
||||
</Card>
|
||||
<Card title="Kustomize Quick-Start" href="/self-host/manual/kubernetes/kustomize" icon="layer-group">
|
||||
Use overlays and patches for manifest-based deployments.
|
||||
</Card>
|
||||
<Card title="Argo CD Guide" href="/self-host/manual/kubernetes/gitops/argocd" icon="code-branch">
|
||||
Deploy Pangolin or Sites (Newt) with Argo CD.
|
||||
</Card>
|
||||
<Card title="Flux Guide" href="/self-host/manual/kubernetes/gitops/flux" icon="code-branch">
|
||||
Deploy Pangolin or Sites (Newt) with Flux.
|
||||
</Card>
|
||||
<Card title="Helmfile Guide" href="/self-host/manual/kubernetes/helmfile" icon="boxes-stacked">
|
||||
Manage multiple Helm releases together.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,419 @@
|
||||
---
|
||||
title: "Argo CD"
|
||||
description: "Deploy Pangolin and Newt using Argo CD for Git-driven GitOps reconciliation."
|
||||
---
|
||||
|
||||
Argo CD is a declarative GitOps tool that continuously syncs your cluster state to your Git repository. This guide covers installing Pangolin and Newt using Argo CD.
|
||||
|
||||
## Install Pangolin with Argo CD using Helm
|
||||
|
||||
### Step 1: Create Pangolin namespace
|
||||
|
||||
```bash
|
||||
kubectl create namespace pangolin
|
||||
```
|
||||
|
||||
### Step 2: Create Application
|
||||
|
||||
Create an Argo CD Application resource that tells Argo CD to deploy Pangolin using the Helm chart:
|
||||
|
||||
```yaml
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: pangolin
|
||||
namespace: argocd
|
||||
spec:
|
||||
project: default
|
||||
|
||||
source:
|
||||
repoURL: https://charts.fossorial.io
|
||||
chart: pangolin
|
||||
targetRevision: 0.1.0-alpha.0 # or use ~0.1.0 for range
|
||||
helm:
|
||||
values: |
|
||||
deployment:
|
||||
type: controller
|
||||
mode: multi
|
||||
|
||||
database:
|
||||
mode: cloudnativepg
|
||||
|
||||
pangolin:
|
||||
config:
|
||||
app:
|
||||
dashboard_url: https://pangolin.example.com
|
||||
domains:
|
||||
domain1:
|
||||
base_domain: example.com
|
||||
gerbil:
|
||||
base_endpoint: vpn.example.com
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
className: traefik
|
||||
hosts:
|
||||
- host: pangolin.example.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
tls:
|
||||
- secretName: pangolin-tls
|
||||
hosts:
|
||||
- pangolin.example.com
|
||||
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: pangolin
|
||||
|
||||
syncPolicy:
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
```
|
||||
|
||||
Apply the Application:
|
||||
|
||||
```bash
|
||||
kubectl apply -f pangolin-app.yaml
|
||||
```
|
||||
|
||||
### Step 3: Monitor in Argo CD
|
||||
|
||||
In the Argo CD UI, you should see the `pangolin` application. Argo CD will:
|
||||
|
||||
1. Fetch the Helm chart from `https://charts.fossorial.io`
|
||||
2. Render the chart with your inline `values`
|
||||
3. Create all resources in the `pangolin` namespace
|
||||
4. Continuously monitor for drift
|
||||
|
||||
### Step 4: Verify deployment
|
||||
|
||||
```bash
|
||||
# Check Argo CD status
|
||||
kubectl describe app -n argocd pangolin
|
||||
|
||||
# Check pod status
|
||||
kubectl get pods -n pangolin
|
||||
```
|
||||
|
||||
## Install Newt with Argo CD using Helm
|
||||
|
||||
### Step 1: Create Newt auth secret
|
||||
|
||||
```bash
|
||||
kubectl create secret generic newt-auth \
|
||||
-n pangolin \
|
||||
--from-literal=PANGOLIN_ENDPOINT=https://pangolin.example.com \
|
||||
--from-literal=NEWT_ID=<your-newt-id> \
|
||||
--from-literal=NEWT_SECRET=<your-newt-secret>
|
||||
```
|
||||
|
||||
### Step 2: Create Newt Application
|
||||
|
||||
```yaml
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: newt
|
||||
namespace: argocd
|
||||
spec:
|
||||
project: default
|
||||
|
||||
source:
|
||||
repoURL: https://charts.fossorial.io
|
||||
chart: newt
|
||||
targetRevision: 1.4.0
|
||||
helm:
|
||||
values: |
|
||||
newtInstances:
|
||||
- name: main-tunnel
|
||||
enabled: true
|
||||
auth:
|
||||
existingSecretName: newt-auth
|
||||
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: pangolin
|
||||
|
||||
syncPolicy:
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
```
|
||||
|
||||
Apply:
|
||||
|
||||
```bash
|
||||
kubectl apply -f newt-app.yaml
|
||||
```
|
||||
|
||||
## Using Argo CD with Git repository
|
||||
|
||||
Instead of inline values, you can store configuration in Git and have Argo CD deploy from there:
|
||||
|
||||
### Repository structure
|
||||
|
||||
```
|
||||
infrastructure/
|
||||
├── apps/
|
||||
│ ├── pangolin/
|
||||
│ │ ├── values-base.yaml
|
||||
│ │ ├── values-prod.yaml
|
||||
│ │ └── app.yaml (Argo CD Application CRD)
|
||||
│ └── newt/
|
||||
│ ├── values.yaml
|
||||
│ └── app.yaml
|
||||
└── clusters/
|
||||
└── production/
|
||||
├── pangolin.yaml (reference to app)
|
||||
└── newt.yaml
|
||||
```
|
||||
|
||||
### Git-based Application
|
||||
|
||||
```yaml
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: pangolin
|
||||
namespace: argocd
|
||||
spec:
|
||||
project: default
|
||||
|
||||
source:
|
||||
repoURL: https://github.com/my-org/infrastructure
|
||||
path: apps/pangolin
|
||||
targetRevision: main
|
||||
helm:
|
||||
valuesObject:
|
||||
deployment:
|
||||
type: controller
|
||||
mode: multi
|
||||
releaseName: pangolin
|
||||
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: pangolin
|
||||
|
||||
syncPolicy:
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
```
|
||||
|
||||
Argo CD will watch the Git repository and auto-sync on changes to `apps/pangolin`.
|
||||
|
||||
## Using Argo CD with Kustomize
|
||||
|
||||
Deploy Pangolin using Kustomize overlays:
|
||||
|
||||
```yaml
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: pangolin
|
||||
namespace: argocd
|
||||
spec:
|
||||
project: default
|
||||
|
||||
source:
|
||||
repoURL: https://github.com/my-org/infrastructure
|
||||
path: overlays/production
|
||||
targetRevision: main
|
||||
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: pangolin
|
||||
|
||||
syncPolicy:
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
```
|
||||
|
||||
## Sync policies
|
||||
|
||||
### Automated sync
|
||||
|
||||
**prune: true**: Deletes resources in cluster that are no longer in Git
|
||||
|
||||
**selfHeal: true**: Resyncs if cluster drifts from Git (e.g., manual `kubectl apply`)
|
||||
|
||||
```yaml
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
allowEmpty: false # prevent accidental deletion of all resources
|
||||
```
|
||||
|
||||
### Manual sync
|
||||
|
||||
Sync only when you explicitly trigger it:
|
||||
|
||||
```yaml
|
||||
syncPolicy:
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
```
|
||||
|
||||
Manually sync:
|
||||
|
||||
```bash
|
||||
argocd app sync pangolin
|
||||
# or use UI
|
||||
```
|
||||
|
||||
## Advanced: ApplicationSet for multi-environment
|
||||
|
||||
Deploy Pangolin and Newt across multiple clusters or environments:
|
||||
|
||||
```yaml
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: ApplicationSet
|
||||
metadata:
|
||||
name: pangolin-multienv
|
||||
namespace: argocd
|
||||
spec:
|
||||
generators:
|
||||
- list:
|
||||
elements:
|
||||
- cluster: production
|
||||
env: prod
|
||||
- cluster: staging
|
||||
env: staging
|
||||
template:
|
||||
metadata:
|
||||
name: pangolin-{{ .cluster }}
|
||||
spec:
|
||||
project: default
|
||||
source:
|
||||
repoURL: https://github.com/my-org/infrastructure
|
||||
path: clusters/{{ .cluster }}/pangolin
|
||||
targetRevision: main
|
||||
destination:
|
||||
name: '{{ .cluster }}'
|
||||
namespace: pangolin
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
```
|
||||
|
||||
## OCI Helm sources (if available)
|
||||
|
||||
If the Helm chart is available in an OCI registry:
|
||||
|
||||
```yaml
|
||||
source:
|
||||
repoURL: oci://registry.example.com/fossorial
|
||||
chart: pangolin
|
||||
targetRevision: 0.1.0-alpha.0
|
||||
helm:
|
||||
values: |
|
||||
# ... values ...
|
||||
```
|
||||
|
||||
OCI chart references work the same as traditional Helm repository references in Argo CD.
|
||||
|
||||
## Troubleshooting Argo CD deployments
|
||||
|
||||
### Check Application status
|
||||
|
||||
```bash
|
||||
kubectl describe app -n argocd pangolin
|
||||
kubectl get app -n argocd pangolin -o yaml
|
||||
```
|
||||
|
||||
### Check sync status
|
||||
|
||||
```bash
|
||||
argocd app get pangolin
|
||||
argocd app logs pangolin
|
||||
```
|
||||
|
||||
### Manual sync
|
||||
|
||||
```bash
|
||||
argocd app sync pangolin --force
|
||||
```
|
||||
|
||||
### Refresh from repository
|
||||
|
||||
```bash
|
||||
argocd app diff pangolin
|
||||
```
|
||||
|
||||
### Delete Application
|
||||
|
||||
```bash
|
||||
kubectl delete app -n argocd pangolin
|
||||
```
|
||||
|
||||
## Common patterns
|
||||
|
||||
### Different values per environment
|
||||
|
||||
Use multiple Applications:
|
||||
|
||||
```yaml
|
||||
# production/pangolin-app.yaml
|
||||
spec:
|
||||
source:
|
||||
helm:
|
||||
values: |
|
||||
resources:
|
||||
limits:
|
||||
cpu: 2000m
|
||||
memory: 2Gi
|
||||
replicas: 3
|
||||
|
||||
# staging/pangolin-app.yaml
|
||||
spec:
|
||||
source:
|
||||
helm:
|
||||
values: |
|
||||
resources:
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
replicas: 1
|
||||
```
|
||||
|
||||
### Secrets with sealed-secrets
|
||||
|
||||
Use sealed-secrets to safely store secrets in Git:
|
||||
|
||||
```yaml
|
||||
# In Git
|
||||
apiVersion: bitnami.com/v1alpha1
|
||||
kind: SealedSecret
|
||||
metadata:
|
||||
name: newt-auth
|
||||
namespace: pangolin
|
||||
spec:
|
||||
encryptedData:
|
||||
PANGOLIN_ENDPOINT: AgC4F5qd...
|
||||
NEWT_ID: AgB9l2pK...
|
||||
NEWT_SECRET: AgDq3jX...
|
||||
```
|
||||
|
||||
Argo CD applies the sealed secret; the cluster decrypts it.
|
||||
|
||||
## Next steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="GitOps Overview" href="/self-host/manual/kubernetes/gitops/overview" icon="code-branch" />
|
||||
<Card title="Flux" href="/self-host/manual/kubernetes/gitops/flux" icon="code-branch" />
|
||||
<Card title="Pangolin Configuration" href="/self-host/manual/kubernetes/pangolin/configuration" icon="sliders" />
|
||||
<Card title="Troubleshooting" href="/self-host/manual/kubernetes/pangolin/troubleshooting" icon="circle-question" />
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,512 @@
|
||||
---
|
||||
title: "Flux"
|
||||
description: "Deploy Pangolin and Newt using Flux for Git-driven GitOps reconciliation."
|
||||
---
|
||||
|
||||
Flux is a declarative GitOps tool that uses Kubernetes-native Custom Resources to manage deployments. This guide covers installing Pangolin and Newt using Flux.
|
||||
|
||||
|
||||
## Flux prerequisites
|
||||
|
||||
- Kubernetes 1.25+
|
||||
- `flux` CLI installed: [Flux install guide](https://fluxcd.io/flux/installation/)
|
||||
- Git repository for configuration (optional, can use built-in sources)
|
||||
- GitHub, GitLab, or other Git provider account (optional)
|
||||
|
||||
Install Flux CLI:
|
||||
|
||||
```bash
|
||||
# macOS/Linux with brew
|
||||
brew install flux
|
||||
|
||||
# or curl
|
||||
curl -s https://fluxcd.io/install.sh | sudo bash
|
||||
|
||||
# Verify
|
||||
flux --version
|
||||
```
|
||||
|
||||
## Install Flux on your cluster
|
||||
|
||||
### Option 1: Bootstrap Flux from GitHub
|
||||
|
||||
Flux `bootstrap` automatically installs Flux and configures Git sync:
|
||||
|
||||
```bash
|
||||
flux bootstrap github \
|
||||
--owner=my-org \
|
||||
--repo=infrastructure \
|
||||
--personal \
|
||||
--path=clusters/production
|
||||
```
|
||||
|
||||
This creates the Git repository structure and installs Flux components.
|
||||
|
||||
### Option 2: Manual Flux installation
|
||||
|
||||
```bash
|
||||
# Create flux-system namespace and install Flux
|
||||
flux install --namespace=flux-system --network-policy=true
|
||||
```
|
||||
|
||||
## Install Pangolin with Flux using HelmRelease
|
||||
|
||||
### Step 1: Create HelmRepository
|
||||
|
||||
Define the Fossorial Helm chart repository:
|
||||
|
||||
```yaml
|
||||
apiVersion: source.toolkit.fluxcd.io/v1beta2
|
||||
kind: HelmRepository
|
||||
metadata:
|
||||
name: fossorial
|
||||
namespace: flux-system
|
||||
spec:
|
||||
interval: 5m
|
||||
url: https://charts.fossorial.io
|
||||
```
|
||||
|
||||
Apply:
|
||||
|
||||
```bash
|
||||
kubectl apply -f helmrepo.yaml
|
||||
|
||||
# Verify
|
||||
kubectl get helmrepo -n flux-system
|
||||
```
|
||||
|
||||
### Step 2: Create Pangolin HelmRelease
|
||||
|
||||
```yaml
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: pangolin
|
||||
namespace: pangolin
|
||||
spec:
|
||||
interval: 10m
|
||||
chart:
|
||||
spec:
|
||||
chart: pangolin
|
||||
version: 0.1.0-alpha.0 # or use ~0.1.0 for auto-upgrades
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: fossorial
|
||||
namespace: flux-system
|
||||
|
||||
install:
|
||||
crds: Create
|
||||
upgrade:
|
||||
crds: CreateReplace
|
||||
|
||||
values:
|
||||
deployment:
|
||||
type: controller
|
||||
mode: multi
|
||||
|
||||
database:
|
||||
mode: cloudnativepg
|
||||
|
||||
pangolin:
|
||||
config:
|
||||
app:
|
||||
dashboard_url: https://pangolin.example.com
|
||||
domains:
|
||||
domain1:
|
||||
base_domain: example.com
|
||||
gerbil:
|
||||
base_endpoint: vpn.example.com
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
className: traefik
|
||||
hosts:
|
||||
- host: pangolin.example.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
tls:
|
||||
- secretName: pangolin-tls
|
||||
hosts:
|
||||
- pangolin.example.com
|
||||
```
|
||||
|
||||
Create namespace:
|
||||
|
||||
```bash
|
||||
kubectl create namespace pangolin
|
||||
```
|
||||
|
||||
Apply:
|
||||
|
||||
```bash
|
||||
kubectl apply -f pangolin-helmrelease.yaml
|
||||
```
|
||||
|
||||
### Step 3: Monitor reconciliation
|
||||
|
||||
```bash
|
||||
# Check HelmRelease status
|
||||
kubectl get helmrelease -n pangolin
|
||||
|
||||
# Watch live
|
||||
kubectl get helmrelease -n pangolin -w
|
||||
|
||||
# Describe for details
|
||||
kubectl describe helmrelease pangolin -n pangolin
|
||||
|
||||
# Check Flux logs
|
||||
flux logs --all-namespaces --follow
|
||||
```
|
||||
|
||||
## Install Newt with Flux using HelmRelease
|
||||
|
||||
### Step 1: Create Newt auth secret
|
||||
|
||||
```bash
|
||||
kubectl create secret generic newt-auth \
|
||||
-n pangolin \
|
||||
--from-literal=PANGOLIN_ENDPOINT=https://pangolin.example.com \
|
||||
--from-literal=NEWT_ID=<your-newt-id> \
|
||||
--from-literal=NEWT_SECRET=<your-newt-secret>
|
||||
```
|
||||
|
||||
### Step 2: Create Newt HelmRelease
|
||||
|
||||
```yaml
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: newt
|
||||
namespace: pangolin
|
||||
spec:
|
||||
interval: 10m
|
||||
chart:
|
||||
spec:
|
||||
chart: newt
|
||||
version: 1.4.0
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: fossorial
|
||||
namespace: flux-system
|
||||
|
||||
values:
|
||||
newtInstances:
|
||||
- name: main-tunnel
|
||||
enabled: true
|
||||
auth:
|
||||
existingSecretName: newt-auth
|
||||
```
|
||||
|
||||
Apply:
|
||||
|
||||
```bash
|
||||
kubectl apply -f newt-helmrelease.yaml
|
||||
```
|
||||
|
||||
### Step 3: Verify
|
||||
|
||||
```bash
|
||||
kubectl get helmrelease -n pangolin
|
||||
kubectl describe helmrelease newt -n pangolin
|
||||
```
|
||||
|
||||
## Using Flux with Git repository (GitOps)
|
||||
|
||||
Store Flux configuration in Git and have Flux automatically reconcile changes:
|
||||
|
||||
### Repository structure
|
||||
|
||||
```
|
||||
infrastructure/
|
||||
├── clusters/
|
||||
│ └── production/
|
||||
│ ├── flux-system/
|
||||
│ │ └── gotk-components.yaml (auto-generated)
|
||||
│ ├── pangolin/
|
||||
│ │ ├── helmrepo.yaml
|
||||
│ │ ├── pangolin-helmrelease.yaml
|
||||
│ │ └── newt-helmrelease.yaml
|
||||
│ └── kustomization.yaml
|
||||
└── apps/
|
||||
├── pangolin/
|
||||
│ └── values.yaml
|
||||
└── newt/
|
||||
└── values.yaml
|
||||
```
|
||||
|
||||
### GitRepository for configuration
|
||||
|
||||
```yaml
|
||||
apiVersion: source.toolkit.fluxcd.io/v1beta2
|
||||
kind: GitRepository
|
||||
metadata:
|
||||
name: infrastructure
|
||||
namespace: flux-system
|
||||
spec:
|
||||
interval: 1m
|
||||
url: https://github.com/my-org/infrastructure
|
||||
ref:
|
||||
branch: main
|
||||
```
|
||||
|
||||
### Kustomization for syncing
|
||||
|
||||
```yaml
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
name: production
|
||||
namespace: flux-system
|
||||
spec:
|
||||
interval: 10m
|
||||
sourceRef:
|
||||
kind: GitRepository
|
||||
name: infrastructure
|
||||
path: ./clusters/production
|
||||
prune: true
|
||||
wait: true
|
||||
```
|
||||
|
||||
Flux watches `clusters/production` in Git and auto-applies all resources.
|
||||
|
||||
## Using Flux with Kustomize overlays
|
||||
|
||||
Manage environment-specific overlays with Flux:
|
||||
|
||||
### Repository structure
|
||||
|
||||
```
|
||||
overlays/
|
||||
├── dev/
|
||||
│ ├── kustomization.yaml
|
||||
│ └── pangolin-patch.yaml
|
||||
├── staging/
|
||||
│ └── kustomization.yaml
|
||||
└── prod/
|
||||
├── kustomization.yaml
|
||||
└── pangolin-patch.yaml
|
||||
```
|
||||
|
||||
### Kustomization resource
|
||||
|
||||
```yaml
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
name: pangolin-prod
|
||||
namespace: flux-system
|
||||
spec:
|
||||
interval: 10m
|
||||
sourceRef:
|
||||
kind: GitRepository
|
||||
name: infrastructure
|
||||
path: ./overlays/prod
|
||||
prune: true
|
||||
wait: true
|
||||
```
|
||||
|
||||
Flux builds and applies the Kustomize overlay automatically.
|
||||
|
||||
## Using Flux with OCI Helm charts
|
||||
|
||||
If Helm charts are available in an OCI registry:
|
||||
|
||||
```yaml
|
||||
apiVersion: source.toolkit.fluxcd.io/v1beta2
|
||||
kind: OCIRepository
|
||||
metadata:
|
||||
name: fossorial-oci
|
||||
namespace: flux-system
|
||||
spec:
|
||||
interval: 5m
|
||||
url: oci://registry.example.com/fossorial
|
||||
|
||||
---
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: pangolin
|
||||
namespace: pangolin
|
||||
spec:
|
||||
interval: 10m
|
||||
chart:
|
||||
spec:
|
||||
chart: pangolin
|
||||
version: 0.1.0-alpha.0
|
||||
sourceRef:
|
||||
kind: OCIRepository
|
||||
name: fossorial-oci
|
||||
namespace: flux-system
|
||||
values:
|
||||
# ... values ...
|
||||
```
|
||||
|
||||
## Advanced: Dependency ordering
|
||||
|
||||
Order HelmReleases to install dependencies first:
|
||||
|
||||
```yaml
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: cert-manager
|
||||
namespace: cert-manager
|
||||
spec:
|
||||
interval: 10m
|
||||
chart:
|
||||
spec:
|
||||
chart: cert-manager
|
||||
# ...
|
||||
|
||||
---
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: pangolin
|
||||
namespace: pangolin
|
||||
spec:
|
||||
interval: 10m
|
||||
dependsOn:
|
||||
- name: cert-manager
|
||||
namespace: cert-manager
|
||||
chart:
|
||||
spec:
|
||||
chart: pangolin
|
||||
# ...
|
||||
```
|
||||
|
||||
Flux ensures `cert-manager` reconciles before `pangolin`.
|
||||
|
||||
## Advanced: valuesFrom ConfigMap/Secret
|
||||
|
||||
Store values in ConfigMaps or Secrets, referenced from HelmRelease:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: pangolin-values
|
||||
namespace: pangolin
|
||||
data:
|
||||
values.yaml: |
|
||||
deployment:
|
||||
type: controller
|
||||
mode: multi
|
||||
|
||||
---
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: pangolin
|
||||
namespace: pangolin
|
||||
spec:
|
||||
interval: 10m
|
||||
chart:
|
||||
spec:
|
||||
chart: pangolin
|
||||
# ...
|
||||
valuesFrom:
|
||||
- kind: ConfigMap
|
||||
name: pangolin-values
|
||||
```
|
||||
|
||||
Flux extracts values from the ConfigMap and applies them to the HelmRelease.
|
||||
|
||||
## Troubleshooting Flux
|
||||
|
||||
### Check Flux components
|
||||
|
||||
```bash
|
||||
kubectl get deployments -n flux-system
|
||||
flux check --all-namespaces
|
||||
```
|
||||
|
||||
### Check HelmRelease status
|
||||
|
||||
```bash
|
||||
kubectl get helmrelease -n pangolin
|
||||
kubectl describe helmrelease pangolin -n pangolin
|
||||
kubectl get helmrelease pangolin -n pangolin -o yaml
|
||||
```
|
||||
|
||||
### View reconciliation logs
|
||||
|
||||
```bash
|
||||
flux logs --all-namespaces --follow
|
||||
|
||||
# Specific resource
|
||||
kubectl logs -n pangolin deployment/helm-operator -f
|
||||
```
|
||||
|
||||
### Manual reconciliation
|
||||
|
||||
```bash
|
||||
flux reconcile helmrelease pangolin -n pangolin
|
||||
flux reconcile kustomization production -n flux-system
|
||||
```
|
||||
|
||||
### Suspend reconciliation
|
||||
|
||||
```bash
|
||||
flux suspend helmrelease pangolin -n pangolin
|
||||
```
|
||||
|
||||
### Resume reconciliation
|
||||
|
||||
```bash
|
||||
flux resume helmrelease pangolin -n pangolin
|
||||
```
|
||||
|
||||
## Multi-environment example
|
||||
|
||||
### Bootstrap multiple clusters
|
||||
|
||||
```bash
|
||||
# Production cluster
|
||||
flux bootstrap github \
|
||||
--owner=my-org \
|
||||
--repo=infrastructure \
|
||||
--personal \
|
||||
--path=clusters/production
|
||||
|
||||
# Staging cluster (from different checkout)
|
||||
flux bootstrap github \
|
||||
--owner=my-org \
|
||||
--repo=infrastructure \
|
||||
--personal \
|
||||
--path=clusters/staging
|
||||
```
|
||||
|
||||
Each cluster reconciles its own `clusters/*/` directory.
|
||||
|
||||
### Repository structure
|
||||
|
||||
```
|
||||
clusters/
|
||||
├── production/
|
||||
│ ├── kustomization.yaml
|
||||
│ └── pangolin/
|
||||
│ ├── helmrepo.yaml
|
||||
│ └── helmrelease.yaml (prod values)
|
||||
├── staging/
|
||||
│ ├── kustomization.yaml
|
||||
│ └── pangolin/
|
||||
│ ├── helmrepo.yaml
|
||||
│ └── helmrelease.yaml (staging values)
|
||||
└── dev/
|
||||
├── kustomization.yaml
|
||||
└── pangolin/
|
||||
└── helmrelease.yaml (dev values)
|
||||
```
|
||||
|
||||
Each environment's HelmRelease uses environment-specific values.
|
||||
|
||||
## Next steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="GitOps Overview" href="/self-host/manual/kubernetes/gitops/overview" icon="code-branch" />
|
||||
<Card title="Argo CD" href="/self-host/manual/kubernetes/gitops/argocd" icon="code-branch" />
|
||||
<Card title="Pangolin Configuration" href="/self-host/manual/kubernetes/pangolin/configuration" icon="sliders" />
|
||||
<Card title="Troubleshooting" href="/self-host/manual/kubernetes/pangolin/troubleshooting" icon="circle-question" />
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,72 @@
|
||||
---
|
||||
title: "GitOps Overview"
|
||||
description: "Deploy Pangolin and Sites (Newt) with GitOps workflows such as Argo CD or Flux."
|
||||
---
|
||||
|
||||
Use GitOps when Pangolin and Sites (Newt) should be reconciled from Git instead of being installed manually from a local shell.
|
||||
Can be used together with Blueprints — see [Blueprint config reference](/self-host/advanced/config-file) for details.
|
||||
|
||||
These guides assume you already use, or plan to use, a GitOps controller such as Argo CD or Flux.
|
||||
General GitOps concepts such as reconciliation, desired state, and Git-driven workflows are outside the scope of this documentation. Refer to your GitOps controller's documentation for those concepts.
|
||||
|
||||
## Supported GitOps paths
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Argo CD Guide" href="/self-host/manual/kubernetes/gitops/argocd" icon="code-branch">
|
||||
Deploy Pangolin or Sites (Newt) with Argo CD Applications.
|
||||
</Card>
|
||||
<Card title="Flux Guide" href="/self-host/manual/kubernetes/gitops/flux" icon="code-branch">
|
||||
Deploy Pangolin or Sites (Newt) with Flux HelmRelease or Kustomization resources.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## What GitOps manages
|
||||
|
||||
A GitOps workflow can reconcile the same deployment inputs used by the other Kubernetes guides:
|
||||
|
||||
| Input | Used for |
|
||||
| --- | --- |
|
||||
| Helm chart values | Configure Pangolin, controller mode, database mode, ingress, Sites, and related components. |
|
||||
| Kustomize overlays | Patch or compose rendered manifests for environment-specific deployments. |
|
||||
| Kubernetes Secrets | Provide credentials, TLS material, database connection details, or Site connector credentials. |
|
||||
| Custom resources | Manage Argo CD Applications, Flux HelmReleases, Flux Kustomizations, or related controller resources. |
|
||||
|
||||
## Recommended layout
|
||||
|
||||
Keep the Pangolin and Site configuration close to the cluster or environment that owns it.
|
||||
|
||||
```text
|
||||
infrastructure/
|
||||
├── clusters/
|
||||
│ ├── production/
|
||||
│ │ ├── pangolin/
|
||||
│ │ └── sites/
|
||||
│ ├── staging/
|
||||
│ │ ├── pangolin/
|
||||
│ │ └── sites/
|
||||
│ └── dev/
|
||||
│ ├── pangolin/
|
||||
│ └── sites/
|
||||
└── shared/
|
||||
├── pangolin/
|
||||
└── sites/
|
||||
```
|
||||
|
||||
Use environment-specific directories for values, patches, and secrets that differ between clusters. Use shared directories only for reusable configuration that should stay the same across environments.
|
||||
|
||||
## Next steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Argo CD Guide" href="/self-host/manual/kubernetes/gitops/argocd" icon="code-branch">
|
||||
Create Argo CD Applications for Pangolin and Sites (Newt).
|
||||
</Card>
|
||||
<Card title="Flux Guide" href="/self-host/manual/kubernetes/gitops/flux" icon="code-branch">
|
||||
Create Flux sources, HelmReleases, or Kustomizations for Pangolin and Sites (Newt).
|
||||
</Card>
|
||||
<Card title="Choose an Installation Path" href="/self-host/manual/kubernetes/choose-method" icon="route">
|
||||
Compare the supported Kubernetes deployment paths.
|
||||
</Card>
|
||||
<Card title="Prerequisites" href="/self-host/manual/kubernetes/prerequisites" icon="list-check">
|
||||
Review cluster, networking, storage, RBAC, and resource requirements.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,372 @@
|
||||
---
|
||||
title: "Helm"
|
||||
description: "Kubernetes installation using Helm charts for Pangolin and Newt."
|
||||
---
|
||||
|
||||
Helm is the recommended method for standard Kubernetes installations of Pangolin and Newt.
|
||||
|
||||
Use Helm when you want a chart-based workflow for installing, upgrading, rolling back, and removing releases from your cluster.
|
||||
|
||||
## Helm repository setup
|
||||
|
||||
Add the Fossorial Helm chart repository:
|
||||
|
||||
```bash
|
||||
helm repo add fossorial https://charts.fossorial.io
|
||||
helm repo update fossorial
|
||||
```
|
||||
|
||||
Search for available charts:
|
||||
|
||||
```bash
|
||||
helm search repo fossorial
|
||||
```
|
||||
|
||||
The classic Helm repository flow is the default path for most installations:
|
||||
|
||||
```bash
|
||||
helm install my-newt fossorial/newt
|
||||
helm install my-pangolin fossorial/pangolin
|
||||
```
|
||||
|
||||
## Installation overview
|
||||
|
||||
A typical Helm installation flow looks like this:
|
||||
|
||||
<Steps>
|
||||
<Step title="Create namespace and labels">
|
||||
Create the namespace manually and apply required labels or annotations.
|
||||
</Step>
|
||||
<Step title="Prepare values files">
|
||||
Create a `values.yaml` file for each release (`values-pangolin.yaml`, `values-newt.yaml`).
|
||||
</Step>
|
||||
<Step title="Install with Helm">
|
||||
Install with `helm upgrade --install` to support first install and future updates with the same command.
|
||||
</Step>
|
||||
<Step title="Verify release and resources">
|
||||
Confirm Helm release status and Kubernetes resources after deployment.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<Info>
|
||||
It is recommended to create the namespace explicitly before installation. This allows you to apply Pod Security Admission labels, policy labels, annotations, or other cluster-specific metadata before the chart creates workloads.
|
||||
</Info>
|
||||
|
||||
For detailed installation steps, see:
|
||||
|
||||
* [Pangolin Helm Quick-Start](/self-host/manual/kubernetes/pangolin/helm) — Install Pangolin
|
||||
|
||||
## Install command patterns
|
||||
|
||||
```bash tab="Classic Helm repository"
|
||||
helm upgrade --install pangolin fossorial/pangolin \
|
||||
--namespace pangolin \
|
||||
--values values-pangolin.yaml
|
||||
|
||||
helm upgrade --install newt fossorial/newt \
|
||||
--namespace pangolin \
|
||||
--values values-newt.yaml
|
||||
```
|
||||
|
||||
```bash tab="OCI (GHCR)"
|
||||
helm upgrade --install pangolin oci://ghcr.io/fosrl/helm-charts/pangolin \
|
||||
--version 0.1.0-alpha.0 \
|
||||
--namespace pangolin \
|
||||
--values values-pangolin.yaml
|
||||
|
||||
helm upgrade --install newt oci://ghcr.io/fosrl/helm-charts/newt \
|
||||
--version 1.4.0 \
|
||||
--namespace pangolin \
|
||||
--values values-newt.yaml
|
||||
```
|
||||
|
||||
## Namespace preparation
|
||||
|
||||
Create the namespace before installing the chart:
|
||||
|
||||
```bash
|
||||
kubectl create namespace pangolin
|
||||
```
|
||||
|
||||
If your cluster uses Pod Security Admission or namespace-based policies, apply the required labels before installation.
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
kubectl label namespace pangolin \
|
||||
pod-security.kubernetes.io/enforce=baseline \
|
||||
pod-security.kubernetes.io/audit=restricted \
|
||||
pod-security.kubernetes.io/warn=restricted
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Pangolin deployments that include Gerbil require permissions that are not compatible with a restricted namespace profile, because Gerbil manages WireGuard and requires capabilities such as `NET_ADMIN`.
|
||||
</Warning>
|
||||
|
||||
For more details, see [Prerequisites](/self-host/manual/kubernetes/prerequisites).
|
||||
|
||||
## Install with a values file
|
||||
|
||||
Both charts use values files for configuration.
|
||||
|
||||
Pangolin example:
|
||||
|
||||
```bash
|
||||
helm upgrade --install pangolin fossorial/pangolin \
|
||||
--namespace pangolin \
|
||||
--values values-pangolin.yaml
|
||||
```
|
||||
|
||||
Newt example:
|
||||
|
||||
```bash
|
||||
helm upgrade --install newt fossorial/newt \
|
||||
--namespace pangolin \
|
||||
--values values-newt.yaml
|
||||
```
|
||||
|
||||
Using `helm upgrade --install` keeps the command usable for both the first installation and later configuration changes.
|
||||
|
||||
<Note>
|
||||
Do not use `--create-namespace` if you need custom namespace labels or annotations. Create the namespace first and then run Helm against that namespace.
|
||||
</Note>
|
||||
|
||||
## Values and configuration
|
||||
|
||||
Keep reusable configuration in a values file:
|
||||
|
||||
```bash
|
||||
helm upgrade --install pangolin fossorial/pangolin \
|
||||
--namespace pangolin \
|
||||
--values values-pangolin.yaml
|
||||
```
|
||||
|
||||
Use `--set` only for small tests or temporary overrides:
|
||||
|
||||
```bash
|
||||
helm upgrade --install pangolin fossorial/pangolin \
|
||||
--namespace pangolin \
|
||||
--set example.key=value
|
||||
```
|
||||
|
||||
Common value sources:
|
||||
|
||||
* `values-pangolin.yaml` for Pangolin.
|
||||
* `values-newt.yaml` for Newt.
|
||||
* Kubernetes Secrets for credentials.
|
||||
* Existing cluster resources such as TLS secrets, StorageClasses, or ingress controllers.
|
||||
|
||||
Full configuration options are documented here:
|
||||
|
||||
* [Pangolin Configuration](/self-host/manual/kubernetes/pangolin/configuration)
|
||||
|
||||
## Artifact Hub and chart discovery
|
||||
|
||||
The Fossorial charts can be installed from the Fossorial Helm repository:
|
||||
|
||||
```bash
|
||||
helm repo add fossorial https://charts.fossorial.io
|
||||
helm repo update fossorial
|
||||
helm search repo fossorial
|
||||
```
|
||||
|
||||
Artifact Hub can also be used to discover published chart metadata, available versions, install commands, and repository information.
|
||||
|
||||
<Note>
|
||||
Always verify the chart name, chart version, and repository URL before copying install commands into production.
|
||||
</Note>
|
||||
|
||||
## OCI-based charts
|
||||
|
||||
OCI is not a separate installation method. It only changes where Helm pulls the chart from.
|
||||
|
||||
For Pangolin and Newt, OCI chart publishing is available in GHCR:
|
||||
|
||||
* Newt: `oci://ghcr.io/fosrl/helm-charts/newt`
|
||||
* Pangolin: `oci://ghcr.io/fosrl/helm-charts/pangolin`
|
||||
|
||||
You still use Helm in the same way: choose a chart, select a version, provide values, and install the release.
|
||||
|
||||
### Pull OCI charts
|
||||
|
||||
Newt example:
|
||||
|
||||
```bash
|
||||
helm pull oci://ghcr.io/fosrl/helm-charts/newt \
|
||||
--version 1.4.0
|
||||
```
|
||||
|
||||
Pangolin example:
|
||||
|
||||
```bash
|
||||
helm pull oci://ghcr.io/fosrl/helm-charts/pangolin \
|
||||
--version 0.1.0-alpha.0
|
||||
```
|
||||
|
||||
### Install from OCI
|
||||
|
||||
Newt example:
|
||||
|
||||
```bash
|
||||
helm upgrade --install newt oci://ghcr.io/fosrl/helm-charts/newt \
|
||||
--version 1.4.0 \
|
||||
--namespace pangolin \
|
||||
--values values-newt.yaml
|
||||
```
|
||||
|
||||
Pangolin example:
|
||||
|
||||
```bash
|
||||
helm upgrade --install pangolin oci://ghcr.io/fosrl/helm-charts/pangolin \
|
||||
--version 0.1.0-alpha.0 \
|
||||
--namespace pangolin \
|
||||
--values values-pangolin.yaml
|
||||
```
|
||||
|
||||
<Info>
|
||||
Use the classic Helm repository when you want the normal `helm repo add` and `helm search repo` workflow. Use OCI when you want to pull charts directly from GHCR or when your deployment tooling expects OCI chart references.
|
||||
</Info>
|
||||
|
||||
## Upgrade and maintenance
|
||||
|
||||
### Update the classic Helm repository
|
||||
|
||||
```bash
|
||||
helm repo update fossorial
|
||||
```
|
||||
|
||||
This step is only needed when using the classic Helm repository. OCI installs pull the chart by OCI reference and version.
|
||||
|
||||
### Upgrade Pangolin
|
||||
|
||||
Classic Helm repository:
|
||||
|
||||
```bash
|
||||
helm upgrade pangolin fossorial/pangolin \
|
||||
--namespace pangolin \
|
||||
--values values-pangolin.yaml
|
||||
```
|
||||
|
||||
OCI:
|
||||
|
||||
```bash
|
||||
helm upgrade pangolin oci://ghcr.io/fosrl/helm-charts/pangolin \
|
||||
--version 0.1.0-alpha.0 \
|
||||
--namespace pangolin \
|
||||
--values values-pangolin.yaml
|
||||
```
|
||||
|
||||
### Upgrade Newt
|
||||
|
||||
Classic Helm repository:
|
||||
|
||||
```bash
|
||||
helm upgrade newt fossorial/newt \
|
||||
--namespace pangolin \
|
||||
--values values-newt.yaml
|
||||
```
|
||||
|
||||
OCI:
|
||||
|
||||
```bash
|
||||
helm upgrade newt oci://ghcr.io/fosrl/helm-charts/newt \
|
||||
--version 1.4.0 \
|
||||
--namespace pangolin \
|
||||
--values values-newt.yaml
|
||||
```
|
||||
|
||||
### Check release status
|
||||
|
||||
```bash
|
||||
helm status pangolin --namespace pangolin
|
||||
helm history pangolin --namespace pangolin
|
||||
```
|
||||
|
||||
```bash
|
||||
helm status newt --namespace pangolin
|
||||
helm history newt --namespace pangolin
|
||||
```
|
||||
|
||||
### View rendered manifests
|
||||
|
||||
```bash
|
||||
helm get manifest pangolin --namespace pangolin
|
||||
```
|
||||
|
||||
```bash
|
||||
helm get manifest newt --namespace pangolin
|
||||
```
|
||||
|
||||
### View applied values
|
||||
|
||||
```bash
|
||||
helm get values pangolin --namespace pangolin
|
||||
```
|
||||
|
||||
```bash
|
||||
helm get values newt --namespace pangolin
|
||||
```
|
||||
|
||||
### Roll back a release
|
||||
|
||||
```bash
|
||||
helm rollback pangolin <revision> --namespace pangolin
|
||||
```
|
||||
|
||||
```bash
|
||||
helm rollback newt <revision> --namespace pangolin
|
||||
```
|
||||
|
||||
### Uninstall a release
|
||||
|
||||
```bash
|
||||
helm uninstall pangolin --namespace pangolin
|
||||
```
|
||||
|
||||
```bash
|
||||
helm uninstall newt --namespace pangolin
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Uninstalling a Helm release does not always remove persistent volumes, externally managed secrets, DNS records, certificates, or cloud load balancers. Review the namespace and related cluster resources before deleting data.
|
||||
</Warning>
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
* [Pangolin Troubleshooting](/self-host/manual/kubernetes/pangolin/troubleshooting)
|
||||
|
||||
Useful Helm commands:
|
||||
|
||||
```bash
|
||||
helm list --all-namespaces
|
||||
helm status <release-name> --namespace <namespace>
|
||||
helm history <release-name> --namespace <namespace>
|
||||
helm get values <release-name> --namespace <namespace>
|
||||
helm get manifest <release-name> --namespace <namespace>
|
||||
```
|
||||
|
||||
Useful Kubernetes commands:
|
||||
|
||||
```bash
|
||||
kubectl get pods -n pangolin
|
||||
kubectl get events -n pangolin --sort-by=.lastTimestamp
|
||||
kubectl describe pod <pod-name> -n pangolin
|
||||
kubectl logs <pod-name> -n pangolin
|
||||
```
|
||||
|
||||
## Next steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Pangolin Helm Install" href="/self-host/manual/kubernetes/pangolin/helm" icon="server">
|
||||
Install Pangolin with the Helm chart.
|
||||
</Card>
|
||||
<Card title="Pangolin Configuration" href="/self-host/manual/kubernetes/pangolin/configuration" icon="sliders">
|
||||
Configure Pangolin chart values for your cluster.
|
||||
</Card>
|
||||
<Card title="Argo CD" href="/self-host/manual/kubernetes/gitops/argocd" icon="code-branch">
|
||||
Deploy the charts with Argo CD.
|
||||
</Card>
|
||||
<Card title="Flux" href="/self-host/manual/kubernetes/gitops/flux" icon="code-branch">
|
||||
Deploy the charts with Flux.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,381 @@
|
||||
---
|
||||
title: "Helmfile"
|
||||
description: "Advanced Kubernetes installation using Helmfile for multi-release orchestration."
|
||||
---
|
||||
|
||||
Helmfile is a declarative way to manage multiple Helm releases in a single workflow. Use Helmfile when you need to install Pangolin and/or Newt alongside other Kubernetes components or manage multiple releases together.
|
||||
|
||||
## When to use Helmfile
|
||||
|
||||
Use Helmfile if you want to:
|
||||
|
||||
- **Orchestrate multiple Helm releases** in a single file (Pangolin + Newt + dependencies).
|
||||
- **Manage dependencies** between releases (e.g., install cert-manager before Pangolin).
|
||||
- **Keep release definitions** in version control and synchronized.
|
||||
- **Avoid repeated `helm install` commands** for complex multi-release setups.
|
||||
|
||||
**Not using Helmfile?** If you're installing only Pangolin or only Newt without additional services, [Helm quick-start](/self-host/manual/kubernetes/helm) is simpler.
|
||||
|
||||
## Helm vs. Helmfile
|
||||
|
||||
| Aspect | Helm | Helmfile |
|
||||
| --- | --- | --- |
|
||||
| **Purpose** | Install/manage a single Helm chart release | Orchestrate multiple Helm chart releases |
|
||||
| **Command** | `helm install`, `helm upgrade` | `helmfile sync`, `helmfile apply` |
|
||||
| **Use case** | Quick install, single app | Multi-release, dependencies, fleet management |
|
||||
| **Complexity** | Low | Medium |
|
||||
|
||||
## Helmfile prerequisites
|
||||
|
||||
- Helm 3.10+
|
||||
- `helmfile` CLI installed: [Helmfile GitHub](https://github.com/helmfile/helmfile)
|
||||
- Basic knowledge of Helm values and YAML
|
||||
|
||||
Install helmfile:
|
||||
|
||||
```bash
|
||||
# macOS/Linux with brew
|
||||
brew install helmfile
|
||||
|
||||
# or download from releases
|
||||
wget https://github.com/helmfile/helmfile/releases/download/v<version>/helmfile_<os>_<arch>
|
||||
chmod +x helmfile
|
||||
sudo mv helmfile /usr/local/bin/
|
||||
```
|
||||
|
||||
Verify:
|
||||
|
||||
```bash
|
||||
helmfile --version
|
||||
```
|
||||
|
||||
## Basic Helmfile structure
|
||||
|
||||
A Helmfile is a YAML file (typically named `helmfile.yaml`) that declares multiple releases:
|
||||
|
||||
```yaml
|
||||
# helmfile.yaml
|
||||
releases:
|
||||
- name: cert-manager
|
||||
namespace: cert-manager
|
||||
createNamespace: true
|
||||
chart: jetstack/cert-manager
|
||||
version: v1.14.0
|
||||
|
||||
- name: pangolin
|
||||
namespace: pangolin
|
||||
createNamespace: true
|
||||
chart: fossorial/pangolin
|
||||
version: 0.1.0-alpha.0
|
||||
values:
|
||||
- pangolin-values.yaml
|
||||
|
||||
- name: newt
|
||||
namespace: pangolin
|
||||
chart: fossorial/newt
|
||||
version: 1.4.0
|
||||
values:
|
||||
- newt-values.yaml
|
||||
dependsOn:
|
||||
- pangolin
|
||||
```
|
||||
|
||||
## Helmfile with Pangolin and Newt
|
||||
|
||||
### 1. Add Helm repositories
|
||||
|
||||
```bash
|
||||
helm repo add jetstack https://charts.jetstack.io
|
||||
helm repo add fossorial https://charts.fossorial.io
|
||||
helm repo update
|
||||
```
|
||||
|
||||
### 2. Create Helmfile
|
||||
|
||||
Create `helmfile.yaml`:
|
||||
|
||||
```yaml
|
||||
helmDefaults:
|
||||
atomic: true
|
||||
cleanupOnFail: true
|
||||
wait: true
|
||||
timeout: 600
|
||||
recreatePods: true
|
||||
force: false
|
||||
|
||||
repositories:
|
||||
- name: jetstack
|
||||
url: https://charts.jetstack.io
|
||||
- name: fossorial
|
||||
url: https://charts.fossorial.io
|
||||
|
||||
releases:
|
||||
- name: cert-manager
|
||||
namespace: cert-manager
|
||||
createNamespace: true
|
||||
chart: jetstack/cert-manager
|
||||
version: v1.14.0
|
||||
set:
|
||||
installCRDs: true
|
||||
|
||||
- name: pangolin
|
||||
namespace: pangolin
|
||||
createNamespace: true
|
||||
chart: fossorial/pangolin
|
||||
version: 0.1.0-alpha.0
|
||||
values:
|
||||
- ./values/pangolin.yaml
|
||||
dependsOn:
|
||||
- cert-manager
|
||||
|
||||
- name: newt
|
||||
namespace: pangolin
|
||||
chart: fossorial/newt
|
||||
version: 1.4.0
|
||||
values:
|
||||
- ./values/newt.yaml
|
||||
dependsOn:
|
||||
- pangolin
|
||||
```
|
||||
|
||||
### 3. Create values files
|
||||
|
||||
Create `values/pangolin.yaml`:
|
||||
|
||||
```yaml
|
||||
deployment:
|
||||
type: controller
|
||||
mode: multi
|
||||
|
||||
database:
|
||||
mode: cloudnativepg
|
||||
|
||||
pangolin:
|
||||
config:
|
||||
app:
|
||||
dashboard_url: https://pangolin.example.com
|
||||
domains:
|
||||
domain1:
|
||||
base_domain: example.com
|
||||
gerbil:
|
||||
base_endpoint: vpn.example.com
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
className: traefik
|
||||
hosts:
|
||||
- host: pangolin.example.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
tls:
|
||||
- secretName: pangolin-tls
|
||||
hosts:
|
||||
- pangolin.example.com
|
||||
```
|
||||
|
||||
Create `values/newt.yaml`:
|
||||
|
||||
```yaml
|
||||
newtInstances:
|
||||
- name: main-tunnel
|
||||
enabled: true
|
||||
auth:
|
||||
existingSecretName: newt-auth
|
||||
```
|
||||
|
||||
### 4. Create Newt auth secret
|
||||
|
||||
Before applying Helmfile:
|
||||
|
||||
```bash
|
||||
kubectl create namespace pangolin
|
||||
kubectl create secret generic newt-auth \
|
||||
-n pangolin \
|
||||
--from-literal=PANGOLIN_ENDPOINT=https://pangolin.example.com \
|
||||
--from-literal=NEWT_ID=<your-newt-id> \
|
||||
--from-literal=NEWT_SECRET=<your-newt-secret>
|
||||
```
|
||||
|
||||
### 5. Deploy with Helmfile
|
||||
|
||||
```bash
|
||||
# Preview changes
|
||||
helmfile diff
|
||||
|
||||
# Apply releases
|
||||
helmfile sync
|
||||
|
||||
# or
|
||||
helmfile apply
|
||||
```
|
||||
|
||||
### 6. Verify deployment
|
||||
|
||||
```bash
|
||||
helmfile status
|
||||
|
||||
# Check individual releases
|
||||
helm status cert-manager -n cert-manager
|
||||
helm status pangolin -n pangolin
|
||||
helm status newt -n pangolin
|
||||
|
||||
# Check pods
|
||||
kubectl get pods -n pangolin
|
||||
kubectl get pods -n cert-manager
|
||||
```
|
||||
|
||||
## Advanced: Helmfile with environments
|
||||
|
||||
For multi-environment setups (dev, staging, prod), use Helmfile environments:
|
||||
|
||||
```yaml
|
||||
environments:
|
||||
dev:
|
||||
values:
|
||||
environment: dev
|
||||
domain: dev.example.com
|
||||
replicaCount: 1
|
||||
prod:
|
||||
values:
|
||||
environment: prod
|
||||
domain: pangolin.example.com
|
||||
replicaCount: 3
|
||||
|
||||
helmDefaults:
|
||||
atomic: true
|
||||
wait: true
|
||||
|
||||
repositories:
|
||||
- name: fossorial
|
||||
url: https://charts.fossorial.io
|
||||
|
||||
releases:
|
||||
- name: pangolin
|
||||
namespace: pangolin
|
||||
createNamespace: true
|
||||
chart: fossorial/pangolin
|
||||
version: 0.1.0-alpha.0
|
||||
values:
|
||||
- ./values/pangolin-{{ .Environment.Values.environment }}.yaml
|
||||
```
|
||||
|
||||
Deploy to specific environment:
|
||||
|
||||
```bash
|
||||
helmfile -e dev sync
|
||||
helmfile -e prod sync
|
||||
```
|
||||
|
||||
## Helmfile with GitOps
|
||||
|
||||
### Using Helmfile with FluxCD
|
||||
|
||||
FluxCD can reconcile Helmfile declarations using the `helmfile-controller`. This allows Git-driven Helmfile updates:
|
||||
|
||||
1. Commit Helmfile and values to Git
|
||||
2. Create HelmRelease for each release in your Helmfile
|
||||
3. Flux reconciles and applies changes
|
||||
|
||||
See [Flux Guide](/self-host/manual/kubernetes/gitops/flux) for details.
|
||||
|
||||
### Using Helmfile with Argo CD
|
||||
|
||||
While Argo CD has native Helm and Kustomize support, you can:
|
||||
|
||||
1. Use Helmfile to render manifests: `helmfile template > manifests.yaml`
|
||||
2. Commit manifests to Git
|
||||
3. Have Argo CD manage the raw YAML
|
||||
|
||||
Alternatively, use Helm source in Argo CD (simpler than Helmfile for single releases).
|
||||
|
||||
## Troubleshooting Helmfile
|
||||
|
||||
### Check syntax
|
||||
|
||||
```bash
|
||||
helmfile lint
|
||||
```
|
||||
|
||||
### Debug release dependencies
|
||||
|
||||
```bash
|
||||
helmfile template
|
||||
```
|
||||
|
||||
### See what will be deployed
|
||||
|
||||
```bash
|
||||
helmfile diff
|
||||
```
|
||||
|
||||
### Remove releases
|
||||
|
||||
```bash
|
||||
helmfile destroy
|
||||
```
|
||||
|
||||
<Warning>
|
||||
`helmfile destroy` uninstalls all releases and may delete data (e.g., databases). Use with caution in production.
|
||||
</Warning>
|
||||
|
||||
## Common patterns
|
||||
|
||||
### Helmfile with local chart overrides
|
||||
|
||||
```yaml
|
||||
releases:
|
||||
- name: pangolin
|
||||
namespace: pangolin
|
||||
chart: ./charts/pangolin # local path
|
||||
values:
|
||||
- values.yaml
|
||||
```
|
||||
|
||||
### Helmfile with inline values
|
||||
|
||||
```yaml
|
||||
releases:
|
||||
- name: pangolin
|
||||
namespace: pangolin
|
||||
chart: fossorial/pangolin
|
||||
set:
|
||||
deployment.type: controller
|
||||
deployment.mode: multi
|
||||
```
|
||||
|
||||
### Helmfile with conditional releases
|
||||
|
||||
```yaml
|
||||
releases:
|
||||
- name: cert-manager
|
||||
namespace: cert-manager
|
||||
createNamespace: true
|
||||
chart: jetstack/cert-manager
|
||||
installed: {{ .Environment.Values.installCertManager | default true }}
|
||||
```
|
||||
|
||||
## Important notes
|
||||
|
||||
### Official support
|
||||
|
||||
Helmfile for Pangolin/Newt Kubernetes deployments is **advanced/community-supported**. The primary supported methods are:
|
||||
|
||||
- Helm directly
|
||||
- Kustomize overlays
|
||||
- GitOps tools (Argo CD, Flux)
|
||||
|
||||
If you encounter Helmfile-specific issues, refer to the [Helmfile documentation](https://github.com/roboll/helmfile) and community.
|
||||
|
||||
### Helm chart dependencies
|
||||
|
||||
The Pangolin Helm chart includes optional sub-chart dependencies (e.g., CloudNativePG operator). Helmfile does not manage these—they're handled by Helm. Ensure chart dependencies are available when installing.
|
||||
|
||||
## Next steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Helm Quick-Start" href="/self-host/manual/kubernetes/helm" icon="box" />
|
||||
<Card title="Pangolin Configuration" href="/self-host/manual/kubernetes/pangolin/configuration" icon="sliders" />
|
||||
<Card title="GitOps with Flux" href="/self-host/manual/kubernetes/gitops/flux" icon="code-branch" />
|
||||
<Card title="Troubleshooting" href="/self-host/manual/kubernetes/pangolin/troubleshooting" icon="circle-question" />
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,372 @@
|
||||
---
|
||||
title: "Kustomize"
|
||||
description: "Customize Helm-rendered Kubernetes manifests with Kustomize overlays."
|
||||
---
|
||||
|
||||
Kustomize can be used to customize Kubernetes manifests with bases, overlays, and patches.
|
||||
|
||||
For Pangolin and Newt, the supported Kustomize workflow is to render the Helm charts into manifests and use those rendered manifests as the Kustomize base.
|
||||
|
||||
Use Kustomize when you need:
|
||||
|
||||
- environment-specific overlays for dev, staging, or production
|
||||
- explicit manifest patches in Git
|
||||
- a manifest-driven workflow for GitOps tools
|
||||
- small changes on top of a shared base without maintaining separate full manifests
|
||||
|
||||
## Supported workflow
|
||||
|
||||
The chart repository does not provide native Kustomize bases. Use this workflow instead:
|
||||
|
||||
<Steps>
|
||||
<Step title="Render chart manifests">
|
||||
Render the Helm chart with your values file and save the output as base manifests.
|
||||
</Step>
|
||||
<Step title="Commit base manifests">
|
||||
Commit rendered manifests as the Kustomize base in Git.
|
||||
</Step>
|
||||
<Step title="Create environment overlays">
|
||||
Create overlays for each environment (for example dev, staging, production).
|
||||
</Step>
|
||||
<Step title="Apply or reconcile">
|
||||
Apply overlays manually or reconcile them with Argo CD or Flux.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<Warning>
|
||||
Do not manage the same resources with both a live Helm release and Kustomize. Pick one ownership model per environment.
|
||||
</Warning>
|
||||
|
||||
Recommended ownership model:
|
||||
|
||||
- Use Helm only to render manifests.
|
||||
- Use Kustomize, Argo CD, or Flux to apply and reconcile the rendered manifests.
|
||||
- Re-render the base when upgrading the chart version.
|
||||
|
||||
## Example repository layout
|
||||
|
||||
```text
|
||||
my-pangolin-k8s/
|
||||
├── base/
|
||||
│ ├── kustomization.yaml
|
||||
│ ├── pangolin.yaml
|
||||
│ └── newt.yaml
|
||||
├── overlays/
|
||||
│ ├── dev/
|
||||
│ │ ├── kustomization.yaml
|
||||
│ │ └── pangolin-resources.patch.yaml
|
||||
│ ├── staging/
|
||||
│ │ ├── kustomization.yaml
|
||||
│ │ └── pangolin-resources.patch.yaml
|
||||
│ └── prod/
|
||||
│ ├── kustomization.yaml
|
||||
│ └── pangolin-resources.patch.yaml
|
||||
└── values/
|
||||
├── values-pangolin.yaml
|
||||
└── values-newt.yaml
|
||||
```
|
||||
|
||||
## Step 1: Render manifests from Helm
|
||||
|
||||
Create a base directory:
|
||||
|
||||
```bash
|
||||
mkdir -p base overlays/dev overlays/staging overlays/prod
|
||||
```
|
||||
|
||||
Render Pangolin:
|
||||
|
||||
```bash tab="Classic Helm repository"
|
||||
helm template pangolin fossorial/pangolin \
|
||||
--namespace pangolin \
|
||||
--values values/values-pangolin.yaml \
|
||||
> base/pangolin.yaml
|
||||
```
|
||||
|
||||
```bash tab="OCI (GHCR)"
|
||||
helm template pangolin oci://ghcr.io/fosrl/helm-charts/pangolin \
|
||||
--version 0.1.0-alpha.0 \
|
||||
--namespace pangolin \
|
||||
--values values/values-pangolin.yaml \
|
||||
> base/pangolin.yaml
|
||||
```
|
||||
|
||||
Render Newt:
|
||||
|
||||
```bash tab="Classic Helm repository"
|
||||
helm template newt fossorial/newt \
|
||||
--namespace pangolin \
|
||||
--values values/values-newt.yaml \
|
||||
> base/newt.yaml
|
||||
```
|
||||
|
||||
```bash tab="OCI (GHCR)"
|
||||
helm template newt oci://ghcr.io/fosrl/helm-charts/newt \
|
||||
--version 1.4.0 \
|
||||
--namespace pangolin \
|
||||
--values values/values-newt.yaml \
|
||||
> base/newt.yaml
|
||||
```
|
||||
|
||||
## Step 2: Create the base kustomization
|
||||
|
||||
```yaml
|
||||
# base/kustomization.yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
resources:
|
||||
- pangolin.yaml
|
||||
- newt.yaml
|
||||
```
|
||||
|
||||
## Step 3: Create an overlay
|
||||
|
||||
Use `resources` to reference the base.
|
||||
|
||||
```yaml
|
||||
# overlays/prod/kustomization.yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
resources:
|
||||
- ../../base
|
||||
|
||||
labels:
|
||||
- pairs:
|
||||
app.kubernetes.io/environment: production
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
|
||||
patches:
|
||||
- path: pangolin-resources.patch.yaml
|
||||
target:
|
||||
group: apps
|
||||
version: v1
|
||||
kind: Deployment
|
||||
name: pangolin
|
||||
```
|
||||
|
||||
<Note>
|
||||
Avoid `namePrefix` and `nameSuffix` for Helm-rendered bases unless you have verified every generated reference. Renaming chart-generated resources can break service names, selectors, secret references, and workload dependencies.
|
||||
</Note>
|
||||
|
||||
## Step 4: Add patches
|
||||
|
||||
Example Strategic Merge patch for container resources:
|
||||
|
||||
```yaml
|
||||
# overlays/prod/pangolin-resources.patch.yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: pangolin
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: pangolin
|
||||
resources:
|
||||
requests:
|
||||
cpu: 1000m
|
||||
memory: 1Gi
|
||||
limits:
|
||||
memory: 2Gi
|
||||
```
|
||||
|
||||
Example JSON6902-style inline patch:
|
||||
|
||||
```yaml
|
||||
# overlays/prod/kustomization.yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
resources:
|
||||
- ../../base
|
||||
|
||||
patches:
|
||||
- target:
|
||||
group: apps
|
||||
version: v1
|
||||
kind: Deployment
|
||||
name: pangolin
|
||||
patch: |-
|
||||
- op: replace
|
||||
path: /spec/template/spec/containers/0/resources/requests/cpu
|
||||
value: "1000m"
|
||||
```
|
||||
|
||||
<Note>
|
||||
Modern Kustomize uses the `patches` field for both Strategic Merge and JSON6902-style patches. Avoid `patchesStrategicMerge`, `patchesJson6902`, and `bases` in new examples.
|
||||
</Note>
|
||||
|
||||
## Apply an overlay
|
||||
|
||||
Preview the rendered output:
|
||||
|
||||
```bash
|
||||
kustomize build overlays/prod
|
||||
```
|
||||
|
||||
Compare with the live cluster:
|
||||
|
||||
```bash
|
||||
kustomize build overlays/prod | kubectl diff -f -
|
||||
```
|
||||
|
||||
Apply the overlay:
|
||||
|
||||
```bash
|
||||
kubectl apply -k overlays/prod
|
||||
```
|
||||
|
||||
Or apply the rendered output:
|
||||
|
||||
```bash
|
||||
kustomize build overlays/prod | kubectl apply -f -
|
||||
```
|
||||
|
||||
## Updating the base
|
||||
|
||||
When upgrading chart versions or changing Helm values, re-render the base and review the diff.
|
||||
|
||||
```bash
|
||||
helm repo update fossorial
|
||||
```
|
||||
|
||||
Render the updated chart output:
|
||||
|
||||
```bash
|
||||
helm template pangolin fossorial/pangolin \
|
||||
--namespace pangolin \
|
||||
--values values/values-pangolin.yaml \
|
||||
> base/pangolin.yaml
|
||||
```
|
||||
|
||||
```bash
|
||||
helm template newt fossorial/newt \
|
||||
--namespace pangolin \
|
||||
--values values/values-newt.yaml \
|
||||
> base/newt.yaml
|
||||
```
|
||||
|
||||
Then validate the overlay:
|
||||
|
||||
```bash
|
||||
kustomize build overlays/prod
|
||||
```
|
||||
|
||||
Review changes before applying:
|
||||
|
||||
```bash
|
||||
git diff
|
||||
kustomize build overlays/prod | kubectl diff -f -
|
||||
```
|
||||
|
||||
Apply after review:
|
||||
|
||||
```bash
|
||||
kubectl apply -k overlays/prod
|
||||
```
|
||||
|
||||
## Important considerations
|
||||
|
||||
### Namespace handling
|
||||
|
||||
Render the charts with the namespace you intend to use:
|
||||
|
||||
```bash
|
||||
helm template pangolin fossorial/pangolin \
|
||||
--namespace pangolin \
|
||||
--values values/values-pangolin.yaml \
|
||||
> base/pangolin.yaml
|
||||
```
|
||||
|
||||
Create the namespace before applying the overlay:
|
||||
|
||||
```bash
|
||||
kubectl create namespace pangolin
|
||||
```
|
||||
|
||||
Apply any required Pod Security Admission labels or cluster-policy labels before workloads are created.
|
||||
|
||||
### Secrets
|
||||
|
||||
Do not commit plaintext secrets into rendered manifests.
|
||||
|
||||
Use one of these approaches instead:
|
||||
|
||||
* reference existing Kubernetes Secrets in the values file before rendering
|
||||
* create secrets separately with your secret-management workflow
|
||||
* use Sealed Secrets, External Secrets Operator, SOPS, or another GitOps-safe secret solution
|
||||
|
||||
### Do not mix ownership models
|
||||
|
||||
Avoid this pattern:
|
||||
|
||||
```text
|
||||
helm upgrade pangolin fossorial/pangolin
|
||||
kubectl apply -k overlays/prod
|
||||
```
|
||||
|
||||
This creates two tools managing the same objects.
|
||||
|
||||
Use one of these models instead:
|
||||
|
||||
| Model | Description |
|
||||
| ----------------- | ---------------------------------------------------------------------------------------- |
|
||||
| Helm-managed | Helm installs and upgrades the live release. Kustomize is not used for the same objects. |
|
||||
| Kustomize-managed | Helm only renders the base. Kustomize applies and owns the live objects. |
|
||||
| GitOps-managed | Argo CD or Flux applies the Kustomize overlay and owns reconciliation. |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
Validate the overlay:
|
||||
|
||||
```bash
|
||||
kustomize build overlays/prod
|
||||
```
|
||||
|
||||
Check the generated YAML:
|
||||
|
||||
```bash
|
||||
kustomize build overlays/prod > manifests.yaml
|
||||
```
|
||||
|
||||
Run a server-side dry run:
|
||||
|
||||
```bash
|
||||
kubectl apply -f manifests.yaml --dry-run=server
|
||||
```
|
||||
|
||||
Preview live changes:
|
||||
|
||||
```bash
|
||||
kubectl diff -f manifests.yaml
|
||||
```
|
||||
|
||||
Check live resources:
|
||||
|
||||
```bash
|
||||
kubectl get all -n pangolin
|
||||
kubectl get events -n pangolin --sort-by=.lastTimestamp
|
||||
```
|
||||
|
||||
## Next steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Pangolin Kustomize Install" href="/self-host/manual/kubernetes/pangolin/kustomize" icon="server">
|
||||
Install Pangolin with rendered manifests and Kustomize overlays.
|
||||
</Card>
|
||||
<Card title="Newt Kustomize Install" href="/manage/sites/kubernetes/kustomize" icon="globe">
|
||||
Install Newt with rendered manifests and Kustomize overlays.
|
||||
</Card>
|
||||
<Card title="Argo CD" href="/self-host/manual/kubernetes/gitops/argocd" icon="code-branch">
|
||||
Reconcile Kustomize overlays with Argo CD.
|
||||
</Card>
|
||||
<Card title="Flux" href="/self-host/manual/kubernetes/gitops/flux" icon="code-branch">
|
||||
Reconcile Kustomize overlays with Flux.
|
||||
</Card>
|
||||
<Card title="Troubleshooting" href="/self-host/manual/kubernetes/pangolin/troubleshooting" icon="circle-question">
|
||||
Troubleshoot Pangolin deployments on Kubernetes.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,77 @@
|
||||
---
|
||||
title: "Overview"
|
||||
description: "Deploy Pangolin, Sites (Newt), and related components on Kubernetes."
|
||||
---
|
||||
|
||||
## Components
|
||||
|
||||
| Component | Role |
|
||||
| --- | --- |
|
||||
| Pangolin | Main application for the dashboard, API, authentication, configuration, and database-backed state. |
|
||||
| Gerbil | Tunnel stack component used by Pangolin for site connectivity. |
|
||||
| Site (Newt) | Site connector used to connect private resources to Pangolin. |
|
||||
| Traefik | Reverse proxy and router for ingress traffic. |
|
||||
| PostgreSQL / SQLite | Database options for Pangolin deployments, depending on the selected chart configuration. |
|
||||
| Pangolin Kube Controller | Kubernetes controller for integrating Pangolin with Kubernetes and Traefik resources. |
|
||||
|
||||
<Info>
|
||||
Depending on your deployment mode, not every component is required. Local reverse proxy deployments and tunneled site deployments can have different component requirements.
|
||||
</Info>
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
U[Users] --> T[Traefik]
|
||||
T --> P[Pangolin]
|
||||
P --> G[Gerbil]
|
||||
S[Site connector<br/>Newt] --> G
|
||||
P --> D[(Database)]
|
||||
```
|
||||
|
||||
## Installation paths
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Choose an Installation Path" href="/self-host/manual/kubernetes/choose-method" icon="route">
|
||||
Pick the Kubernetes workflow that matches how you deploy applications.
|
||||
</Card>
|
||||
<Card title="Prerequisites" href="/self-host/manual/kubernetes/prerequisites" icon="list-check">
|
||||
Review the required cluster, ingress, DNS, storage, and secret setup.
|
||||
</Card>
|
||||
<Card title="Helm Quick-Start" href="/self-host/manual/kubernetes/helm" icon="box">
|
||||
Install Pangolin or Sites (Newt) with the standard chart-based workflow.
|
||||
</Card>
|
||||
<Card title="Kustomize Quick-Start" href="/self-host/manual/kubernetes/kustomize" icon="layer-group">
|
||||
Use overlays and patches for manifest-based deployments.
|
||||
</Card>
|
||||
<Card title="Argo CD Guide" href="/self-host/manual/kubernetes/gitops/argocd" icon="code-branch">
|
||||
Deploy Pangolin or Sites (Newt) with Argo CD.
|
||||
</Card>
|
||||
<Card title="Flux Guide" href="/self-host/manual/kubernetes/gitops/flux" icon="code-branch">
|
||||
Deploy Pangolin or Sites (Newt) with Flux.
|
||||
</Card>
|
||||
<Card title="Helmfile Guide" href="/self-host/manual/kubernetes/helmfile" icon="boxes-stacked">
|
||||
Manage multiple Helm releases together.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Component guides
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Pangolin with Helm" href="/self-host/manual/kubernetes/pangolin/helm" icon="server">
|
||||
Install Pangolin with the Helm chart.
|
||||
</Card>
|
||||
<Card title="Pangolin Configuration" href="/self-host/manual/kubernetes/pangolin/configuration" icon="sliders">
|
||||
Configure Pangolin for your Kubernetes environment.
|
||||
</Card>
|
||||
<Card title="Pangolin Troubleshooting" href="/self-host/manual/kubernetes/pangolin/troubleshooting" icon="circle-question">
|
||||
Diagnose and resolve Pangolin deployment issues.
|
||||
</Card>
|
||||
<Card title="Site (Newt) Helm" href="/manage/sites/kubernetes/helm" icon="server">
|
||||
Install a Site connector with the Newt Helm chart.
|
||||
</Card>
|
||||
<Card title="Site (Newt) Configuration" href="/manage/sites/kubernetes/configuration" icon="sliders">
|
||||
Configure Site connector credentials and runtime settings.
|
||||
</Card>
|
||||
<Card title="Site (Newt) Troubleshooting" href="/manage/sites/kubernetes/troubleshooting" icon="circle-question">
|
||||
Diagnose and resolve Site connector deployment issues.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,994 @@
|
||||
---
|
||||
title: "Configuration"
|
||||
description: "Configuration reference for Pangolin Kubernetes deployments."
|
||||
---
|
||||
|
||||
This page covers the main Pangolin Kubernetes configuration options for Helm and Kustomize workflows.
|
||||
|
||||
For exhaustive option coverage, refer to the chart resources:
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="README" href="https://github.com/fosrl/helm-charts/blob/main/charts/pangolin/README.md" />
|
||||
<Card title="values.yaml" href="https://github.com/fosrl/helm-charts/blob/main/charts/pangolin/values.yaml" />
|
||||
<Card title="values.schema.json" href="https://github.com/fosrl/helm-charts/blob/main/charts/pangolin/values.schema.json" />
|
||||
</CardGroup>
|
||||
|
||||
## Version context
|
||||
|
||||
This page is aligned with the Pangolin Helm chart `0.1.0-alpha.0`.
|
||||
|
||||
| Item | Value |
|
||||
| --- | --- |
|
||||
| Chart version | `0.1.0-alpha.0` |
|
||||
| Pangolin app version | `1.18.2` |
|
||||
| Kubernetes version | `>=1.30.14-0` |
|
||||
| Gerbil image tag | `1.3.1` |
|
||||
| pangolin-kube-controller image tag | `0.1.0-alpha.1` |
|
||||
| Traefik image tag | `v3.6.15` |
|
||||
|
||||
## Configuration sections
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Deployment topology" defaultOpen>
|
||||
|
||||
Control how Pangolin components are deployed and integrated with Kubernetes.
|
||||
|
||||
```yaml
|
||||
deployment:
|
||||
type: controller
|
||||
mode: multi
|
||||
installTraefikController: false
|
||||
traefikNamespace: ""
|
||||
```
|
||||
|
||||
Recommended production topology:
|
||||
|
||||
```yaml
|
||||
deployment:
|
||||
type: controller
|
||||
mode: multi
|
||||
```
|
||||
|
||||
| Setting | Description |
|
||||
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------- |
|
||||
| `deployment.type=controller` | Uses `pangolin-kube-controller` and Traefik CRDs. Recommended for Kubernetes deployments. |
|
||||
| `deployment.type=standalone` | Runs an internal Traefik workload managed by this chart. Mainly useful for labs and self-contained deployments. |
|
||||
| `deployment.mode=multi` | Runs Pangolin, Gerbil, and controller/Traefik components as separate workloads. Recommended for production. |
|
||||
| `deployment.mode=single` | Runs multiple components in one shared Pod. Useful only when you explicitly need a compact topology. |
|
||||
| `deployment.installTraefikController=true` | Installs the bundled Traefik dependency in controller mode. |
|
||||
| `deployment.traefikNamespace` | Namespace where Traefik controller resources live. Defaults to the release namespace when empty. |
|
||||
|
||||
<Note>
|
||||
In controller mode, Traefik CRDs and a Traefik controller must be available. You can install Traefik separately or enable the bundled Traefik dependency with `deployment.installTraefikController=true`.
|
||||
</Note>
|
||||
|
||||
If you enable the bundled Traefik dependency, put Traefik chart overrides under the `traefikController` key.
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Namespace and Pod Security Admission">
|
||||
|
||||
Namespace creation is controlled by the `namespace` block.
|
||||
|
||||
```yaml
|
||||
namespace:
|
||||
create: false
|
||||
name: ""
|
||||
labels: {}
|
||||
podSecurity:
|
||||
enforce: ""
|
||||
warn: ""
|
||||
audit: ""
|
||||
```
|
||||
|
||||
Recommended pattern:
|
||||
|
||||
1. Create the namespace manually.
|
||||
2. Apply the required labels and annotations.
|
||||
3. Install the chart into that namespace.
|
||||
|
||||
```bash
|
||||
kubectl create namespace pangolin
|
||||
```
|
||||
|
||||
Gerbil requires `NET_ADMIN` for WireGuard interface management. If your cluster enforces Pod Security Admission, the namespace must allow that capability.
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
kubectl label namespace pangolin \
|
||||
pod-security.kubernetes.io/enforce=privileged \
|
||||
--overwrite
|
||||
```
|
||||
|
||||
If you let the chart create the namespace, configure the Pod Security labels through values:
|
||||
|
||||
```yaml
|
||||
namespace:
|
||||
create: true
|
||||
name: pangolin
|
||||
podSecurity:
|
||||
enforce: privileged
|
||||
warn: baseline
|
||||
audit: restricted
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Do not apply a restricted Pod Security profile to a namespace running Gerbil unless you have validated WireGuard functionality. Gerbil requires `NET_ADMIN`; removing it breaks tunnel management.
|
||||
</Warning>
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Database modes">
|
||||
|
||||
Choose the database backend for Pangolin.
|
||||
|
||||
```yaml
|
||||
database:
|
||||
mode: cloudnativepg
|
||||
name: pangolin
|
||||
username: pangolin
|
||||
```
|
||||
|
||||
Supported modes:
|
||||
|
||||
| Mode | Use case |
|
||||
| --------------- | --------------------------------------------------------------------- |
|
||||
| `cloudnativepg` | Recommended production mode using CloudNativePG. This is the default. |
|
||||
| `external` | Production mode with an externally managed PostgreSQL database. |
|
||||
| `embedded` | Chart-managed PostgreSQL for labs and test environments. |
|
||||
| `sqlite` | Development or CI only. Not recommended for production. |
|
||||
|
||||
### CloudNativePG
|
||||
|
||||
The default database mode is `cloudnativepg`.
|
||||
|
||||
```yaml
|
||||
database:
|
||||
mode: cloudnativepg
|
||||
cloudnativepg:
|
||||
cluster:
|
||||
name: pangolin-db
|
||||
connection:
|
||||
database: pangolin
|
||||
username: pangolin
|
||||
sslMode: disable
|
||||
|
||||
cnpg-operator:
|
||||
enabled: false
|
||||
|
||||
cnpg-cluster:
|
||||
enabled: false
|
||||
fullnameOverride: pangolin-db
|
||||
```
|
||||
|
||||
CloudNativePG can be used in four common ways:
|
||||
|
||||
| Mode | Values |
|
||||
| -------------------------------------- | ----------------------------------------------------------- |
|
||||
| Existing operator and existing cluster | `cnpg-operator.enabled=false`, `cnpg-cluster.enabled=false` |
|
||||
| Chart installs operator only | `cnpg-operator.enabled=true`, `cnpg-cluster.enabled=false` |
|
||||
| Chart installs cluster only | `cnpg-operator.enabled=false`, `cnpg-cluster.enabled=true` |
|
||||
| Chart installs operator and cluster | `cnpg-operator.enabled=true`, `cnpg-cluster.enabled=true` |
|
||||
|
||||
When `cnpg-cluster.enabled=true`, keep the CNPG cluster name consistent:
|
||||
|
||||
```yaml
|
||||
database:
|
||||
cloudnativepg:
|
||||
cluster:
|
||||
name: pangolin-db
|
||||
|
||||
cnpg-cluster:
|
||||
enabled: true
|
||||
fullnameOverride: pangolin-db
|
||||
```
|
||||
|
||||
For the default CNPG cluster name `pangolin-db`, CloudNativePG creates an application Secret named `pangolin-db-app` with the key `uri`. The chart can automatically use this default Secret when no explicit `database.connection.existingSecretName` is set.
|
||||
|
||||
Explicit Secret reference:
|
||||
|
||||
```yaml
|
||||
database:
|
||||
connection:
|
||||
existingSecretName: pangolin-db-app
|
||||
existingSecretKey: uri
|
||||
```
|
||||
|
||||
### External PostgreSQL
|
||||
|
||||
For an external PostgreSQL database, prefer a Kubernetes Secret containing the final connection string.
|
||||
|
||||
```yaml
|
||||
database:
|
||||
mode: external
|
||||
connection:
|
||||
existingSecretName: pangolin-db-connection
|
||||
existingSecretKey: connectionString
|
||||
```
|
||||
|
||||
The Secret should contain a PostgreSQL connection string:
|
||||
|
||||
```bash
|
||||
kubectl create secret generic pangolin-db-connection \
|
||||
--namespace pangolin \
|
||||
--from-literal=connectionString='postgresql://pangolin:password@postgres.example.com:5432/pangolin?sslmode=require'
|
||||
```
|
||||
|
||||
You can also let the chart create a connection Secret from values:
|
||||
|
||||
```yaml
|
||||
database:
|
||||
mode: external
|
||||
external:
|
||||
generatedSecret:
|
||||
create: true
|
||||
host: postgres.example.com
|
||||
port: 5432
|
||||
database: pangolin
|
||||
username: pangolin
|
||||
password: "<password>"
|
||||
sslMode: require
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Avoid storing database passwords directly in values files for production. Use an existing Secret or your normal secret-management workflow.
|
||||
</Warning>
|
||||
|
||||
### Embedded PostgreSQL
|
||||
|
||||
Embedded PostgreSQL is intended for labs and tests.
|
||||
|
||||
```yaml
|
||||
database:
|
||||
mode: embedded
|
||||
embedded:
|
||||
persistence:
|
||||
enabled: true
|
||||
size: 8Gi
|
||||
```
|
||||
|
||||
### SQLite
|
||||
|
||||
SQLite is only suitable for development, CI, or very small test deployments.
|
||||
|
||||
```yaml
|
||||
database:
|
||||
mode: sqlite
|
||||
sqlite:
|
||||
persistence:
|
||||
enabled: true
|
||||
size: 1Gi
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Pangolin application config">
|
||||
|
||||
The `pangolin.config` block renders `/app/config/config.yml`.
|
||||
|
||||
```yaml
|
||||
pangolin:
|
||||
config:
|
||||
app:
|
||||
dashboard_url: "https://pangolin.example.com"
|
||||
log_level: info
|
||||
domains:
|
||||
domain1:
|
||||
base_domain: "example.com"
|
||||
cert_resolver: "letsencrypt"
|
||||
gerbil:
|
||||
start_port: 51820
|
||||
clients_start_port: 21820
|
||||
base_endpoint: "pangolin.example.com"
|
||||
traefik:
|
||||
enabled: true
|
||||
http_entrypoint: web
|
||||
https_entrypoint: websecure
|
||||
cert_resolver: letsencrypt
|
||||
```
|
||||
|
||||
Important settings:
|
||||
|
||||
| Setting | Description |
|
||||
| ------------------------------------------- | ------------------------------------------------------------------------------------------- |
|
||||
| `pangolin.config.app.dashboard_url` | Public dashboard URL. Set this to the real user-facing URL. |
|
||||
| `pangolin.config.domains` | Domain map used by Pangolin. Replace the default `example.com` entry before production use. |
|
||||
| `pangolin.config.gerbil.base_endpoint` | Public hostname or IP where Gerbil is reachable. |
|
||||
| `pangolin.config.gerbil.start_port` | First WireGuard site port. Keep this aligned with `gerbil.ports.wg1`. |
|
||||
| `pangolin.config.gerbil.clients_start_port` | Client WireGuard port. Keep this aligned with `gerbil.ports.wg2`. |
|
||||
| `pangolin.config.traefik.enabled` | Includes Pangolin's Traefik config section. This does not install Traefik. |
|
||||
| `pangolin.config.traefik.cert_resolver` | ACME resolver name used in Pangolin-generated Traefik configuration. |
|
||||
|
||||
<Note>
|
||||
`pangolin.config.traefik` controls the Traefik configuration generated by Pangolin. Traefik installation is controlled separately through controller mode, the bundled Traefik dependency, or standalone Traefik mode.
|
||||
</Note>
|
||||
|
||||
### Pangolin app secret
|
||||
|
||||
Pangolin requires `SERVER_SECRET`.
|
||||
|
||||
Use an existing Secret for production:
|
||||
|
||||
```yaml
|
||||
pangolin:
|
||||
secret:
|
||||
existingSecretName: pangolin-app-secret
|
||||
existingSecretKey: SERVER_SECRET
|
||||
```
|
||||
|
||||
Create the Secret:
|
||||
|
||||
```bash
|
||||
kubectl create secret generic pangolin-app-secret \
|
||||
--namespace pangolin \
|
||||
--from-literal=SERVER_SECRET='<strong-random-secret>'
|
||||
```
|
||||
|
||||
If no existing Secret is provided, the chart can generate one:
|
||||
|
||||
```yaml
|
||||
pangolin:
|
||||
secret:
|
||||
generated:
|
||||
create: true
|
||||
key: SERVER_SECRET
|
||||
length: 64
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Do not commit plaintext secrets to Git. For GitOps workflows, use SOPS, Sealed Secrets, External Secrets Operator, Vault, Infisical, or a cloud secret manager.
|
||||
</Warning>
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Dashboard IngressRoute">
|
||||
|
||||
In controller mode, the chart can render a Traefik `IngressRoute` for the Pangolin dashboard and API.
|
||||
|
||||
```yaml
|
||||
pangolin:
|
||||
ingressRoute:
|
||||
dashboard:
|
||||
enabled: true
|
||||
host: ""
|
||||
ingressClassName: ""
|
||||
traefikSelectorLabels: {}
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
api:
|
||||
enabled: true
|
||||
pathPrefix: /api/v1
|
||||
priority: 100
|
||||
dashboard:
|
||||
enabled: true
|
||||
priority: 10
|
||||
tls:
|
||||
enabled: true
|
||||
certResolver: ""
|
||||
secretName: ""
|
||||
```
|
||||
|
||||
Default routing behavior:
|
||||
|
||||
| Route | Match | Backend port |
|
||||
| --------- | ---------------------------------- | ------------------------------------------------- |
|
||||
| API | `Host(...) && PathPrefix(/api/v1)` | `pangolin.service.ports.external`, default `3000` |
|
||||
| Dashboard | `Host(...)` | `pangolin.service.ports.next`, default `3002` |
|
||||
|
||||
The host defaults to the hostname from `pangolin.config.app.dashboard_url`. You can override it with:
|
||||
|
||||
```yaml
|
||||
pangolin:
|
||||
ingressRoute:
|
||||
dashboard:
|
||||
host: pangolin.example.com
|
||||
```
|
||||
|
||||
### TLS with certResolver
|
||||
|
||||
```yaml
|
||||
pangolin:
|
||||
config:
|
||||
traefik:
|
||||
cert_resolver: letsencrypt
|
||||
ingressRoute:
|
||||
dashboard:
|
||||
tls:
|
||||
enabled: true
|
||||
certResolver: letsencrypt
|
||||
secretName: ""
|
||||
```
|
||||
|
||||
### TLS with existing Secret
|
||||
|
||||
```yaml
|
||||
pangolin:
|
||||
ingressRoute:
|
||||
dashboard:
|
||||
tls:
|
||||
enabled: true
|
||||
certResolver: ""
|
||||
secretName: pangolin-dashboard-tls
|
||||
```
|
||||
|
||||
<Warning>
|
||||
`tls.certResolver` and `tls.secretName` are mutually exclusive. Use one or the other.
|
||||
</Warning>
|
||||
|
||||
### Multi-Traefik setups
|
||||
|
||||
Use labels to target a specific Traefik CRD provider when multiple Traefik instances watch different label selectors:
|
||||
|
||||
```yaml
|
||||
pangolin:
|
||||
ingressRoute:
|
||||
dashboard:
|
||||
traefikSelectorLabels:
|
||||
traefik-instance: public
|
||||
```
|
||||
|
||||
You can also set an ingress class annotation:
|
||||
|
||||
```yaml
|
||||
pangolin:
|
||||
ingressRoute:
|
||||
dashboard:
|
||||
ingressClassName: traefik-public
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Gerbil">
|
||||
|
||||
Gerbil manages WireGuard tunnel connectivity for Pangolin.
|
||||
|
||||
```yaml
|
||||
gerbil:
|
||||
enabled: true
|
||||
startupMode: normal
|
||||
ports:
|
||||
wg1: 51820
|
||||
wg2: 21820
|
||||
internalApi: 3004
|
||||
service:
|
||||
enabled: true
|
||||
type: ClusterIP
|
||||
persistence:
|
||||
enabled: true
|
||||
size: 1Gi
|
||||
```
|
||||
|
||||
Important settings:
|
||||
|
||||
| Setting | Description |
|
||||
| ---------------------------- | ----------------------------------------------------------------------------------------- |
|
||||
| `gerbil.enabled` | Enables the Gerbil component. |
|
||||
| `gerbil.startupMode` | Controls first-run and normal startup behavior. |
|
||||
| `gerbil.ports.wg1` | First WireGuard UDP port. Keep aligned with `pangolin.config.gerbil.start_port`. |
|
||||
| `gerbil.ports.wg2` | Second WireGuard UDP port. Keep aligned with `pangolin.config.gerbil.clients_start_port`. |
|
||||
| `gerbil.ports.internalApi` | Internal Gerbil API/listener port. |
|
||||
| `gerbil.service.enabled` | Creates a Service for Gerbil UDP traffic. |
|
||||
| `gerbil.persistence.enabled` | Persists Gerbil key/config data. Recommended for production. |
|
||||
|
||||
<Info>
|
||||
If Gerbil is exposed through a reverse proxy or UDP gateway, keep proxy protocol settings aligned end-to-end. Do not enable proxy protocol on the upstream hop unless Gerbil is configured to accept it.
|
||||
</Info>
|
||||
|
||||
### Startup mode
|
||||
|
||||
```yaml
|
||||
gerbil:
|
||||
startupMode: delayed
|
||||
```
|
||||
|
||||
| Mode | Behavior |
|
||||
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `normal` | Starts Gerbil immediately. Use after Pangolin setup is complete. |
|
||||
| `delayed` | Renders Gerbil resources but keeps the Deployment at `replicas: 0` in multi mode. Useful for first installs and smoke tests. |
|
||||
| `disabledUntilSetup` | Does not render Gerbil resources until switched back to `normal` or `delayed`. |
|
||||
|
||||
For first installs, `delayed` can help when Gerbil would otherwise fail before the initial Pangolin setup is complete.
|
||||
|
||||
Switch back after setup:
|
||||
|
||||
```bash
|
||||
helm upgrade pangolin fossorial/pangolin \
|
||||
--namespace pangolin \
|
||||
--reuse-values \
|
||||
--set gerbil.startupMode=normal
|
||||
```
|
||||
|
||||
### Security
|
||||
|
||||
Gerbil requires `NET_ADMIN`.
|
||||
|
||||
```yaml
|
||||
gerbil:
|
||||
securityContext:
|
||||
runAsNonRoot: false
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: false
|
||||
capabilities:
|
||||
add:
|
||||
- NET_ADMIN
|
||||
drop:
|
||||
- ALL
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Do not remove `NET_ADMIN` from Gerbil. Without it, Gerbil cannot create or manage WireGuard interfaces. `SYS_MODULE` is not added by default and should only be added when your node kernel requires module loading from inside the container.
|
||||
</Warning>
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="NetworkPolicy">
|
||||
|
||||
NetworkPolicy rendering is enabled by default.
|
||||
|
||||
<Note>
|
||||
The chart-managed NetworkPolicies are intended to allow required Pangolin, Gerbil, database, DNS, and controller traffic for standard deployments.
|
||||
</Note>
|
||||
|
||||
```yaml
|
||||
networkPolicy:
|
||||
enabled: true
|
||||
allowExternalIngress: true
|
||||
allowExternalEgressHttps: false
|
||||
dns:
|
||||
enabled: true
|
||||
database:
|
||||
enabled: true
|
||||
port: 5432
|
||||
controller:
|
||||
egress:
|
||||
enabled: true
|
||||
kubernetesApi:
|
||||
enabled: true
|
||||
cidr: ""
|
||||
port: 443
|
||||
metrics:
|
||||
enabled: false
|
||||
gerbil:
|
||||
allowWireguardUdpEgress: true
|
||||
wireguardUdpCIDRs:
|
||||
- 0.0.0.0/0
|
||||
```
|
||||
|
||||
Important defaults:
|
||||
|
||||
| Setting | Default | Notes |
|
||||
| ------------------------------------------------------- | ------- | -------------------------------------------------------------------------------- |
|
||||
| `networkPolicy.enabled` | `true` | Renders NetworkPolicy resources. |
|
||||
| `networkPolicy.allowExternalIngress` | `true` | Allows public ingress to exposed services controlled by the chart. |
|
||||
| `networkPolicy.allowExternalEgressHttps` | `false` | Broad HTTPS egress is not allowed by default. Prefer scoped `extraEgress` rules. |
|
||||
| `networkPolicy.dns.enabled` | `true` | Allows DNS egress. |
|
||||
| `networkPolicy.database.enabled` | `true` | Adds database egress rules for Pangolin. |
|
||||
| `networkPolicy.controller.egress.kubernetesApi.enabled` | `true` | Allows controller API-server access when configured. |
|
||||
| `networkPolicy.gerbil.allowWireguardUdpEgress` | `true` | Allows Gerbil UDP egress for WireGuard peer traffic. |
|
||||
|
||||
When tightening policies, verify these paths:
|
||||
|
||||
* DNS egress
|
||||
* Pangolin to database
|
||||
* controller to Kubernetes API
|
||||
* ingress controller to Pangolin service
|
||||
* Gerbil UDP traffic
|
||||
* outbound access for SMTP, OIDC, webhooks, or other external integrations
|
||||
|
||||
Use component-scoped rules where possible:
|
||||
|
||||
```yaml
|
||||
networkPolicy:
|
||||
pangolin:
|
||||
extraEgress: []
|
||||
controller:
|
||||
extraEgress: []
|
||||
gerbil:
|
||||
extraEgress: []
|
||||
```
|
||||
|
||||
<Warning>
|
||||
If you disable or replace chart-managed NetworkPolicies, ensure your custom policies still allow all required traffic paths.
|
||||
</Warning>
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Monitoring">
|
||||
|
||||
The chart has chart-level monitoring settings for Pangolin and controller-specific monitoring settings for `pangolin-kube-controller`.
|
||||
|
||||
### Pangolin monitoring
|
||||
|
||||
```yaml
|
||||
monitoring:
|
||||
enabled: false
|
||||
service:
|
||||
enabled: false
|
||||
type: ClusterIP
|
||||
port: 9090
|
||||
portName: metrics
|
||||
metrics:
|
||||
targetPortName: metrics
|
||||
targetPort: 9090
|
||||
path: /metrics
|
||||
```
|
||||
|
||||
### Controller monitoring
|
||||
|
||||
```yaml
|
||||
controller:
|
||||
service:
|
||||
enabled: true
|
||||
port: 9090
|
||||
portName: metrics
|
||||
monitoring:
|
||||
serviceMonitor:
|
||||
enabled: false
|
||||
podMonitor:
|
||||
enabled: false
|
||||
prometheusRule:
|
||||
enabled: false
|
||||
```
|
||||
|
||||
Enable controller ServiceMonitor when Prometheus Operator is available:
|
||||
|
||||
```yaml
|
||||
controller:
|
||||
monitoring:
|
||||
serviceMonitor:
|
||||
enabled: true
|
||||
```
|
||||
|
||||
Enable chart-level metrics Service when the Pangolin app exposes metrics in your selected configuration:
|
||||
|
||||
```yaml
|
||||
monitoring:
|
||||
enabled: true
|
||||
service:
|
||||
enabled: true
|
||||
```
|
||||
|
||||
<Note>
|
||||
Only enable ServiceMonitor, PodMonitor, or PrometheusRule resources when the matching CRDs are installed in the cluster.
|
||||
</Note>
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="ServiceAccount and RBAC">
|
||||
|
||||
The chart uses separate ServiceAccounts for Pangolin, Gerbil, and the controller in multi mode.
|
||||
|
||||
```yaml
|
||||
serviceAccount:
|
||||
pangolin:
|
||||
create: true
|
||||
automountServiceAccountToken: false
|
||||
gerbil:
|
||||
create: true
|
||||
automountServiceAccountToken: false
|
||||
controller:
|
||||
create: true
|
||||
automountServiceAccountToken: true
|
||||
|
||||
rbac:
|
||||
create: true
|
||||
```
|
||||
|
||||
Default behavior:
|
||||
|
||||
| Component | API token mounted by default | Reason |
|
||||
| ---------- | ---------------------------- | ----------------------------------------------------------------------- |
|
||||
| Pangolin | No | The app does not need Kubernetes API access. |
|
||||
| Gerbil | No | Gerbil manages WireGuard and does not need Kubernetes API access. |
|
||||
| Controller | Yes | The controller reconciles Traefik CRDs and needs Kubernetes API access. |
|
||||
|
||||
<Note>
|
||||
In `deployment.mode=single` with `deployment.type=controller`, Kubernetes ServiceAccount selection is Pod-level. The shared Pod uses the controller ServiceAccount and token.
|
||||
</Note>
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Resources, scheduling, and images">
|
||||
|
||||
Global scheduling defaults:
|
||||
|
||||
```yaml
|
||||
global:
|
||||
storageClass: ""
|
||||
image:
|
||||
registry: docker.io
|
||||
imagePullPolicy: IfNotPresent
|
||||
imagePullSecrets: []
|
||||
nodeSelector: {}
|
||||
tolerations: []
|
||||
affinity: {}
|
||||
topologySpreadConstraints: []
|
||||
priorityClassName: ""
|
||||
```
|
||||
|
||||
Resource rendering policy:
|
||||
|
||||
```yaml
|
||||
resourcesPolicy:
|
||||
cpuLimits:
|
||||
enabled: true
|
||||
ephemeralStorage:
|
||||
enabled: false
|
||||
```
|
||||
|
||||
<Warning>
|
||||
CPU limits can cause throttling even when spare CPU exists on the node. For most deployments, start with CPU requests and memory limits, then add CPU limits only when explicitly required.
|
||||
</Warning>
|
||||
|
||||
Pangolin resources:
|
||||
|
||||
```yaml
|
||||
pangolin:
|
||||
resources:
|
||||
requests:
|
||||
cpu: 200m
|
||||
memory: 256Mi
|
||||
ephemeral-storage: 32Mi
|
||||
limits:
|
||||
cpu: 1000m
|
||||
memory: 1Gi
|
||||
ephemeral-storage: 256Mi
|
||||
```
|
||||
|
||||
Gerbil resources:
|
||||
|
||||
```yaml
|
||||
gerbil:
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
ephemeral-storage: 16Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
ephemeral-storage: 128Mi
|
||||
```
|
||||
|
||||
Controller resources:
|
||||
|
||||
```yaml
|
||||
controller:
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
ephemeral-storage: 16Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
ephemeral-storage: 128Mi
|
||||
```
|
||||
|
||||
Image configuration:
|
||||
|
||||
```yaml
|
||||
images:
|
||||
pangolin:
|
||||
registry: docker.io
|
||||
repository: fosrl/pangolin
|
||||
tag: ""
|
||||
digest: ""
|
||||
pangolinPostgresql:
|
||||
registry: docker.io
|
||||
repository: fosrl/pangolin
|
||||
tag: ""
|
||||
digest: ""
|
||||
gerbil:
|
||||
registry: docker.io
|
||||
repository: fosrl/gerbil
|
||||
tag: "1.3.1"
|
||||
digest: ""
|
||||
controller:
|
||||
registry: ghcr.io
|
||||
repository: fosrl/pangolin-kube-controller
|
||||
tag: "0.1.0-alpha.1"
|
||||
digest: ""
|
||||
traefik:
|
||||
registry: docker.io
|
||||
repository: traefik
|
||||
tag: v3.6.15
|
||||
digest: ""
|
||||
```
|
||||
|
||||
The chart automatically selects the PostgreSQL-capable Pangolin image variant for non-SQLite database modes unless you override the Pangolin tag or digest.
|
||||
|
||||
<Note>
|
||||
Ephemeral-storage requests and limits are only rendered when `resourcesPolicy.ephemeralStorage.enabled=true`.
|
||||
</Note>
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Standalone Traefik">
|
||||
|
||||
Standalone Traefik is used mainly when `deployment.type=standalone`.
|
||||
|
||||
```yaml
|
||||
traefik:
|
||||
enabled: false
|
||||
service:
|
||||
enabled: true
|
||||
type: LoadBalancer
|
||||
config:
|
||||
dashboard: false
|
||||
httpEntrypoint: web
|
||||
httpsEntrypoint: websecure
|
||||
certResolver: letsencrypt
|
||||
letsencryptEmail: ""
|
||||
persistence:
|
||||
enabled: false
|
||||
```
|
||||
|
||||
Important notes:
|
||||
|
||||
- `traefik.enabled=true` runs an internal Traefik workload managed by this chart.
|
||||
- `traefik.config.letsencryptEmail` is required when standalone Traefik is enabled.
|
||||
- If you enable the Traefik dashboard, enable `traefik.persistence.enabled` so ACME state survives restarts.
|
||||
- In controller mode, prefer using an existing or bundled Traefik controller instead of standalone Traefik.
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Blueprint storage">
|
||||
|
||||
The chart can store Pangolin Blueprint YAML files as Kubernetes ConfigMaps and Secrets.
|
||||
|
||||
```yaml
|
||||
pangolin:
|
||||
blueprints:
|
||||
enabled: false
|
||||
configMap:
|
||||
create: true
|
||||
files: {}
|
||||
environmentSecret:
|
||||
create: true
|
||||
existingConfigMap: ""
|
||||
existingEnvironmentSecret: ""
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```yaml
|
||||
pangolin:
|
||||
blueprints:
|
||||
enabled: true
|
||||
configMap:
|
||||
create: true
|
||||
files:
|
||||
site-blueprint.yaml: |
|
||||
sites:
|
||||
my-site:
|
||||
name: My Site
|
||||
public-resources:
|
||||
web-app:
|
||||
name: Web Application
|
||||
protocol: http
|
||||
full-domain: "app.example.com"
|
||||
targets:
|
||||
- site: my-site
|
||||
hostname: app
|
||||
port: 8080
|
||||
method: http
|
||||
```
|
||||
|
||||
Sensitive blueprint environment values should come from a Secret:
|
||||
|
||||
```yaml
|
||||
pangolin:
|
||||
blueprints:
|
||||
enabled: true
|
||||
existingConfigMap: my-blueprint-configmap
|
||||
existingEnvironmentSecret: my-blueprint-env
|
||||
```
|
||||
|
||||
<Warning>
|
||||
The Pangolin server does not apply Blueprint files directly. Blueprints are applied by Pangolin Sites through the Pangolin API using `--blueprint-file` or `--provisioning-blueprint-file`.
|
||||
</Warning>
|
||||
|
||||
</Accordion>
|
||||
|
||||
</AccordionGroup>
|
||||
|
||||
## Configuration by install method
|
||||
|
||||
### Helm
|
||||
|
||||
Use a values file:
|
||||
|
||||
```bash
|
||||
helm upgrade --install pangolin fossorial/pangolin \
|
||||
--namespace pangolin \
|
||||
--values values-pangolin.yaml
|
||||
```
|
||||
|
||||
Use inline values only for small tests:
|
||||
|
||||
```bash
|
||||
helm upgrade --install pangolin fossorial/pangolin \
|
||||
--namespace pangolin \
|
||||
--set deployment.type=controller \
|
||||
--set deployment.mode=multi \
|
||||
--set database.mode=cloudnativepg \
|
||||
--set pangolin.config.app.dashboard_url=https://pangolin.example.com
|
||||
```
|
||||
|
||||
See [Pangolin Helm](/self-host/manual/kubernetes/pangolin/helm) for the installation flow.
|
||||
|
||||
For complete application configuration keys and examples, see:
|
||||
|
||||
- [Public config file reference](/self-host/advanced/config-file)
|
||||
- [Private config file reference](/self-host/advanced/private-config-file)
|
||||
|
||||
### Kustomize
|
||||
|
||||
Render the chart with Helm, then apply Kustomize overlays:
|
||||
|
||||
```bash
|
||||
helm template pangolin fossorial/pangolin \
|
||||
--namespace pangolin \
|
||||
--values values-pangolin.yaml \
|
||||
> base/pangolin.yaml
|
||||
```
|
||||
|
||||
Apply the overlay:
|
||||
|
||||
```bash
|
||||
kubectl apply -k overlays/prod
|
||||
```
|
||||
|
||||
See [Pangolin Kustomize](/self-host/manual/kubernetes/pangolin/kustomize) for the Kustomize workflow.
|
||||
|
||||
### GitOps
|
||||
|
||||
Store Helm values or Kustomize overlays in Git. Argo CD or Flux reconciles the desired state.
|
||||
|
||||
Argo CD Helm example:
|
||||
|
||||
```yaml
|
||||
spec:
|
||||
source:
|
||||
helm:
|
||||
values: |
|
||||
deployment:
|
||||
type: controller
|
||||
mode: multi
|
||||
database:
|
||||
mode: cloudnativepg
|
||||
```
|
||||
|
||||
Flux HelmRelease example:
|
||||
|
||||
```yaml
|
||||
spec:
|
||||
values:
|
||||
deployment:
|
||||
type: controller
|
||||
mode: multi
|
||||
database:
|
||||
mode: cloudnativepg
|
||||
```
|
||||
|
||||
See [GitOps](/self-host/manual/kubernetes/gitops/overview) for GitOps guidance.
|
||||
|
||||
## Next steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Helm Install" href="/self-host/manual/kubernetes/pangolin/helm" icon="box">
|
||||
Install Pangolin with Helm.
|
||||
</Card>
|
||||
<Card title="Kustomize Install" href="/self-host/manual/kubernetes/pangolin/kustomize" icon="layer-group">
|
||||
Install Pangolin with rendered manifests and Kustomize overlays.
|
||||
</Card>
|
||||
<Card title="Troubleshooting" href="/self-host/manual/kubernetes/pangolin/troubleshooting" icon="circle-question">
|
||||
Debug Pangolin deployments on Kubernetes.
|
||||
</Card>
|
||||
<Card title="GitOps" href="/self-host/manual/kubernetes/gitops/overview" icon="code-branch">
|
||||
Deploy Pangolin with Argo CD or Flux.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,398 @@
|
||||
---
|
||||
title: "Helm"
|
||||
description: "Quick-start guide for installing Pangolin on Kubernetes using Helm."
|
||||
---
|
||||
|
||||
<Warning>
|
||||
The Pangolin Helm chart is currently alpha (`0.1.0-alpha.0`). Test installs and upgrades in a non-production environment before using the chart for production traffic.
|
||||
</Warning>
|
||||
|
||||
## What Pangolin deploys
|
||||
|
||||
The Pangolin Helm chart deploys the Pangolin control plane and related Kubernetes components.
|
||||
|
||||
Depending on the selected values, the chart can deploy:
|
||||
|
||||
- **Pangolin application**: dashboard, API, authentication, configuration, and application state.
|
||||
- **pangolin-kube-controller**: Kubernetes controller used in controller mode.
|
||||
- **Gerbil**: WireGuard tunnel manager used by the Pangolin tunnel stack.
|
||||
- **Traefik integration**: Traefik CRD-based routing in controller mode, bundled Traefik controller when enabled, or standalone Traefik mode.
|
||||
- **Database backend**: CloudNativePG, external PostgreSQL, embedded PostgreSQL, or SQLite.
|
||||
|
||||
See [Version Matrix](https://github.com/fosrl/helm-charts/VERSION_MATRIX.md) for chart and default app version references.
|
||||
|
||||
## Gerbil setup in the Pangolin chart
|
||||
|
||||
This chart deploys Gerbil when `gerbil.enabled=true`. This is the default when using `deployment.type=controller` and recommended.
|
||||
|
||||
<Info>
|
||||
If Gerbil is exposed through a reverse proxy or UDP gateway, keep proxy protocol settings aligned end-to-end. Do not enable proxy protocol on the upstream hop unless Gerbil is configured to accept it.
|
||||
</Info>
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before installing Pangolin, you need:
|
||||
|
||||
- Kubernetes `1.30.14` or newer.
|
||||
- Helm 3.x.
|
||||
- `kubectl` access to the target cluster.
|
||||
- A namespace prepared for the install.
|
||||
- A StorageClass if you use chart-managed persistent storage.
|
||||
- DNS records for the Pangolin dashboard and tunnel endpoint.
|
||||
- Traefik CRDs and a Traefik controller when using `deployment.type=controller`.
|
||||
- A database plan: CloudNativePG, external PostgreSQL, embedded PostgreSQL, or SQLite.
|
||||
|
||||
See [Prerequisites](/self-host/manual/kubernetes/prerequisites) for detailed cluster, namespace, storage, networking, and security requirements.
|
||||
|
||||
## Recommended quick install
|
||||
|
||||
This quick install uses:
|
||||
|
||||
- `deployment.type=controller`
|
||||
- `deployment.mode=multi`
|
||||
- `database.mode=cloudnativepg`
|
||||
- chart-managed CloudNativePG operator and cluster
|
||||
- chart-managed dashboard `IngressRoute`
|
||||
- Traefik cert resolver for TLS
|
||||
|
||||
<Note>
|
||||
This example assumes a Traefik controller is available and can process the chart-managed `IngressRoute`. If you want the chart to install the bundled Traefik controller, set `deployment.installTraefikController=true`.
|
||||
</Note>
|
||||
|
||||
### Step 1: Create the namespace
|
||||
|
||||
Create the namespace before installing the chart:
|
||||
|
||||
```bash
|
||||
kubectl create namespace pangolin
|
||||
```
|
||||
|
||||
Gerbil requires `NET_ADMIN` for WireGuard interface management. If your cluster enforces Pod Security Admission, label the namespace accordingly:
|
||||
|
||||
```bash
|
||||
kubectl label namespace pangolin \
|
||||
pod-security.kubernetes.io/enforce=privileged \
|
||||
pod-security.kubernetes.io/warn=baseline \
|
||||
pod-security.kubernetes.io/audit=restricted \
|
||||
--overwrite
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Do not use a restricted Pod Security profile for a namespace running Gerbil unless you have validated the selected chart mode. Gerbil requires `NET_ADMIN` for WireGuard.
|
||||
</Warning>
|
||||
|
||||
### Step 2: Create a Pangolin app secret
|
||||
|
||||
Create a Secret for `SERVER_SECRET`:
|
||||
|
||||
```bash
|
||||
kubectl create secret generic pangolin-app-secret \
|
||||
--namespace pangolin \
|
||||
--from-literal=SERVER_SECRET='<strong-random-secret>'
|
||||
```
|
||||
|
||||
Use a long random value. Do not commit this secret to Git.
|
||||
|
||||
### Step 3: Create a values file
|
||||
|
||||
Create `values-pangolin.yaml`:
|
||||
|
||||
```yaml
|
||||
deployment:
|
||||
type: controller
|
||||
mode: multi
|
||||
installTraefikController: false
|
||||
|
||||
database:
|
||||
mode: cloudnativepg
|
||||
cloudnativepg:
|
||||
cluster:
|
||||
name: pangolin-db
|
||||
|
||||
cnpg-operator:
|
||||
enabled: true
|
||||
|
||||
cnpg-cluster:
|
||||
enabled: true
|
||||
fullnameOverride: pangolin-db
|
||||
cluster:
|
||||
instances: 1
|
||||
storage:
|
||||
size: 8Gi
|
||||
|
||||
pangolin:
|
||||
secret:
|
||||
existingSecretName: pangolin-app-secret
|
||||
existingSecretKey: SERVER_SECRET
|
||||
|
||||
config:
|
||||
app:
|
||||
dashboard_url: https://pangolin.example.com
|
||||
domains:
|
||||
domain1:
|
||||
base_domain: example.com
|
||||
cert_resolver: letsencrypt
|
||||
gerbil:
|
||||
base_endpoint: vpn.example.com
|
||||
start_port: 51820
|
||||
clients_start_port: 21820
|
||||
traefik:
|
||||
enabled: true
|
||||
http_entrypoint: web
|
||||
https_entrypoint: websecure
|
||||
cert_resolver: letsencrypt
|
||||
|
||||
ingressRoute:
|
||||
dashboard:
|
||||
enabled: true
|
||||
host: pangolin.example.com
|
||||
entryPoints:
|
||||
- websecure
|
||||
tls:
|
||||
enabled: true
|
||||
certResolver: letsencrypt
|
||||
secretName: ""
|
||||
|
||||
gerbil:
|
||||
enabled: true
|
||||
startupMode: delayed
|
||||
persistence:
|
||||
enabled: true
|
||||
size: 1Gi
|
||||
```
|
||||
|
||||
Important points:
|
||||
|
||||
* Replace `pangolin.example.com`, `example.com`, and `vpn.example.com`.
|
||||
* Keep `pangolin.config.gerbil.start_port` aligned with `gerbil.ports.wg1`.
|
||||
* Keep `pangolin.config.gerbil.clients_start_port` aligned with `gerbil.ports.wg2`.
|
||||
* Use `gerbil.startupMode=delayed` for the first install if Gerbil should not start before the initial Pangolin setup is complete.
|
||||
|
||||
The chart defaults to `deployment.type=controller`, `deployment.mode=multi`, `database.mode=cloudnativepg`, and NetworkPolicy rendering enabled. Gerbil `startupMode` supports `normal`, `delayed`, and `disabledUntilSetup`. ([GitHub][1])
|
||||
|
||||
### Step 4: Install Pangolin
|
||||
|
||||
Add the Helm repository:
|
||||
|
||||
```bash
|
||||
helm repo add fossorial https://charts.fossorial.io
|
||||
helm repo update fossorial
|
||||
```
|
||||
|
||||
Install Pangolin:
|
||||
|
||||
```bash
|
||||
helm upgrade --install pangolin fossorial/pangolin \
|
||||
--namespace pangolin \
|
||||
--values values-pangolin.yaml
|
||||
```
|
||||
|
||||
Do not use `--create-namespace` here. The namespace was created and labeled before installation.
|
||||
|
||||
### Step 5: Verify the deployment
|
||||
|
||||
Check Helm release status:
|
||||
|
||||
```bash
|
||||
helm status pangolin --namespace pangolin
|
||||
helm history pangolin --namespace pangolin
|
||||
```
|
||||
|
||||
Check workloads:
|
||||
|
||||
```bash
|
||||
kubectl get pods --namespace pangolin
|
||||
kubectl get deploy,statefulset --namespace pangolin
|
||||
```
|
||||
|
||||
Check Services:
|
||||
|
||||
```bash
|
||||
kubectl get svc --namespace pangolin
|
||||
```
|
||||
|
||||
Check Traefik `IngressRoute` resources:
|
||||
|
||||
```bash
|
||||
kubectl get ingressroute --namespace pangolin
|
||||
```
|
||||
|
||||
If Traefik CRDs are not installed, this command will fail. In that case, install Traefik CRDs or enable/install the Traefik controller path required by your selected deployment mode.
|
||||
|
||||
Wait for the Pangolin pod to become ready:
|
||||
|
||||
```bash
|
||||
kubectl wait --for=condition=ready pod \
|
||||
-l app.kubernetes.io/name=pangolin \
|
||||
--namespace pangolin \
|
||||
--timeout=300s
|
||||
```
|
||||
|
||||
## Accessing the dashboard
|
||||
|
||||
After DNS and Traefik routing are configured, access Pangolin through the dashboard URL:
|
||||
|
||||
```text
|
||||
https://pangolin.example.com
|
||||
```
|
||||
|
||||
The API route is exposed under:
|
||||
|
||||
```text
|
||||
https://pangolin.example.com/api/v1
|
||||
```
|
||||
|
||||
<Tip>
|
||||
For a temporary local check, port-forward the dashboard/UI port:
|
||||
|
||||
```bash
|
||||
kubectl port-forward --namespace pangolin svc/pangolin 8080:3002
|
||||
```
|
||||
|
||||
Then open:
|
||||
|
||||
```text
|
||||
http://localhost:8080
|
||||
```
|
||||
|
||||
</Tip>
|
||||
|
||||
The chart routes `/api/v1` to the Pangolin external/API port and the dashboard route to the Next/UI port. The default service ports are `3000` for external/API and `3002` for the dashboard/UI. ([GitHub][1])
|
||||
|
||||
## Switch Gerbil to normal startup
|
||||
|
||||
If you installed with `gerbil.startupMode=delayed`, switch Gerbil to normal mode after the initial setup is complete:
|
||||
|
||||
```bash
|
||||
helm upgrade pangolin fossorial/pangolin \
|
||||
--namespace pangolin \
|
||||
--reuse-values \
|
||||
--set gerbil.startupMode=normal
|
||||
```
|
||||
|
||||
Check Gerbil resources:
|
||||
|
||||
```bash
|
||||
kubectl get pods,svc,pvc --namespace pangolin \
|
||||
-l app.kubernetes.io/name=gerbil
|
||||
```
|
||||
|
||||
## Upgrade
|
||||
|
||||
Update the Helm repository:
|
||||
|
||||
```bash
|
||||
helm repo update fossorial
|
||||
```
|
||||
|
||||
Upgrade the release:
|
||||
|
||||
```bash
|
||||
helm upgrade pangolin fossorial/pangolin \
|
||||
--namespace pangolin \
|
||||
--values values-pangolin.yaml
|
||||
```
|
||||
|
||||
Check upgrade status:
|
||||
|
||||
```bash
|
||||
helm status pangolin --namespace pangolin
|
||||
helm history pangolin --namespace pangolin
|
||||
```
|
||||
|
||||
Rollback if needed:
|
||||
|
||||
```bash
|
||||
helm rollback pangolin <revision> --namespace pangolin
|
||||
```
|
||||
|
||||
## OCI install
|
||||
|
||||
The Pangolin chart is also published as an OCI chart in GHCR.
|
||||
|
||||
Pull the chart:
|
||||
|
||||
```bash
|
||||
helm pull oci://ghcr.io/fosrl/helm-charts/pangolin \
|
||||
--version 0.1.0-alpha.0
|
||||
```
|
||||
|
||||
Install from OCI:
|
||||
|
||||
```bash
|
||||
helm upgrade --install pangolin oci://ghcr.io/fosrl/helm-charts/pangolin \
|
||||
--version 0.1.0-alpha.0 \
|
||||
--namespace pangolin \
|
||||
--values values-pangolin.yaml
|
||||
```
|
||||
|
||||
OCI changes where Helm pulls the chart from. It does not change the values file or the release behavior.
|
||||
|
||||
## Architecture overview
|
||||
|
||||
Recommended deployment mode:
|
||||
|
||||
```yaml
|
||||
deployment:
|
||||
type: controller
|
||||
mode: multi
|
||||
```
|
||||
|
||||
In this topology:
|
||||
|
||||
| Component | Role |
|
||||
| -------------------------- | -------------------------------------------------------------------- |
|
||||
| Pangolin | Main application, dashboard, API, authentication, and configuration. |
|
||||
| pangolin-kube-controller | Reconciles dynamic Kubernetes and Traefik CRD configuration. |
|
||||
| Gerbil | WireGuard tunnel manager for Pangolin sites. |
|
||||
| Traefik | Routes dashboard, API, and site traffic. |
|
||||
| CloudNativePG / PostgreSQL | Stores Pangolin application state. |
|
||||
|
||||
Database modes:
|
||||
|
||||
| Mode | Use case |
|
||||
| --------------- | --------------------------------------------------- |
|
||||
| `cloudnativepg` | Recommended Kubernetes production path. |
|
||||
| `external` | Production path with externally managed PostgreSQL. |
|
||||
| `embedded` | Lab or test setups. |
|
||||
| `sqlite` | Development or CI only. |
|
||||
|
||||
The chart supports `cloudnativepg`, `external`, `embedded`, and `sqlite` database modes. The chart comments mark `cloudnativepg` as the preferred production mode and SQLite as development/test only. ([GitHub][1])
|
||||
|
||||
## Chart signing
|
||||
|
||||
The chart metadata includes Artifact Hub signing information:
|
||||
|
||||
```text
|
||||
Fingerprint: 48E7F670FCC13645FC48B08D587294B228C2EC2C
|
||||
Public key: https://charts.fossorial.io/pgp_keys.asc
|
||||
```
|
||||
|
||||
Use this metadata when verifying signed chart releases. The signing key and fingerprint are published in the chart annotations. ([GitHub][2])
|
||||
|
||||
## References
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Chart README" href="https://github.com/fosrl/helm-charts/blob/main/charts/pangolin/README.md" icon="book" />
|
||||
<Card title="values.yaml" href="https://github.com/fosrl/helm-charts/blob/main/charts/pangolin/values.yaml" icon="file-code" />
|
||||
<Card title="values.schema.json" href="https://github.com/fosrl/helm-charts/blob/main/charts/pangolin/values.schema.json" icon="file-code" />
|
||||
<Card title="Examples" href="https://github.com/fosrl/helm-charts/tree/main/charts/pangolin/examples" icon="list-check" />
|
||||
<Card title="Issues" href="https://github.com/fosrl/helm-charts/issues" icon="circle-question" />
|
||||
</CardGroup>
|
||||
|
||||
## Next steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Full Configuration" href="/self-host/manual/kubernetes/pangolin/configuration" icon="sliders">
|
||||
Review Pangolin chart options.
|
||||
</Card>
|
||||
<Card title="Troubleshooting" href="/self-host/manual/kubernetes/pangolin/troubleshooting" icon="circle-question">
|
||||
Debug Pangolin deployment and routing issues.
|
||||
</Card>
|
||||
<Card title="Kustomize Install" href="/self-host/manual/kubernetes/pangolin/kustomize" icon="layer-group">
|
||||
Install Pangolin with rendered manifests and Kustomize overlays.
|
||||
</Card>
|
||||
<Card title="GitOps" href="/self-host/manual/kubernetes/gitops/overview" icon="code-branch">
|
||||
Deploy Pangolin with Argo CD or Flux.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,694 @@
|
||||
---
|
||||
title: "Kustomize"
|
||||
description: "Deploy Pangolin on Kubernetes using Helm-rendered manifests and Kustomize overlays."
|
||||
---
|
||||
|
||||
Use Kustomize when you want to manage Pangolin with rendered manifests, environment-specific overlays, and explicit patches in Git.
|
||||
|
||||
For Pangolin, the supported Kustomize workflow is:
|
||||
|
||||
1. Render the Pangolin Helm chart to manifests.
|
||||
2. Use the rendered output as the Kustomize base.
|
||||
3. Create overlays per environment.
|
||||
4. Apply the overlay with `kubectl apply -k` or reconcile it with Argo CD or Flux.
|
||||
|
||||
## When to use Kustomize for Pangolin
|
||||
|
||||
Use Kustomize if you:
|
||||
|
||||
- want environment-specific overlays for dev, staging, or production
|
||||
- need explicit patches committed to Git
|
||||
- prefer reviewing rendered Kubernetes manifests before applying them
|
||||
- use Argo CD or Flux with Kustomize sources
|
||||
- want to customize Helm-rendered output without forking the chart
|
||||
|
||||
For a simpler single-environment setup, use [Pangolin Helm](/self-host/manual/kubernetes/pangolin/helm).
|
||||
|
||||
## Version context
|
||||
|
||||
This page is aligned with the Pangolin Helm chart `0.1.0-alpha.0`.
|
||||
|
||||
| Item | Value |
|
||||
| --- | --- |
|
||||
| Chart version | `0.1.0-alpha.0` |
|
||||
| Pangolin app version | `1.18.2` |
|
||||
| Kubernetes version | `>=1.30.14-0` |
|
||||
| Gerbil image tag | `1.3.1` |
|
||||
| pangolin-kube-controller image tag | `0.1.0-alpha.1` |
|
||||
| Traefik image tag | `v3.6.15` |
|
||||
|
||||
## Supported approach
|
||||
|
||||
The Pangolin chart does not provide native Kustomize bases. Render the Helm chart first, then use Kustomize on the rendered manifests.
|
||||
|
||||
<Warning>
|
||||
Do not manage the same Pangolin resources with both a live Helm release and Kustomize. Pick one ownership model per environment.
|
||||
</Warning>
|
||||
|
||||
Recommended ownership model:
|
||||
|
||||
- Use Helm only to render the Pangolin chart.
|
||||
- Use Kustomize, Argo CD, or Flux to apply and reconcile the rendered manifests.
|
||||
- Re-render the base when upgrading the chart or changing Helm values.
|
||||
|
||||
## Example directory structure
|
||||
|
||||
```text
|
||||
pangolin-deployment/
|
||||
├── base/
|
||||
│ ├── kustomization.yaml
|
||||
│ └── pangolin.yaml
|
||||
├── overlays/
|
||||
│ ├── dev/
|
||||
│ │ ├── kustomization.yaml
|
||||
│ │ └── patches/
|
||||
│ │ └── pangolin-resources.patch.yaml
|
||||
│ ├── staging/
|
||||
│ │ ├── kustomization.yaml
|
||||
│ │ └── patches/
|
||||
│ │ └── pangolin-resources.patch.yaml
|
||||
│ └── prod/
|
||||
│ ├── kustomization.yaml
|
||||
│ └── patches/
|
||||
│ ├── pangolin-resources.patch.yaml
|
||||
│ └── ingressroute-host.patch.yaml
|
||||
└── values/
|
||||
├── values-base.yaml
|
||||
├── values-dev.yaml
|
||||
├── values-staging.yaml
|
||||
└── values-prod.yaml
|
||||
```
|
||||
|
||||
## Step 1: Create the namespace
|
||||
|
||||
Create the namespace before applying rendered manifests:
|
||||
|
||||
```bash
|
||||
kubectl create namespace pangolin
|
||||
```
|
||||
|
||||
Gerbil requires `NET_ADMIN` for WireGuard interface management. If your cluster enforces Pod Security Admission, label the namespace before creating workloads:
|
||||
|
||||
```bash
|
||||
kubectl label namespace pangolin \
|
||||
pod-security.kubernetes.io/enforce=privileged \
|
||||
pod-security.kubernetes.io/warn=baseline \
|
||||
pod-security.kubernetes.io/audit=restricted \
|
||||
--overwrite
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Do not use a restricted Pod Security profile for a namespace running Gerbil unless you have validated the selected chart mode. Gerbil requires `NET_ADMIN`.
|
||||
</Warning>
|
||||
|
||||
## Step 2: Create the Pangolin app Secret
|
||||
|
||||
Create a Secret for `SERVER_SECRET`:
|
||||
|
||||
```bash
|
||||
kubectl create secret generic pangolin-app-secret \
|
||||
--namespace pangolin \
|
||||
--from-literal=SERVER_SECRET='<strong-random-secret>'
|
||||
```
|
||||
|
||||
Do not commit this Secret to Git.
|
||||
|
||||
## Step 3: Create base values
|
||||
|
||||
Create `values/values-base.yaml`:
|
||||
|
||||
```yaml
|
||||
deployment:
|
||||
type: controller
|
||||
mode: multi
|
||||
installTraefikController: false
|
||||
|
||||
database:
|
||||
mode: cloudnativepg
|
||||
cloudnativepg:
|
||||
cluster:
|
||||
name: pangolin-db
|
||||
|
||||
cnpg-operator:
|
||||
enabled: true
|
||||
|
||||
cnpg-cluster:
|
||||
enabled: true
|
||||
fullnameOverride: pangolin-db
|
||||
cluster:
|
||||
instances: 1
|
||||
storage:
|
||||
size: 8Gi
|
||||
|
||||
pangolin:
|
||||
secret:
|
||||
existingSecretName: pangolin-app-secret
|
||||
existingSecretKey: SERVER_SECRET
|
||||
|
||||
config:
|
||||
app:
|
||||
dashboard_url: https://pangolin.example.com
|
||||
domains:
|
||||
domain1:
|
||||
base_domain: example.com
|
||||
cert_resolver: letsencrypt
|
||||
gerbil:
|
||||
base_endpoint: vpn.example.com
|
||||
start_port: 51820
|
||||
clients_start_port: 21820
|
||||
traefik:
|
||||
enabled: true
|
||||
http_entrypoint: web
|
||||
https_entrypoint: websecure
|
||||
cert_resolver: letsencrypt
|
||||
|
||||
ingressRoute:
|
||||
dashboard:
|
||||
enabled: true
|
||||
host: pangolin.example.com
|
||||
entryPoints:
|
||||
- websecure
|
||||
tls:
|
||||
enabled: true
|
||||
certResolver: letsencrypt
|
||||
secretName: ""
|
||||
|
||||
gerbil:
|
||||
enabled: true
|
||||
startupMode: delayed
|
||||
persistence:
|
||||
enabled: true
|
||||
size: 1Gi
|
||||
```
|
||||
|
||||
Replace:
|
||||
|
||||
* `pangolin.example.com`
|
||||
* `example.com`
|
||||
* `vpn.example.com`
|
||||
* TLS resolver names
|
||||
* storage settings
|
||||
|
||||
<Note>
|
||||
Use `gerbil.startupMode=delayed` for the first install if Gerbil should not start before the initial Pangolin setup is complete. Switch it to `normal` after setup.
|
||||
</Note>
|
||||
|
||||
## Step 4: Render Pangolin to the base
|
||||
|
||||
Add and update the Helm repository:
|
||||
|
||||
```bash
|
||||
helm repo add fossorial https://charts.fossorial.io
|
||||
helm repo update fossorial
|
||||
```
|
||||
|
||||
Create directories:
|
||||
|
||||
```bash
|
||||
mkdir -p base overlays/dev/patches overlays/staging/patches overlays/prod/patches values
|
||||
```
|
||||
|
||||
Render the Pangolin chart:
|
||||
|
||||
```bash
|
||||
helm template pangolin fossorial/pangolin \
|
||||
--namespace pangolin \
|
||||
--values values/values-base.yaml \
|
||||
> base/pangolin.yaml
|
||||
```
|
||||
|
||||
You can also render from the GHCR OCI chart:
|
||||
|
||||
```bash
|
||||
helm template pangolin oci://ghcr.io/fosrl/helm-charts/pangolin \
|
||||
--version 0.1.0-alpha.0 \
|
||||
--namespace pangolin \
|
||||
--values values/values-base.yaml \
|
||||
> base/pangolin.yaml
|
||||
```
|
||||
|
||||
## Step 5: Create the base kustomization
|
||||
|
||||
```yaml
|
||||
# base/kustomization.yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
resources:
|
||||
- pangolin.yaml
|
||||
```
|
||||
|
||||
<Note>
|
||||
The namespace is already rendered by Helm through `--namespace pangolin`. You can also set `namespace: pangolin` in Kustomize, but avoid changing namespaces in overlays unless you have verified all rendered resources and references.
|
||||
</Note>
|
||||
|
||||
## Step 6: Inspect rendered resource names
|
||||
|
||||
Before writing patches, inspect the generated resource names:
|
||||
|
||||
```bash
|
||||
kustomize build base | grep -E "^(kind:| name:)"
|
||||
```
|
||||
|
||||
Or list the main resource names with `yq`:
|
||||
|
||||
```bash
|
||||
kustomize build base | yq '. | select(.kind == "Deployment" or .kind == "StatefulSet" or .kind == "IngressRoute" or .kind == "Service") | .kind + " " + .metadata.name'
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Do not assume generated resource names. Helm names can change with the release name, chart name, `nameOverride`, or `fullnameOverride`.
|
||||
</Warning>
|
||||
|
||||
Use the actual rendered names in your patch targets.
|
||||
|
||||
## Step 7: Create a production overlay
|
||||
|
||||
Example `overlays/prod/kustomization.yaml`:
|
||||
|
||||
```yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
resources:
|
||||
- ../../base
|
||||
|
||||
labels:
|
||||
- pairs:
|
||||
app.kubernetes.io/environment: production
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
|
||||
patches:
|
||||
- path: patches/pangolin-resources.patch.yaml
|
||||
target:
|
||||
group: apps
|
||||
version: v1
|
||||
kind: Deployment
|
||||
name: pangolin
|
||||
|
||||
- path: patches/ingressroute-host.patch.yaml
|
||||
target:
|
||||
group: traefik.io
|
||||
version: v1alpha1
|
||||
kind: IngressRoute
|
||||
name: pangolin-dashboard
|
||||
```
|
||||
|
||||
<Note>
|
||||
Replace `pangolin` and `pangolin-dashboard` with the actual names from your rendered manifests.
|
||||
</Note>
|
||||
|
||||
## Step 8: Add patches
|
||||
|
||||
### Patch Pangolin resources
|
||||
|
||||
```yaml
|
||||
# overlays/prod/patches/pangolin-resources.patch.yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: pangolin
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: pangolin
|
||||
resources:
|
||||
requests:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
limits:
|
||||
memory: 1Gi
|
||||
```
|
||||
|
||||
<Note>
|
||||
CPU limits are rendered by default through the chart's `resourcesPolicy.cpuLimits.enabled=true`. If you disable CPU limits in chart values, keep your Kustomize patches consistent with that policy.
|
||||
</Note>
|
||||
|
||||
### Patch dashboard IngressRoute host
|
||||
|
||||
The Pangolin chart uses Traefik `IngressRoute` for the dashboard and API in controller mode, not a standard Kubernetes `Ingress`.
|
||||
|
||||
```yaml
|
||||
# overlays/prod/patches/ingressroute-host.patch.yaml
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: pangolin-dashboard
|
||||
spec:
|
||||
routes:
|
||||
- kind: Rule
|
||||
match: Host(`pangolin-prod.example.com`) && PathPrefix(`/api/v1`)
|
||||
- kind: Rule
|
||||
match: Host(`pangolin-prod.example.com`)
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Patch the rendered `IngressRoute` only after checking the route order and match rules. The API route and dashboard route target different service ports.
|
||||
</Warning>
|
||||
|
||||
### Patch node affinity
|
||||
|
||||
```yaml
|
||||
# overlays/prod/patches/pangolin-node-affinity.patch.yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: pangolin
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
affinity:
|
||||
nodeAffinity:
|
||||
requiredDuringSchedulingIgnoredDuringExecution:
|
||||
nodeSelectorTerms:
|
||||
- matchExpressions:
|
||||
- key: node-type
|
||||
operator: In
|
||||
values:
|
||||
- production
|
||||
```
|
||||
|
||||
Reference it in `overlays/prod/kustomization.yaml`:
|
||||
|
||||
```yaml
|
||||
patches:
|
||||
- path: patches/pangolin-node-affinity.patch.yaml
|
||||
target:
|
||||
group: apps
|
||||
version: v1
|
||||
kind: Deployment
|
||||
name: pangolin
|
||||
```
|
||||
|
||||
### Patch Gerbil startup mode
|
||||
|
||||
For first install, this should usually be handled in Helm values before rendering. If you still need to patch rendered manifests, inspect the generated Deployment first.
|
||||
|
||||
To switch Gerbil from delayed to normal mode, prefer updating values and re-rendering:
|
||||
|
||||
```yaml
|
||||
gerbil:
|
||||
startupMode: normal
|
||||
```
|
||||
|
||||
Then re-render:
|
||||
|
||||
```bash
|
||||
helm template pangolin fossorial/pangolin \
|
||||
--namespace pangolin \
|
||||
--values values/values-base.yaml \
|
||||
> base/pangolin.yaml
|
||||
```
|
||||
|
||||
## Do not rename rendered Helm resources by default
|
||||
|
||||
Avoid Kustomize options such as `namePrefix` and `nameSuffix` for Helm-rendered bases unless you have verified every generated reference.
|
||||
|
||||
Renaming rendered resources can break:
|
||||
|
||||
* Service selectors
|
||||
* Secret references
|
||||
* ConfigMap references
|
||||
* ServiceAccount references
|
||||
* NetworkPolicy selectors
|
||||
* Traefik `IngressRoute` service references
|
||||
* Prometheus monitor selectors
|
||||
* CloudNativePG references
|
||||
|
||||
If you need different resource names, prefer changing the Helm release name or chart naming values before rendering.
|
||||
|
||||
## Apply the overlay
|
||||
|
||||
Preview the rendered output:
|
||||
|
||||
```bash
|
||||
kustomize build overlays/prod
|
||||
```
|
||||
|
||||
Compare with the live cluster:
|
||||
|
||||
```bash
|
||||
kustomize build overlays/prod | kubectl diff -f -
|
||||
```
|
||||
|
||||
Apply the overlay:
|
||||
|
||||
```bash
|
||||
kubectl apply -k overlays/prod
|
||||
```
|
||||
|
||||
Verify workloads:
|
||||
|
||||
```bash
|
||||
kubectl get pods --namespace pangolin
|
||||
kubectl get deploy,statefulset --namespace pangolin
|
||||
kubectl get svc --namespace pangolin
|
||||
```
|
||||
|
||||
Verify Traefik resources:
|
||||
|
||||
```bash
|
||||
kubectl get ingressroute --namespace pangolin
|
||||
```
|
||||
|
||||
Check events:
|
||||
|
||||
```bash
|
||||
kubectl get events --namespace pangolin --sort-by=.lastTimestamp
|
||||
```
|
||||
|
||||
## Updating the rendered base
|
||||
|
||||
When upgrading the Pangolin chart or changing Helm values, re-render the base and review the changes.
|
||||
|
||||
Update the Helm repository:
|
||||
|
||||
```bash
|
||||
helm repo update fossorial
|
||||
```
|
||||
|
||||
Render the updated chart output:
|
||||
|
||||
```bash
|
||||
helm template pangolin fossorial/pangolin \
|
||||
--namespace pangolin \
|
||||
--values values/values-base.yaml \
|
||||
> base/pangolin.yaml
|
||||
```
|
||||
|
||||
Or with OCI:
|
||||
|
||||
```bash
|
||||
helm template pangolin oci://ghcr.io/fosrl/helm-charts/pangolin \
|
||||
--version 0.1.0-alpha.0 \
|
||||
--namespace pangolin \
|
||||
--values values/values-base.yaml \
|
||||
> base/pangolin.yaml
|
||||
```
|
||||
|
||||
Validate the overlay:
|
||||
|
||||
```bash
|
||||
kustomize build overlays/prod
|
||||
```
|
||||
|
||||
Review the diff:
|
||||
|
||||
```bash
|
||||
git diff
|
||||
kustomize build overlays/prod | kubectl diff -f -
|
||||
```
|
||||
|
||||
Commit the updated base and overlays:
|
||||
|
||||
```bash
|
||||
git add base/ overlays/ values/
|
||||
git commit -m "Update Pangolin rendered manifests"
|
||||
```
|
||||
|
||||
Apply after review:
|
||||
|
||||
```bash
|
||||
kubectl apply -k overlays/prod
|
||||
```
|
||||
|
||||
## Ownership model
|
||||
|
||||
Do not run `helm upgrade` against a release that is managed by Kustomize.
|
||||
|
||||
Avoid this pattern:
|
||||
|
||||
```bash
|
||||
helm upgrade pangolin fossorial/pangolin --namespace pangolin
|
||||
kubectl apply -k overlays/prod
|
||||
```
|
||||
|
||||
Use one of these models instead:
|
||||
|
||||
| Model | Description |
|
||||
| ----------------- | ------------------------------------------------------------------------------------------ |
|
||||
| Helm-managed | Helm installs and upgrades the live release. Kustomize is not used for the same resources. |
|
||||
| Kustomize-managed | Helm renders manifests only. Kustomize applies and owns the live resources. |
|
||||
| GitOps-managed | Argo CD or Flux applies the Kustomize overlay and owns reconciliation. |
|
||||
|
||||
## Common Kustomize patches for Pangolin
|
||||
|
||||
### Patch resource requests and limits
|
||||
|
||||
```yaml
|
||||
patches:
|
||||
- path: patches/pangolin-resources.patch.yaml
|
||||
target:
|
||||
group: apps
|
||||
version: v1
|
||||
kind: Deployment
|
||||
name: pangolin
|
||||
```
|
||||
|
||||
### Patch IngressRoute host
|
||||
|
||||
```yaml
|
||||
patches:
|
||||
- path: patches/ingressroute-host.patch.yaml
|
||||
target:
|
||||
group: traefik.io
|
||||
version: v1alpha1
|
||||
kind: IngressRoute
|
||||
name: pangolin-dashboard
|
||||
```
|
||||
|
||||
### Add annotations
|
||||
|
||||
```yaml
|
||||
patches:
|
||||
- target:
|
||||
group: apps
|
||||
version: v1
|
||||
kind: Deployment
|
||||
name: pangolin
|
||||
patch: |-
|
||||
- op: add
|
||||
path: /metadata/annotations
|
||||
value:
|
||||
example.com/owner: platform
|
||||
```
|
||||
|
||||
### Patch Gerbil Service type
|
||||
|
||||
Patch the Gerbil Service only after checking the rendered Service name.
|
||||
|
||||
```yaml
|
||||
patches:
|
||||
- target:
|
||||
version: v1
|
||||
kind: Service
|
||||
name: pangolin-gerbil
|
||||
patch: |-
|
||||
- op: replace
|
||||
path: /spec/type
|
||||
value: LoadBalancer
|
||||
```
|
||||
|
||||
<Note>
|
||||
For important topology settings such as database mode, Gerbil ports, `startupMode`, Traefik mode, and CloudNativePG settings, prefer changing Helm values and re-rendering instead of patching rendered YAML.
|
||||
</Note>
|
||||
|
||||
## Validation
|
||||
|
||||
Validate Kustomize output:
|
||||
|
||||
```bash
|
||||
kustomize build overlays/prod
|
||||
```
|
||||
|
||||
Run a server-side dry run:
|
||||
|
||||
```bash
|
||||
kustomize build overlays/prod | kubectl apply -f - --dry-run=server
|
||||
```
|
||||
|
||||
Preview live changes:
|
||||
|
||||
```bash
|
||||
kustomize build overlays/prod | kubectl diff -f -
|
||||
```
|
||||
|
||||
If a patch does not apply, inspect generated resource names:
|
||||
|
||||
```bash
|
||||
kustomize build base | grep -E "^(kind:| name:)"
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### The patch does not apply
|
||||
|
||||
Check the rendered resource name and kind:
|
||||
|
||||
```bash
|
||||
kustomize build base | grep -E "^(kind:| name:)"
|
||||
```
|
||||
|
||||
Then verify the patch target in your overlay.
|
||||
|
||||
### The pod does not start
|
||||
|
||||
Check pod status and events:
|
||||
|
||||
```bash
|
||||
kubectl get pods --namespace pangolin
|
||||
kubectl describe pod <pod-name> --namespace pangolin
|
||||
kubectl get events --namespace pangolin --sort-by=.lastTimestamp
|
||||
```
|
||||
|
||||
### Dashboard routing does not work
|
||||
|
||||
Check the rendered and applied `IngressRoute`:
|
||||
|
||||
```bash
|
||||
kubectl get ingressroute --namespace pangolin
|
||||
kubectl describe ingressroute <name> --namespace pangolin
|
||||
```
|
||||
|
||||
Verify:
|
||||
|
||||
* Traefik CRDs are installed.
|
||||
* A Traefik controller is watching the namespace and labels.
|
||||
* `pangolin.ingressRoute.dashboard.host` or the patched host matches DNS.
|
||||
* The API route still contains `PathPrefix(/api/v1)`.
|
||||
* TLS settings match your Traefik setup.
|
||||
|
||||
### Gerbil does not start
|
||||
|
||||
Check Gerbil resources:
|
||||
|
||||
```bash
|
||||
kubectl get pods,svc,pvc --namespace pangolin \
|
||||
-l app.kubernetes.io/name=gerbil
|
||||
```
|
||||
|
||||
Verify:
|
||||
|
||||
* namespace allows `NET_ADMIN`
|
||||
* `gerbil.startupMode` is set correctly
|
||||
* Gerbil persistence is enabled or intentionally disabled
|
||||
* `pangolin.config.gerbil.start_port` matches `gerbil.ports.wg1`
|
||||
* `pangolin.config.gerbil.clients_start_port` matches `gerbil.ports.wg2`
|
||||
|
||||
## Next steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Helm Install" href="/self-host/manual/kubernetes/pangolin/helm" icon="box">
|
||||
Install Pangolin with Helm.
|
||||
</Card>
|
||||
<Card title="Configuration" href="/self-host/manual/kubernetes/pangolin/configuration" icon="sliders">
|
||||
Review Pangolin chart options.
|
||||
</Card>
|
||||
<Card title="Troubleshooting" href="/self-host/manual/kubernetes/pangolin/troubleshooting" icon="circle-question">
|
||||
Debug Pangolin deployment and routing issues.
|
||||
</Card>
|
||||
<Card title="GitOps" href="/self-host/manual/kubernetes/gitops/overview" icon="code-branch">
|
||||
Deploy Pangolin with Argo CD or Flux.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,924 @@
|
||||
---
|
||||
title: "Troubleshooting"
|
||||
description: "Diagnose and resolve Pangolin Kubernetes deployment issues."
|
||||
---
|
||||
|
||||
This page covers troubleshooting Pangolin Kubernetes deployments across Helm, Kustomize, Argo CD, and Flux workflows.
|
||||
|
||||
Start with the core checks, then use the section that matches the symptom.
|
||||
|
||||
## Core diagnostics
|
||||
|
||||
Set the namespace and release name used by your installation:
|
||||
|
||||
```bash
|
||||
export PANGOLIN_NAMESPACE=pangolin
|
||||
export PANGOLIN_RELEASE=pangolin
|
||||
```
|
||||
|
||||
### Helm diagnostics
|
||||
|
||||
Check the release:
|
||||
|
||||
```bash
|
||||
helm status "$PANGOLIN_RELEASE" --namespace "$PANGOLIN_NAMESPACE"
|
||||
helm history "$PANGOLIN_RELEASE" --namespace "$PANGOLIN_NAMESPACE"
|
||||
helm get values "$PANGOLIN_RELEASE" --namespace "$PANGOLIN_NAMESPACE" --all
|
||||
```
|
||||
|
||||
Render the chart locally with your values file:
|
||||
|
||||
```bash
|
||||
helm repo update fossorial
|
||||
|
||||
helm template "$PANGOLIN_RELEASE" fossorial/pangolin \
|
||||
--namespace "$PANGOLIN_NAMESPACE" \
|
||||
--values values-pangolin.yaml
|
||||
```
|
||||
|
||||
Preview an upgrade:
|
||||
|
||||
```bash
|
||||
helm upgrade "$PANGOLIN_RELEASE" fossorial/pangolin \
|
||||
--namespace "$PANGOLIN_NAMESPACE" \
|
||||
--values values-pangolin.yaml \
|
||||
--dry-run
|
||||
```
|
||||
|
||||
<Note>
|
||||
`helm lint charts/pangolin` is only useful when you are working inside the Helm chart repository. For normal installs, use `helm template` and `helm upgrade --dry-run`.
|
||||
</Note>
|
||||
|
||||
### Kubernetes diagnostics
|
||||
|
||||
Check workloads and events:
|
||||
|
||||
```bash
|
||||
kubectl get pods --namespace "$PANGOLIN_NAMESPACE"
|
||||
kubectl get deploy,statefulset,job,cronjob --namespace "$PANGOLIN_NAMESPACE"
|
||||
kubectl get events --namespace "$PANGOLIN_NAMESPACE" --sort-by=.lastTimestamp
|
||||
```
|
||||
|
||||
Inspect a pod:
|
||||
|
||||
```bash
|
||||
kubectl describe pod <pod-name> --namespace "$PANGOLIN_NAMESPACE"
|
||||
kubectl logs <pod-name> --namespace "$PANGOLIN_NAMESPACE" --all-containers --tail=200
|
||||
```
|
||||
|
||||
Check services, PVCs, and policies:
|
||||
|
||||
```bash
|
||||
kubectl get svc,pvc,secret,configmap --namespace "$PANGOLIN_NAMESPACE"
|
||||
kubectl get networkpolicy --namespace "$PANGOLIN_NAMESPACE"
|
||||
```
|
||||
|
||||
### Traefik diagnostics
|
||||
|
||||
In controller mode, the chart uses Traefik CRDs such as `IngressRoute`.
|
||||
|
||||
Check whether Traefik CRDs are installed:
|
||||
|
||||
```bash
|
||||
kubectl get crd | grep traefik
|
||||
```
|
||||
|
||||
Check rendered or applied Traefik resources:
|
||||
|
||||
```bash
|
||||
kubectl get ingressroute --namespace "$PANGOLIN_NAMESPACE"
|
||||
kubectl describe ingressroute <name> --namespace "$PANGOLIN_NAMESPACE"
|
||||
```
|
||||
|
||||
Depending on your Traefik setup, also check:
|
||||
|
||||
```bash
|
||||
kubectl get middleware,tlsoption,traefikservice --namespace "$PANGOLIN_NAMESPACE"
|
||||
```
|
||||
|
||||
<Note>
|
||||
`kubectl get ingress` is only useful if your selected deployment mode renders standard Kubernetes Ingress resources. In controller mode, use `IngressRoute`.
|
||||
</Note>
|
||||
|
||||
### Database diagnostics
|
||||
|
||||
If you use CloudNativePG, first check that the CRD exists:
|
||||
|
||||
```bash
|
||||
kubectl get crd | grep postgresql.cnpg.io
|
||||
```
|
||||
|
||||
Then check CNPG resources:
|
||||
|
||||
```bash
|
||||
kubectl get cluster --namespace "$PANGOLIN_NAMESPACE"
|
||||
kubectl describe cluster <cluster-name> --namespace "$PANGOLIN_NAMESPACE"
|
||||
kubectl get pods --namespace "$PANGOLIN_NAMESPACE" | grep -E 'pangolin-db|postgres'
|
||||
kubectl get secret --namespace "$PANGOLIN_NAMESPACE" | grep -E 'pangolin-db|postgres'
|
||||
```
|
||||
|
||||
If you use external PostgreSQL, verify the connection Secret:
|
||||
|
||||
```bash
|
||||
kubectl get secret <connection-secret-name> --namespace "$PANGOLIN_NAMESPACE"
|
||||
kubectl describe secret <connection-secret-name> --namespace "$PANGOLIN_NAMESPACE"
|
||||
```
|
||||
|
||||
Do not decode and paste database credentials into logs, screenshots, or issue reports.
|
||||
|
||||
## Common issues and solutions
|
||||
|
||||
<AccordionGroup>
|
||||
|
||||
<Accordion title="Gerbil fails during the first install">
|
||||
|
||||
**Symptoms**
|
||||
|
||||
* Gerbil pod crashes during a fresh install.
|
||||
* Logs mention missing setup data, missing exit node, or tunnel configuration not being ready.
|
||||
* Pangolin itself is not initialized yet.
|
||||
|
||||
**Cause**
|
||||
|
||||
On first install, Gerbil may start before Pangolin has completed the initial setup. The chart supports `gerbil.startupMode` for this case.
|
||||
|
||||
**Resolution**
|
||||
|
||||
Use delayed startup for the first install:
|
||||
|
||||
```yaml
|
||||
gerbil:
|
||||
startupMode: delayed
|
||||
```
|
||||
|
||||
Install or upgrade with the values file:
|
||||
|
||||
```bash
|
||||
helm upgrade --install "$PANGOLIN_RELEASE" fossorial/pangolin \
|
||||
--namespace "$PANGOLIN_NAMESPACE" \
|
||||
--values values-pangolin.yaml
|
||||
```
|
||||
|
||||
After Pangolin setup is complete, switch Gerbil to normal startup:
|
||||
|
||||
```bash
|
||||
helm upgrade "$PANGOLIN_RELEASE" fossorial/pangolin \
|
||||
--namespace "$PANGOLIN_NAMESPACE" \
|
||||
--reuse-values \
|
||||
--set gerbil.startupMode=normal
|
||||
```
|
||||
|
||||
Check Gerbil resources:
|
||||
|
||||
```bash
|
||||
kubectl get pods,svc,pvc --namespace "$PANGOLIN_NAMESPACE" \
|
||||
-l app.kubernetes.io/name=gerbil
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Gerbil is blocked by Pod Security Admission">
|
||||
|
||||
**Symptoms**
|
||||
|
||||
* Gerbil pod does not start.
|
||||
* Events mention Pod Security Admission.
|
||||
* Events mention forbidden capabilities.
|
||||
* Logs or events mention `NET_ADMIN`.
|
||||
|
||||
**Cause**
|
||||
|
||||
Gerbil requires the `NET_ADMIN` Linux capability for WireGuard interface management. A namespace using a restricted Pod Security profile can block this.
|
||||
|
||||
**Resolution**
|
||||
|
||||
Check namespace labels:
|
||||
|
||||
```bash
|
||||
kubectl get namespace "$PANGOLIN_NAMESPACE" --show-labels
|
||||
```
|
||||
|
||||
For a namespace running Gerbil, use a policy profile that allows the required capability. Example:
|
||||
|
||||
```bash
|
||||
kubectl label namespace "$PANGOLIN_NAMESPACE" \
|
||||
pod-security.kubernetes.io/enforce=privileged \
|
||||
pod-security.kubernetes.io/warn=baseline \
|
||||
pod-security.kubernetes.io/audit=restricted \
|
||||
--overwrite
|
||||
```
|
||||
|
||||
Then restart the affected pods:
|
||||
|
||||
```bash
|
||||
kubectl rollout restart deploy --namespace "$PANGOLIN_NAMESPACE"
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Do not use a restricted Pod Security profile for Gerbil unless you have validated the selected chart mode and security context. Removing `NET_ADMIN` breaks WireGuard management.
|
||||
</Warning>
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Dashboard is not reachable through IngressRoute">
|
||||
|
||||
**Symptoms**
|
||||
|
||||
* The dashboard URL does not load.
|
||||
* Browser shows timeout, bad gateway, 404, or TLS error.
|
||||
* API path `/api/v1` fails while the dashboard path works, or the reverse.
|
||||
|
||||
**Common causes**
|
||||
|
||||
* DNS points to the wrong load balancer or ingress endpoint.
|
||||
* Traefik CRDs are missing.
|
||||
* Traefik controller is not watching the namespace or selector labels.
|
||||
* `IngressRoute` host does not match the dashboard URL.
|
||||
* API route was changed and no longer matches `PathPrefix(/api/v1)`.
|
||||
* TLS resolver or TLS Secret is misconfigured.
|
||||
|
||||
**Checks**
|
||||
|
||||
Check DNS:
|
||||
|
||||
```bash
|
||||
nslookup pangolin.example.com
|
||||
```
|
||||
|
||||
Check Traefik CRDs:
|
||||
|
||||
```bash
|
||||
kubectl get crd | grep traefik
|
||||
```
|
||||
|
||||
Check IngressRoute resources:
|
||||
|
||||
```bash
|
||||
kubectl get ingressroute --namespace "$PANGOLIN_NAMESPACE"
|
||||
kubectl describe ingressroute <name> --namespace "$PANGOLIN_NAMESPACE"
|
||||
```
|
||||
|
||||
Check the rendered values:
|
||||
|
||||
```bash
|
||||
helm get values "$PANGOLIN_RELEASE" --namespace "$PANGOLIN_NAMESPACE" --all | grep -A30 ingressRoute
|
||||
```
|
||||
|
||||
Check Traefik logs. Adjust the namespace and label selector to your Traefik installation:
|
||||
|
||||
```bash
|
||||
kubectl logs --namespace traefik -l app.kubernetes.io/name=traefik --tail=100
|
||||
```
|
||||
|
||||
Temporary local check for the dashboard/UI service port:
|
||||
|
||||
```bash
|
||||
kubectl port-forward --namespace "$PANGOLIN_NAMESPACE" svc/pangolin 8080:3002
|
||||
```
|
||||
|
||||
Then open:
|
||||
|
||||
```text
|
||||
http://localhost:8080
|
||||
```
|
||||
|
||||
<Note>
|
||||
The dashboard/UI port is `3002`. The API/external port is `3000`. Port-forward `3002` when checking the dashboard locally.
|
||||
</Note>
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="TLS certResolver and secretName conflict">
|
||||
|
||||
**Symptoms**
|
||||
|
||||
* `IngressRoute` is created but TLS does not work.
|
||||
* Traefik logs mention TLS configuration problems.
|
||||
* Certificate is not issued or the TLS Secret is not found.
|
||||
|
||||
**Cause**
|
||||
|
||||
The dashboard `IngressRoute` TLS configuration should use either a Traefik certificate resolver or an existing TLS Secret.
|
||||
|
||||
**Resolution**
|
||||
|
||||
Use Traefik ACME certificate resolver:
|
||||
|
||||
```yaml
|
||||
pangolin:
|
||||
ingressRoute:
|
||||
dashboard:
|
||||
tls:
|
||||
enabled: true
|
||||
certResolver: letsencrypt
|
||||
secretName: ""
|
||||
```
|
||||
|
||||
Or use an existing TLS Secret:
|
||||
|
||||
```yaml
|
||||
pangolin:
|
||||
ingressRoute:
|
||||
dashboard:
|
||||
tls:
|
||||
enabled: true
|
||||
certResolver: ""
|
||||
secretName: pangolin-dashboard-tls
|
||||
```
|
||||
|
||||
Verify the Secret if using `secretName`:
|
||||
|
||||
```bash
|
||||
kubectl get secret pangolin-dashboard-tls --namespace "$PANGOLIN_NAMESPACE"
|
||||
```
|
||||
|
||||
<Note>
|
||||
`certResolver` is a Traefik ACME resolver setting. It is not a cert-manager issuer reference.
|
||||
</Note>
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Newt cannot reach Gerbil WireGuard ports">
|
||||
|
||||
**Symptoms**
|
||||
|
||||
* Newt shows repeated connection or tunnel errors.
|
||||
* Tunnel traffic does not pass.
|
||||
* WireGuard UDP ports are unreachable from the Newt location.
|
||||
|
||||
**Common causes**
|
||||
|
||||
* `pangolin.config.gerbil.base_endpoint` points to the wrong host.
|
||||
* Gerbil Service is not exposed as expected.
|
||||
* External firewall blocks UDP traffic.
|
||||
* NetworkPolicy blocks the required traffic.
|
||||
* `pangolin.config.gerbil.start_port` and `gerbil.ports.wg1` are not aligned.
|
||||
* `pangolin.config.gerbil.clients_start_port` and `gerbil.ports.wg2` are not aligned.
|
||||
|
||||
**Checks**
|
||||
|
||||
Check Gerbil Service:
|
||||
|
||||
```bash
|
||||
kubectl get svc --namespace "$PANGOLIN_NAMESPACE" \
|
||||
-l app.kubernetes.io/name=gerbil
|
||||
|
||||
kubectl describe svc <gerbil-service-name> --namespace "$PANGOLIN_NAMESPACE"
|
||||
```
|
||||
|
||||
Check Gerbil values:
|
||||
|
||||
```bash
|
||||
helm get values "$PANGOLIN_RELEASE" --namespace "$PANGOLIN_NAMESPACE" --all | grep -A30 gerbil
|
||||
```
|
||||
|
||||
Check NetworkPolicies:
|
||||
|
||||
```bash
|
||||
kubectl get networkpolicy --namespace "$PANGOLIN_NAMESPACE"
|
||||
kubectl describe networkpolicy --namespace "$PANGOLIN_NAMESPACE"
|
||||
```
|
||||
|
||||
Verify external firewall rules for the configured UDP ports.
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Gerbil is behind reverse proxy or UDP gateway and tunnels fail">
|
||||
|
||||
**Symptoms**
|
||||
|
||||
* Newt peers do not establish stable handshakes.
|
||||
* Tunnel traffic drops even though Gerbil pods are healthy.
|
||||
* Logs show connection resets or malformed upstream traffic.
|
||||
|
||||
**Cause**
|
||||
|
||||
Proxy protocol handling is inconsistent between the upstream hop and Gerbil.
|
||||
|
||||
<Info>
|
||||
If Gerbil is exposed through a reverse proxy or UDP gateway, keep proxy protocol settings aligned end-to-end. Do not enable proxy protocol on the upstream hop unless Gerbil is configured to accept it.
|
||||
</Info>
|
||||
|
||||
**Checks**
|
||||
|
||||
Check endpoint and port alignment:
|
||||
|
||||
```bash
|
||||
helm get values "$PANGOLIN_RELEASE" --namespace "$PANGOLIN_NAMESPACE" --all | grep -A40 gerbil
|
||||
```
|
||||
|
||||
Check Gerbil logs:
|
||||
|
||||
```bash
|
||||
kubectl logs --namespace "$PANGOLIN_NAMESPACE" \
|
||||
-l app.kubernetes.io/name=gerbil \
|
||||
--tail=200
|
||||
```
|
||||
|
||||
Check Service exposure:
|
||||
|
||||
```bash
|
||||
kubectl get svc --namespace "$PANGOLIN_NAMESPACE" \
|
||||
-l app.kubernetes.io/name=gerbil -o wide
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="External database mode missing or invalid Secret">
|
||||
|
||||
**Symptoms**
|
||||
|
||||
* Pangolin pod crashes.
|
||||
* Logs mention database connection errors.
|
||||
* Events mention missing Secret or missing Secret key.
|
||||
|
||||
**Cause**
|
||||
|
||||
`database.mode=external` needs a valid database connection Secret unless the chart is configured to generate one from values.
|
||||
|
||||
**Resolution**
|
||||
|
||||
Create a connection Secret:
|
||||
|
||||
```bash
|
||||
kubectl create secret generic pangolin-db-connection \
|
||||
--namespace "$PANGOLIN_NAMESPACE" \
|
||||
--from-literal=connectionString='postgresql://pangolin:password@postgres.example.com:5432/pangolin?sslmode=require'
|
||||
```
|
||||
|
||||
Reference it in values:
|
||||
|
||||
```yaml
|
||||
database:
|
||||
mode: external
|
||||
connection:
|
||||
existingSecretName: pangolin-db-connection
|
||||
existingSecretKey: connectionString
|
||||
```
|
||||
|
||||
Check the Secret:
|
||||
|
||||
```bash
|
||||
kubectl describe secret pangolin-db-connection --namespace "$PANGOLIN_NAMESPACE"
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Do not put database passwords directly in values files for production. Use an existing Secret or your normal secret-management workflow.
|
||||
</Warning>
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="CloudNativePG cluster does not provision">
|
||||
|
||||
**Symptoms**
|
||||
|
||||
* CNPG Cluster resource is missing.
|
||||
* CNPG pods do not start.
|
||||
* Pangolin cannot connect to the generated CNPG database.
|
||||
* Secret such as `pangolin-db-app` is missing.
|
||||
|
||||
**Common causes**
|
||||
|
||||
* CloudNativePG CRDs/operator are not installed.
|
||||
* `cnpg-cluster.enabled` is false when you expected the chart to create a cluster.
|
||||
* `cnpg-operator.enabled` is false and no operator exists.
|
||||
* `database.cloudnativepg.cluster.name` does not match the CNPG cluster name.
|
||||
* StorageClass or PVC provisioning fails.
|
||||
|
||||
**Checks**
|
||||
|
||||
Check CRDs:
|
||||
|
||||
```bash
|
||||
kubectl get crd | grep postgresql.cnpg.io
|
||||
```
|
||||
|
||||
Check CNPG operator pods:
|
||||
|
||||
```bash
|
||||
kubectl get pods --all-namespaces | grep -i cnpg
|
||||
```
|
||||
|
||||
Check CNPG Cluster:
|
||||
|
||||
```bash
|
||||
kubectl get cluster --namespace "$PANGOLIN_NAMESPACE"
|
||||
kubectl describe cluster pangolin-db --namespace "$PANGOLIN_NAMESPACE"
|
||||
```
|
||||
|
||||
Check PVCs and Secrets:
|
||||
|
||||
```bash
|
||||
kubectl get pvc --namespace "$PANGOLIN_NAMESPACE"
|
||||
kubectl get secret --namespace "$PANGOLIN_NAMESPACE" | grep pangolin-db
|
||||
```
|
||||
|
||||
Expected naming when using the default example:
|
||||
|
||||
```yaml
|
||||
database:
|
||||
cloudnativepg:
|
||||
cluster:
|
||||
name: pangolin-db
|
||||
|
||||
cnpg-cluster:
|
||||
enabled: true
|
||||
fullnameOverride: pangolin-db
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="NetworkPolicy blocks DNS, database, controller, or tunnel traffic">
|
||||
|
||||
**Symptoms**
|
||||
|
||||
* DNS lookups fail.
|
||||
* Pangolin cannot connect to the database.
|
||||
* Controller cannot reach the Kubernetes API.
|
||||
* Gerbil or Newt traffic does not work.
|
||||
* External services such as SMTP, OIDC, or webhooks time out.
|
||||
|
||||
**Cause**
|
||||
|
||||
The chart can render NetworkPolicies. If your CNI enforces them, missing egress or ingress rules can break required paths.
|
||||
|
||||
**Checks**
|
||||
|
||||
```bash
|
||||
kubectl get networkpolicy --namespace "$PANGOLIN_NAMESPACE"
|
||||
kubectl describe networkpolicy --namespace "$PANGOLIN_NAMESPACE"
|
||||
```
|
||||
|
||||
Check whether DNS is allowed:
|
||||
|
||||
```yaml
|
||||
networkPolicy:
|
||||
dns:
|
||||
enabled: true
|
||||
```
|
||||
|
||||
Check database egress:
|
||||
|
||||
```yaml
|
||||
networkPolicy:
|
||||
database:
|
||||
enabled: true
|
||||
port: 5432
|
||||
```
|
||||
|
||||
Check controller API access:
|
||||
|
||||
```yaml
|
||||
networkPolicy:
|
||||
controller:
|
||||
egress:
|
||||
enabled: true
|
||||
kubernetesApi:
|
||||
enabled: true
|
||||
port: 443
|
||||
```
|
||||
|
||||
For external integrations, add scoped egress rules for the required services instead of allowing broad egress.
|
||||
|
||||
For a temporary isolation test, disable NetworkPolicy and re-apply:
|
||||
|
||||
```yaml
|
||||
networkPolicy:
|
||||
enabled: false
|
||||
```
|
||||
|
||||
If this fixes the issue, re-enable policies and add the missing rules.
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Pangolin pod is CrashLoopBackOff or Pending">
|
||||
|
||||
**Symptoms**
|
||||
|
||||
* Pangolin pod restarts repeatedly.
|
||||
* Pod stays Pending.
|
||||
* Readiness never becomes true.
|
||||
|
||||
**Checks**
|
||||
|
||||
Find the pod:
|
||||
|
||||
```bash
|
||||
kubectl get pods --namespace "$PANGOLIN_NAMESPACE" \
|
||||
-l app.kubernetes.io/name=pangolin
|
||||
```
|
||||
|
||||
Inspect it:
|
||||
|
||||
```bash
|
||||
kubectl describe pod <pod-name> --namespace "$PANGOLIN_NAMESPACE"
|
||||
kubectl logs <pod-name> --namespace "$PANGOLIN_NAMESPACE" --tail=200
|
||||
kubectl logs <pod-name> --namespace "$PANGOLIN_NAMESPACE" --previous --tail=200
|
||||
```
|
||||
|
||||
Check PVCs:
|
||||
|
||||
```bash
|
||||
kubectl get pvc --namespace "$PANGOLIN_NAMESPACE"
|
||||
kubectl describe pvc <pvc-name> --namespace "$PANGOLIN_NAMESPACE"
|
||||
```
|
||||
|
||||
Common causes:
|
||||
|
||||
| Status | Common causes |
|
||||
| ------------------ | ----------------------------------------------------------------------------------------------------- |
|
||||
| `CrashLoopBackOff` | Database connection issue, missing Secret, invalid config, startup dependency not ready |
|
||||
| `Pending` | PVC not bound, insufficient resources, node selector/affinity mismatch, Pod Security policy rejection |
|
||||
| `ImagePullBackOff` | Wrong image override, registry access issue, missing imagePullSecret |
|
||||
|
||||
<Note>
|
||||
Do not assume tools such as `psql`, `curl`, or `dig` are available inside the Pangolin container. Use logs, Events, or a temporary debug pod when needed.
|
||||
</Note>
|
||||
|
||||
Run a temporary debug pod for network tests:
|
||||
|
||||
```bash
|
||||
kubectl run net-debug \
|
||||
--namespace "$PANGOLIN_NAMESPACE" \
|
||||
--rm -it \
|
||||
--image=curlimages/curl:latest \
|
||||
--restart=Never \
|
||||
-- sh
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Traefik CRDs or resources are missing">
|
||||
|
||||
**Symptoms**
|
||||
|
||||
* Helm template or install succeeds, but Traefik resources are not reconciled.
|
||||
* `kubectl get ingressroute` fails with unknown resource type.
|
||||
* Argo CD or Flux reports missing kind `IngressRoute`.
|
||||
|
||||
**Cause**
|
||||
|
||||
Controller mode expects Traefik CRDs and a Traefik controller. They must be installed separately or through the bundled dependency when enabled.
|
||||
|
||||
**Checks**
|
||||
|
||||
```bash
|
||||
kubectl get crd | grep traefik
|
||||
kubectl get pods --all-namespaces | grep -i traefik
|
||||
```
|
||||
|
||||
If you want the chart to install the bundled Traefik controller, enable it:
|
||||
|
||||
```yaml
|
||||
deployment:
|
||||
type: controller
|
||||
installTraefikController: true
|
||||
```
|
||||
|
||||
If Traefik is already installed elsewhere, keep it disabled and make sure the controller watches the namespace and labels used by the Pangolin `IngressRoute`.
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Helm upgrade fails or rendered output is unexpected">
|
||||
|
||||
**Symptoms**
|
||||
|
||||
* `helm upgrade` fails.
|
||||
* Rendered resources changed unexpectedly.
|
||||
* Existing resources conflict with chart-managed resources.
|
||||
* GitOps reports immutable field changes or ownership conflicts.
|
||||
|
||||
**Checks**
|
||||
|
||||
Render before upgrading:
|
||||
|
||||
```bash
|
||||
helm template "$PANGOLIN_RELEASE" fossorial/pangolin \
|
||||
--namespace "$PANGOLIN_NAMESPACE" \
|
||||
--values values-pangolin.yaml > rendered.yaml
|
||||
```
|
||||
|
||||
Run a server-side dry run:
|
||||
|
||||
```bash
|
||||
kubectl apply -f rendered.yaml --dry-run=server
|
||||
```
|
||||
|
||||
Compare the current live release:
|
||||
|
||||
```bash
|
||||
helm get manifest "$PANGOLIN_RELEASE" --namespace "$PANGOLIN_NAMESPACE" > live-release.yaml
|
||||
diff -u live-release.yaml rendered.yaml
|
||||
```
|
||||
|
||||
Check ownership conflicts:
|
||||
|
||||
```bash
|
||||
kubectl get all --namespace "$PANGOLIN_NAMESPACE" -o yaml | grep -E "meta.helm.sh|app.kubernetes.io/managed-by"
|
||||
```
|
||||
|
||||
Avoid `--force` unless you understand which resources will be recreated.
|
||||
|
||||
<Warning>
|
||||
`helm upgrade --force` can delete and recreate resources. That can interrupt traffic and may affect persistent workloads depending on the resource type.
|
||||
</Warning>
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Kustomize patches do not apply">
|
||||
|
||||
**Symptoms**
|
||||
|
||||
* Kustomize build succeeds but changes are missing.
|
||||
* Patch target does not match any resource.
|
||||
* Patch breaks after chart upgrade.
|
||||
|
||||
**Checks**
|
||||
|
||||
List generated resource names:
|
||||
|
||||
```bash
|
||||
kustomize build base | grep -E "^(kind:| name:)"
|
||||
```
|
||||
|
||||
Validate the overlay:
|
||||
|
||||
```bash
|
||||
kustomize build overlays/prod
|
||||
```
|
||||
|
||||
Run a server-side dry run:
|
||||
|
||||
```bash
|
||||
kustomize build overlays/prod | kubectl apply -f - --dry-run=server
|
||||
```
|
||||
|
||||
Preview live changes:
|
||||
|
||||
```bash
|
||||
kustomize build overlays/prod | kubectl diff -f -
|
||||
```
|
||||
|
||||
Use modern Kustomize `patches` syntax:
|
||||
|
||||
```yaml
|
||||
patches:
|
||||
- path: patches/pangolin-resources.patch.yaml
|
||||
target:
|
||||
group: apps
|
||||
version: v1
|
||||
kind: Deployment
|
||||
name: pangolin
|
||||
```
|
||||
|
||||
<Note>
|
||||
For Helm-rendered bases, do not assume resource names. Check the rendered manifests after each chart upgrade.
|
||||
</Note>
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="GitOps deployment does not sync">
|
||||
|
||||
**Symptoms**
|
||||
|
||||
* Argo CD Application is OutOfSync or Degraded.
|
||||
* Flux HelmRelease or Kustomization is not Ready.
|
||||
* Resources are missing or constantly reverted.
|
||||
|
||||
**Argo CD checks**
|
||||
|
||||
```bash
|
||||
kubectl describe application pangolin --namespace argocd
|
||||
kubectl logs --namespace argocd deployment/argocd-application-controller --tail=100
|
||||
argocd app diff pangolin
|
||||
argocd app sync pangolin
|
||||
```
|
||||
|
||||
**Flux checks**
|
||||
|
||||
```bash
|
||||
flux get sources all --all-namespaces
|
||||
flux get helmreleases --all-namespaces
|
||||
flux get kustomizations --all-namespaces
|
||||
flux logs --all-namespaces --follow
|
||||
```
|
||||
|
||||
Reconcile manually:
|
||||
|
||||
```bash
|
||||
flux reconcile helmrelease pangolin --namespace "$PANGOLIN_NAMESPACE"
|
||||
flux reconcile kustomization pangolin --namespace flux-system
|
||||
```
|
||||
|
||||
Common causes:
|
||||
|
||||
- chart repository or OCI source not reachable
|
||||
- wrong chart version
|
||||
- missing CRDs
|
||||
- invalid values
|
||||
- rendered resource ownership conflict
|
||||
- Secret not available in the expected namespace
|
||||
|
||||
</Accordion>
|
||||
|
||||
</AccordionGroup>
|
||||
|
||||
## Routing issues to the right repository
|
||||
|
||||
Use the repository that matches the failing area:
|
||||
|
||||
| Area | Repository |
|
||||
| ----------------------------------------------------- | ------------------- |
|
||||
| Chart templates, values, examples, rendered manifests | `fosrl/helm-charts` |
|
||||
| Pangolin runtime, API, UI, auth, application behavior | `fosrl/pangolin` |
|
||||
| Newt client behavior or connectivity | `fosrl/newt` |
|
||||
| Documentation | `fosrl/docs-v2` |
|
||||
|
||||
## Before opening an issue, collect
|
||||
|
||||
Collect this information before opening an issue:
|
||||
|
||||
* chart version
|
||||
* Pangolin app version
|
||||
* Kubernetes version
|
||||
* Helm version
|
||||
* deployment method: Helm, Kustomize, Argo CD, or Flux
|
||||
* sanitized values file
|
||||
* pod logs
|
||||
* namespace events
|
||||
* Traefik logs, if routing is involved
|
||||
* rendered manifests from `helm template` or `kustomize build`
|
||||
* Helm release status or GitOps sync status
|
||||
* reproduction steps
|
||||
|
||||
Collect basic diagnostics:
|
||||
|
||||
```bash
|
||||
kubectl version
|
||||
helm version
|
||||
|
||||
helm status "$PANGOLIN_RELEASE" --namespace "$PANGOLIN_NAMESPACE"
|
||||
helm get values "$PANGOLIN_RELEASE" --namespace "$PANGOLIN_NAMESPACE" --all > pangolin-values.yaml
|
||||
helm get manifest "$PANGOLIN_RELEASE" --namespace "$PANGOLIN_NAMESPACE" > pangolin-manifest.yaml
|
||||
|
||||
kubectl get pods --namespace "$PANGOLIN_NAMESPACE" -o wide > pangolin-pods.txt
|
||||
kubectl get events --namespace "$PANGOLIN_NAMESPACE" --sort-by=.lastTimestamp > pangolin-events.txt
|
||||
```
|
||||
|
||||
Before sharing diagnostics, remove:
|
||||
|
||||
* database passwords
|
||||
* `SERVER_SECRET`
|
||||
* API keys
|
||||
* OAuth/OIDC client secrets
|
||||
* TLS private keys
|
||||
* internal hostnames, if sensitive
|
||||
|
||||
## Useful command reference
|
||||
|
||||
```bash
|
||||
# General cluster info
|
||||
kubectl cluster-info
|
||||
kubectl version
|
||||
|
||||
# Namespace overview
|
||||
kubectl get all --namespace "$PANGOLIN_NAMESPACE"
|
||||
kubectl get pvc,secret,configmap --namespace "$PANGOLIN_NAMESPACE"
|
||||
kubectl get events --namespace "$PANGOLIN_NAMESPACE" --sort-by=.lastTimestamp
|
||||
|
||||
# Logs
|
||||
kubectl logs --namespace "$PANGOLIN_NAMESPACE" \
|
||||
-l app.kubernetes.io/name=pangolin \
|
||||
--tail=200
|
||||
|
||||
kubectl logs --namespace "$PANGOLIN_NAMESPACE" \
|
||||
-l app.kubernetes.io/name=gerbil \
|
||||
--tail=200
|
||||
|
||||
# Dashboard local test
|
||||
kubectl port-forward --namespace "$PANGOLIN_NAMESPACE" svc/pangolin 8080:3002
|
||||
|
||||
# Traefik resources
|
||||
kubectl get ingressroute --namespace "$PANGOLIN_NAMESPACE"
|
||||
|
||||
# Resource usage
|
||||
kubectl top pod --namespace "$PANGOLIN_NAMESPACE"
|
||||
kubectl top node
|
||||
```
|
||||
|
||||
## Next steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Pangolin Configuration" href="/self-host/manual/kubernetes/pangolin/configuration" icon="sliders">
|
||||
Review Pangolin chart options.
|
||||
</Card>
|
||||
<Card title="Helm Quick-Start" href="/self-host/manual/kubernetes/pangolin/helm" icon="box">
|
||||
Install Pangolin with Helm.
|
||||
</Card>
|
||||
<Card title="Kustomize Quick-Start" href="/self-host/manual/kubernetes/pangolin/kustomize" icon="layer-group">
|
||||
Install Pangolin with rendered manifests and Kustomize overlays.
|
||||
</Card>
|
||||
<Card title="GitOps Overview" href="/self-host/manual/kubernetes/gitops/overview" icon="code-branch">
|
||||
Deploy Pangolin with Argo CD or Flux.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,221 @@
|
||||
---
|
||||
title: "Prerequisites"
|
||||
description: "Cluster, tooling, networking, and storage requirements for deploying Pangolin and Sites (Newt) on Kubernetes."
|
||||
---
|
||||
|
||||
Before installing Pangolin or Sites (Newt) on Kubernetes, check that your cluster, tools, networking, and storage setup match the deployment path you want to use.
|
||||
|
||||
## Kubernetes cluster
|
||||
|
||||
Use a Kubernetes version that satisfies the Helm chart `kubeVersion` requirement and is supported by your Kubernetes provider or distribution.
|
||||
|
||||
Check your cluster version:
|
||||
|
||||
```bash
|
||||
kubectl version
|
||||
```
|
||||
|
||||
<Info>
|
||||
See the [Version Matrix](https://github.com/fosrl/helm-charts/VERSION_MATRIX.md) for the supported Kubernetes versions of the Pangolin and Newt Helm charts.
|
||||
</Info>
|
||||
|
||||
## Controller access and RBAC
|
||||
|
||||
Controller mode is the default and recommended Kubernetes deployment mode for Pangolin.
|
||||
|
||||
When controller mode is enabled, the Pangolin Kube Controller runs with its own ServiceAccount and needs permission to watch and manage the Kubernetes and Traefik resources it reconciles. The chart creates the required RBAC resources for you, unless RBAC creation is disabled.
|
||||
|
||||
By default, the controller is scoped to the namespace of a single Pangolin deployment. It can also be configured for a broader scope when one controller should reconcile resources for multiple Pangolin deployments.
|
||||
|
||||
Depending on the configured controller scope, the controller needs namespace-scoped or cluster-scoped access to the resources it reconciles:
|
||||
|
||||
| API group | Resources | Verbs |
|
||||
| --- | --- | --- |
|
||||
| `""` | `events` | `create`, `patch`, `update` |
|
||||
| `""` | `services`, `endpoints` | `get`, `list`, `watch`, `create`, `update`, `patch`, `delete` |
|
||||
| `discovery.k8s.io` | `endpointslices` | `get`, `list`, `watch`, `create`, `update`, `patch`, `delete` |
|
||||
| `traefik.io` | `ingressroutes`, `ingressroutetcps`, `ingressrouteudps`, `middlewares`, `middlewaretcps`, `traefikservices`, `serverstransports`, `serverstransporttcps`, `tlsoptions`, `tlsstores` | `get`, `list`, `watch`, `create`, `update`, `patch`, `delete` |
|
||||
|
||||
If leader election is enabled, the controller also needs access to:
|
||||
|
||||
| API group | Resources | Verbs |
|
||||
| --- | --- | --- |
|
||||
| `coordination.k8s.io` | `leases` | `get`, `list`, `watch`, `create`, `update`, `patch` |
|
||||
|
||||
The controller also needs cluster-wide read access to Kubernetes discovery resources:
|
||||
|
||||
| API group | Resources | Verbs |
|
||||
| --- | --- | --- |
|
||||
| `networking.k8s.io` | `ingressclasses` | `get`, `list`, `watch` |
|
||||
| `apiextensions.k8s.io` | `customresourcedefinitions` | `get`, `list`, `watch` |
|
||||
|
||||
<Info>
|
||||
For namespace-scoped deployments, the chart creates namespaced RBAC for the controller namespace and, if configured, the target namespace. For broader controller scopes, the chart creates the required cluster-scoped RBAC.
|
||||
</Info>
|
||||
|
||||
## Database and storage
|
||||
|
||||
Pangolin requires a database backend. The Helm chart supports multiple database modes, including CloudNativePG, external PostgreSQL, embedded PostgreSQL, and SQLite.
|
||||
|
||||
For persistent database-backed deployments, make sure your cluster has a usable StorageClass or configure the StorageClass explicitly in your chart values.
|
||||
|
||||
Check available StorageClasses:
|
||||
|
||||
```bash
|
||||
kubectl get storageclasses
|
||||
```
|
||||
|
||||
For long-running/production deployments, prefer PostgreSQL-based modes such as CloudNativePG or external PostgreSQL.
|
||||
|
||||
<Info>
|
||||
SQLite can be useful for simple or test deployments, but PostgreSQL-based modes are the better fit for long-running/production Kubernetes deployments.
|
||||
</Info>
|
||||
|
||||
### Site connector storage
|
||||
|
||||
A Site (Newt) deployment does not require persistent storage by default.
|
||||
|
||||
Use writable configuration persistence only if your deployment needs runtime configuration to survive pod replacement, upgrades, node drains, or rescheduling. For simple deployments, no PVC is required.
|
||||
|
||||
## Networking
|
||||
|
||||
### Ingress and routing
|
||||
|
||||
Pangolin needs an external entrypoint for the dashboard, API, and site traffic.
|
||||
|
||||
Depending on your chart values, this can use:
|
||||
|
||||
* controller mode with a Traefik ingress controller
|
||||
* standalone mode with chart-managed Traefik components
|
||||
* an existing ingress or load balancer setup
|
||||
|
||||
If you use controller mode with Traefik CRDs, verify that the required Traefik API resources are available:
|
||||
|
||||
```bash
|
||||
kubectl api-resources --api-group=traefik.io
|
||||
```
|
||||
|
||||
You can also check existing ingress resources:
|
||||
|
||||
```bash
|
||||
kubectl get ingress -A
|
||||
```
|
||||
|
||||
### DNS
|
||||
|
||||
Configure DNS records for the domains used by Pangolin before exposing it publicly.
|
||||
|
||||
At minimum, the Pangolin dashboard domain should resolve to the ingress controller, load balancer, or public endpoint used by your deployment.
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
nslookup pangolin.example.com
|
||||
```
|
||||
|
||||
For tunneled site deployments, also verify the DNS name used by the site connector endpoint.
|
||||
|
||||
### TLS
|
||||
|
||||
Use HTTPS for the Pangolin dashboard and API.
|
||||
|
||||
Common TLS options include:
|
||||
|
||||
* Traefik ACME / Let's Encrypt
|
||||
* cert-manager
|
||||
* a pre-created Kubernetes TLS Secret
|
||||
* TLS termination at an external load balancer or ingress controller
|
||||
|
||||
Use the TLS method that matches your ingress and cluster setup.
|
||||
|
||||
If you use cert-manager, verify that the certificate CRDs are available:
|
||||
|
||||
```bash
|
||||
kubectl get crd certificates.cert-manager.io
|
||||
```
|
||||
|
||||
## Namespace and security
|
||||
|
||||
Choose the namespace where Pangolin and related components should run.
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
kubectl create namespace pangolin
|
||||
```
|
||||
|
||||
When using Helm, you can also let Helm create the namespace:
|
||||
|
||||
```bash
|
||||
helm upgrade --install pangolin fossorial/pangolin \
|
||||
--namespace pangolin \
|
||||
--create-namespace
|
||||
```
|
||||
|
||||
If your cluster enforces Pod Security Admission, make sure the namespace labels match the selected deployment mode. Deployments that include tunnel components may require permissions that are not compatible with a fully restricted namespace profile.
|
||||
|
||||
## NetworkPolicy
|
||||
|
||||
The Pangolin and Newt charts can manage NetworkPolicies for the required application traffic.
|
||||
|
||||
If you enable chart-managed NetworkPolicies, review the generated policies before adding custom deny rules. If you replace them with your own policies, allow the required traffic between the components you deploy, such as Pangolin, Traefik, Gerbil, the database, DNS, and Site connectors.
|
||||
|
||||
## Resource planning
|
||||
|
||||
Pangolin and Site (Newt) Kubernetes deployments include predefined resource profiles for the supported deployment methods. These profiles set CPU and memory requests and limits for the components used by the selected deployment mode.
|
||||
|
||||
The available profiles are:
|
||||
|
||||
| Profile | Intended use |
|
||||
| --- | --- |
|
||||
| Small | Small deployments, or clusters with very limited available resources. |
|
||||
| Standard | Default profile for most normal deployments. |
|
||||
| Large | Larger environments with more Sites, more users, higher traffic, or stricter availability expectations. |
|
||||
|
||||
The selected profile applies to the workloads that are part of your deployment, for example:
|
||||
|
||||
| Component | Resource considerations |
|
||||
| --- | --- |
|
||||
| Pangolin | Main application workload. Size according to dashboard/API usage, users, and traffic. |
|
||||
| Pangolin Kube Controller | Required in controller mode. Size according to the number of reconciled Kubernetes and Traefik resources. |
|
||||
| Traefik | Size according to ingress and proxy traffic. |
|
||||
| Gerbil | Required when the tunnel stack is enabled. Size according to tunnel traffic and number of connected Sites. |
|
||||
| PostgreSQL / CloudNativePG | Size according to database mode, stored state, and expected write/read activity. |
|
||||
| Site connectors (Newt) | Each Site connector adds its own resource usage. Size according to the traffic handled by that Site. |
|
||||
|
||||
<Info>
|
||||
The Standard profile is intended to be enough for most standard deployments. Use Small for very limited lab or test environments, and Large for higher traffic, more Sites, more users, or larger production environments.
|
||||
</Info>
|
||||
|
||||
After installation, monitor CPU and memory usage and adjust the selected profile or individual resource overrides if needed.
|
||||
|
||||
<Warning>
|
||||
Avoid setting CPU limits on latency-sensitive Pangolin components unless your cluster policy requires them or you intentionally want to cap CPU usage.
|
||||
|
||||
CPU limits can cause throttling when a workload temporarily needs more CPU, even if spare CPU capacity is available on the node. This can negatively affect ingress, tunnel, proxy, database, and controller workloads.
|
||||
|
||||
For most deployments, use CPU requests to reserve baseline capacity and memory limits to protect the node from excessive memory usage.
|
||||
</Warning>
|
||||
|
||||
## Next steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Choose an Installation Path" href="/self-host/manual/kubernetes/choose-method" icon="route">
|
||||
Pick the Kubernetes workflow that matches how you deploy applications.
|
||||
</Card>
|
||||
<Card title="Helm Quick-Start" href="/self-host/manual/kubernetes/helm" icon="box">
|
||||
Install Pangolin or Sites (Newt) with Helm.
|
||||
</Card>
|
||||
<Card title="Kustomize Quick-Start" href="/self-host/manual/kubernetes/kustomize" icon="layer-group">
|
||||
Use Kustomize overlays and patches.
|
||||
</Card>
|
||||
<Card title="Argo CD Guide" href="/self-host/manual/kubernetes/gitops/argocd" icon="code-branch">
|
||||
Deploy Pangolin or Sites (Newt) with Argo CD.
|
||||
</Card>
|
||||
<Card title="Flux Guide" href="/self-host/manual/kubernetes/gitops/flux" icon="code-branch">
|
||||
Deploy Pangolin or Sites (Newt) with Flux.
|
||||
</Card>
|
||||
<Card title="Pangolin Helm" href="/self-host/manual/kubernetes/pangolin/helm" icon="server">
|
||||
Start with the Pangolin Helm installation guide.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,238 @@
|
||||
---
|
||||
title: "Docker Compose"
|
||||
description: "Deploy managed Pangolin manually using Docker Compose without the automated installer"
|
||||
---
|
||||
|
||||
<Tip>
|
||||
This guide is for managed self-hosted Pangolin. If you are looking for self-hosted Community Edition Pangolin please see this [Docker Compose](/self-host/manual/docker-compose) guide.
|
||||
</Tip>
|
||||
|
||||
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
|
||||
│ └── traefik/
|
||||
│ ├── traefik_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
|
||||
</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
|
||||
</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
|
||||
mkdir -p config/traefik config/db
|
||||
```
|
||||
</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/config.yml`
|
||||
</Step>
|
||||
|
||||
<Step title="Update domain">
|
||||
Edit the configuration files to replace:
|
||||
|
||||
- `154.123.45.67` with your actual domain OR public IP address of the node
|
||||
|
||||
<Warning>
|
||||
Ensure your domain DNS is properly configured to point to your server's IP address if you choose DNS.
|
||||
</Warning>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Starting the Stack
|
||||
|
||||
<Steps>
|
||||
<Step title="Start the services">
|
||||
```bash
|
||||
sudo docker compose up -d
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Monitor startup">
|
||||
```bash
|
||||
sudo docker compose logs -f
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Verify services">
|
||||
```bash
|
||||
sudo docker compose ps
|
||||
```
|
||||
|
||||
All services should show "Up" status after a few minutes.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Docker Compose Configuration
|
||||
|
||||
Create `docker-compose.yml` in your project root:
|
||||
|
||||
```yaml title="docker-compose.yml"
|
||||
services:
|
||||
pangolin:
|
||||
image: fosrl/pangolin:latest # https://github.com/fosrl/pangolin/releases
|
||||
container_name: pangolin
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./config:/app/config
|
||||
- pangolin-data:/var/certificates
|
||||
- pangolin-data:/var/dynamic
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3001/api/v1/"]
|
||||
interval: "3s"
|
||||
timeout: "3s"
|
||||
retries: 15
|
||||
|
||||
gerbil:
|
||||
image: 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:8443
|
||||
- 80:80
|
||||
|
||||
traefik:
|
||||
image: traefik:v3.7
|
||||
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
|
||||
# Shared volume for certificates and dynamic config in file mode
|
||||
- pangolin-data:/var/certificates:ro
|
||||
- pangolin-data:/var/dynamic:ro
|
||||
|
||||
networks:
|
||||
default:
|
||||
driver: bridge
|
||||
name: pangolin
|
||||
|
||||
volumes:
|
||||
pangolin-data:
|
||||
```
|
||||
|
||||
## Traefik Static Configuration
|
||||
|
||||
Create `config/traefik/traefik_config.yml`:
|
||||
|
||||
```yaml title="config/traefik/traefik_config.yml"
|
||||
api:
|
||||
insecure: true
|
||||
dashboard: true
|
||||
|
||||
providers:
|
||||
file:
|
||||
directory: "/var/dynamic"
|
||||
watch: true
|
||||
|
||||
experimental:
|
||||
plugins:
|
||||
badger:
|
||||
moduleName: "github.com/fosrl/badger"
|
||||
version: "v1.4.1"
|
||||
|
||||
log:
|
||||
level: "INFO"
|
||||
format: "common"
|
||||
|
||||
entryPoints:
|
||||
web:
|
||||
address: ":80"
|
||||
websecure:
|
||||
address: ":443"
|
||||
proxyProtocol:
|
||||
trustedIPs:
|
||||
- 0.0.0.0/0
|
||||
- ::1/128
|
||||
transport:
|
||||
respondingTimeouts:
|
||||
readTimeout: "30m"
|
||||
|
||||
serversTransport:
|
||||
insecureSkipVerify: true
|
||||
|
||||
ping:
|
||||
entryPoint: "web"
|
||||
```
|
||||
|
||||
## Pangolin Configuration
|
||||
|
||||
```yaml title="config/config.yml"
|
||||
gerbil:
|
||||
start_port: 51820
|
||||
base_endpoint: "154.123.45.67" # REPLACE WITH YOUR IP OR DOMAIN
|
||||
|
||||
managed:
|
||||
id: "he4g78wevj25msf"
|
||||
secret: "n7sd18twfko0q0vrb7wyclqzbvvnx1fqt7ezv8xewhdb9s7d"
|
||||
```
|
||||
@@ -0,0 +1,487 @@
|
||||
---
|
||||
title: "Podman Quadlets (Rootless)"
|
||||
description: "Deploy Pangolin manually using Podman Quadlets (rootless) without the automated installer"
|
||||
---
|
||||
|
||||
This guide walks through a manual deployment using the same file layout the installer generates from `install/config/*` in the Pangolin source tree. Use it if you want the installer's defaults, but you want to create and maintain the files yourself.
|
||||
|
||||
This guide assumes you already have a Linux server with Podman installed and has been tested on Debian 13.5 with Podman version 5.4.2.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Review the [quick install guide](/self-host/quick-install) and [DNS & networking](/self-host/dns-and-networking) first. At minimum you need:
|
||||
|
||||
- A public Linux server
|
||||
- A base domain such as `example.com`
|
||||
- A dashboard hostname such as `pangolin.example.com`
|
||||
- An email address for Let's Encrypt
|
||||
- TCP ports `80` and `443` open
|
||||
- UDP ports `51820` and `21820` open if you are using tunneling
|
||||
|
||||
<Tip>
|
||||
If you do not want tunneling, see [Without Tunneling](/self-host/advanced/without-tunneling). In that mode you will skip the `gerbil` service and expose Traefik directly.
|
||||
</Tip>
|
||||
|
||||
<Note>
|
||||
`base domain` is the parent domain you will attach resources to, such as `example.com`. `dashboard hostname` is the specific hostname for the Pangolin UI and API, such as `pangolin.example.com`.
|
||||
</Note>
|
||||
|
||||
<Warning>
|
||||
## Note about ports 80 and 443
|
||||
By default, unprivileged users cannot bind to privileged ports (< 1024). Some workarounds for this include:
|
||||
1. Changing the unprivileged start port to 80
|
||||
2. Using `iptables` / `nftables` to redirect 80 and 443 to ports above 1023 (such as 8080 and 8443, respectively)
|
||||
|
||||
Configuring this is out of the scope of this guide, but many guides exist online for this exact situation.
|
||||
|
||||
**This guide assumes you use option 1.**
|
||||
</Warning>
|
||||
|
||||
|
||||
## File Layout
|
||||
|
||||
Create the following project structure:
|
||||
|
||||
```text
|
||||
~/.config/
|
||||
└── containers/
|
||||
└── systemd
|
||||
├── gerbil.container
|
||||
├── pangolin.container
|
||||
├── traefik.container
|
||||
├── services.pod
|
||||
└── config/
|
||||
├── config.yml
|
||||
├── db/
|
||||
├── letsencrypt/
|
||||
└── traefik/
|
||||
├── dynamic_config.yml
|
||||
├── logs/
|
||||
└── traefik_config.yml
|
||||
```
|
||||
|
||||
The following files are created later by the running services or added only when you enable optional features:
|
||||
|
||||
- `config/db/db.sqlite` is created by Pangolin on first startup.
|
||||
- `config/key` is created by Gerbil when tunneling is enabled.
|
||||
- `config/GeoLite2-Country.mmdb` is optional and only needed for [geo-blocking](/self-host/advanced/enable-geoblocking). It is not downloaded by the running services in a manual install; download it manually before enabling geo-blocking.
|
||||
|
||||
## Create the Directories
|
||||
|
||||
Create the project folders:
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.config/containers/systemd # only needed if this is the first time you're running rootless Podman containers
|
||||
cd ~/.config/containers/systemd
|
||||
mkdir -p config/db config/letsencrypt config/traefik/logs
|
||||
```
|
||||
|
||||
## Create the Configuration Files
|
||||
|
||||
<Steps>
|
||||
<Step title="Create container and pod files">
|
||||
This section defines the Pangolin, Gerbil, and Traefik containers, the pod, their shared volumes, and the ports exposed on the host.
|
||||
|
||||
```ini title="pangolin.container"
|
||||
[Container]
|
||||
ContainerName=pangolin
|
||||
Image=docker.io/fosrl/pangolin:ee-latest
|
||||
|
||||
Pod=services.pod
|
||||
|
||||
HealthCmd=["curl","-f","http://localhost:3001/api/v1/"]
|
||||
HealthInterval=10s
|
||||
HealthRetries=15
|
||||
HealthTimeout=10s
|
||||
Notify=healthy
|
||||
|
||||
Volume=./config:/app/config
|
||||
Volume=./config/letsencrypt:/app/config/letsencrypt:ro
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
```
|
||||
|
||||
```ini title="gerbil.container"
|
||||
[Unit]
|
||||
After=pangolin.service
|
||||
Requires=pangolin.service
|
||||
|
||||
[Container]
|
||||
ContainerName=gerbil
|
||||
Image=docker.io/fosrl/gerbil:latest
|
||||
|
||||
Pod=services.pod
|
||||
|
||||
AddCapability=NET_ADMIN SYS_MODULE
|
||||
Exec='--reachableAt=http://localhost:3004' '--generateAndSaveKeyTo=/var/config/key' '--remoteConfig=http://localhost:3001/api/v1/'
|
||||
|
||||
Volume=./config/:/var/config
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
```
|
||||
|
||||
```ini title="traefik.container"
|
||||
[Unit]
|
||||
After=pangolin.service
|
||||
Requires=pangolin.service
|
||||
|
||||
[Container]
|
||||
ContainerName=traefik
|
||||
Image=docker.io/traefik:latest
|
||||
|
||||
Pod=services.pod
|
||||
|
||||
Exec='--configFile=/etc/traefik/traefik_config.yml'
|
||||
|
||||
Volume=./config/traefik:/etc/traefik:ro
|
||||
Volume=./config/letsencrypt:/letsencrypt
|
||||
Volume=./config/traefik/logs:/var/log/traefik
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
```
|
||||
|
||||
```ini title="services.pod"
|
||||
[Unit]
|
||||
Description=Pangolin Pod
|
||||
Wants=network-online.target
|
||||
After=network-online.target
|
||||
|
||||
[Pod]
|
||||
PodName=services
|
||||
|
||||
Network=pasta
|
||||
|
||||
PublishPort=51820:51820/udp
|
||||
PublishPort=21820:21820/udp
|
||||
PublishPort=443:443
|
||||
# Uncomment the line below if you enable HTTP/3 in Traefik.
|
||||
# PublishPort=443:443/udp
|
||||
PublishPort=80:80
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
```
|
||||
|
||||
<Note>
|
||||
This is the installer's default community layout with Gerbil enabled. If you want to pin releases instead of using `latest`, replace the image tags with the versions you intend to run.
|
||||
</Note>
|
||||
</Step>
|
||||
|
||||
<Step title="Create config/traefik/traefik_config.yml">
|
||||
This file configures Traefik's providers, Badger plugin, Let's Encrypt resolver, entry points, logs, and health check endpoint.
|
||||
|
||||
```yaml title="config/traefik/traefik_config.yml"
|
||||
api:
|
||||
insecure: true
|
||||
dashboard: true
|
||||
|
||||
providers:
|
||||
http:
|
||||
endpoint: "http://localhost:3001/api/v1/traefik-config"
|
||||
pollInterval: "5s"
|
||||
file:
|
||||
filename: "/etc/traefik/dynamic_config.yml"
|
||||
|
||||
experimental:
|
||||
plugins:
|
||||
badger:
|
||||
moduleName: "github.com/fosrl/badger"
|
||||
version: "v1.4.0" # Check github.com/fosrl/badger for the latest release.
|
||||
|
||||
log:
|
||||
level: "INFO"
|
||||
format: "common"
|
||||
maxSize: 100
|
||||
maxBackups: 3
|
||||
maxAge: 3
|
||||
compress: true
|
||||
|
||||
certificatesResolvers:
|
||||
letsencrypt:
|
||||
acme:
|
||||
httpChallenge:
|
||||
entryPoint: web
|
||||
storage: "/letsencrypt/acme.json"
|
||||
caServer: "https://acme-v02.api.letsencrypt.org/directory"
|
||||
|
||||
entryPoints:
|
||||
web:
|
||||
address: ":80"
|
||||
websecure:
|
||||
address: ":443"
|
||||
transport:
|
||||
respondingTimeouts:
|
||||
readTimeout: "30m"
|
||||
# Uncomment to enable HTTP/3. You must also expose 443/udp in services.pod.
|
||||
# http3:
|
||||
# advertisedPort: 443
|
||||
http:
|
||||
tls:
|
||||
certResolver: "letsencrypt"
|
||||
encodedCharacters:
|
||||
allowEncodedSlash: true
|
||||
allowEncodedQuestionMark: true
|
||||
|
||||
serversTransport:
|
||||
insecureSkipVerify: true
|
||||
|
||||
ping:
|
||||
entryPoint: "web"
|
||||
```
|
||||
|
||||
<Note>
|
||||
Traefik stores Let's Encrypt certificates at `/letsencrypt/acme.json` inside the container. The container file mounts that path from `./config/letsencrypt`, so Traefik will create `config/letsencrypt/acme.json` when it needs certificate storage.
|
||||
</Note>
|
||||
</Step>
|
||||
|
||||
<Step title="Create config/traefik/dynamic_config.yml">
|
||||
This file defines the routers, middleware, and services that send dashboard, API, and WebSocket traffic to Pangolin.
|
||||
|
||||
```yaml title="config/traefik/dynamic_config.yml"
|
||||
http:
|
||||
middlewares:
|
||||
badger:
|
||||
plugin:
|
||||
badger:
|
||||
disableForwardAuth: true
|
||||
redirect-to-https:
|
||||
redirectScheme:
|
||||
scheme: https
|
||||
|
||||
routers:
|
||||
main-app-router-redirect:
|
||||
rule: "Host(`pangolin.example.com`)" # REPLACE
|
||||
service: next-service
|
||||
entryPoints:
|
||||
- web
|
||||
middlewares:
|
||||
- redirect-to-https
|
||||
- badger
|
||||
|
||||
next-router:
|
||||
rule: "Host(`pangolin.example.com`) && !PathPrefix(`/api/v1`)" # REPLACE
|
||||
service: next-service
|
||||
entryPoints:
|
||||
- websecure
|
||||
middlewares:
|
||||
- badger
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
|
||||
api-router:
|
||||
rule: "Host(`pangolin.example.com`) && PathPrefix(`/api/v1`)" # REPLACE
|
||||
service: api-service
|
||||
entryPoints:
|
||||
- websecure
|
||||
middlewares:
|
||||
- badger
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
|
||||
ws-router:
|
||||
rule: "Host(`pangolin.example.com`)" # REPLACE
|
||||
service: api-service
|
||||
entryPoints:
|
||||
- websecure
|
||||
middlewares:
|
||||
- badger
|
||||
tls:
|
||||
certResolver: letsencrypt
|
||||
|
||||
services:
|
||||
next-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://localhost:3002"
|
||||
|
||||
api-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://localhost:3000"
|
||||
|
||||
tcp:
|
||||
serversTransports:
|
||||
pp-transport-v1:
|
||||
proxyProtocol:
|
||||
version: 1
|
||||
pp-transport-v2:
|
||||
proxyProtocol:
|
||||
version: 2
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Create config/config.yml">
|
||||
This file contains Pangolin's application settings, dashboard domain, base domain, CORS origin, and server secret.
|
||||
|
||||
```yaml title="config/config.yml"
|
||||
gerbil:
|
||||
start_port: 51820
|
||||
base_endpoint: "pangolin.example.com" # REPLACE WITH YOUR DASHBOARD DOMAIN
|
||||
|
||||
app:
|
||||
dashboard_url: "https://pangolin.example.com" # REPLACE WITH YOUR DASHBOARD DOMAIN
|
||||
log_level: "info"
|
||||
telemetry:
|
||||
anonymous_usage: true
|
||||
|
||||
domains:
|
||||
domain1:
|
||||
base_domain: "example.com" # REPLACE WITH YOUR BASE DOMAIN
|
||||
|
||||
server:
|
||||
secret: "replace-with-a-long-random-secret" # REPLACE WITH SECURE SECRET
|
||||
cors:
|
||||
origins: ["https://pangolin.example.com"] # REPLACE WITH YOUR DASHBOARD 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
|
||||
```
|
||||
|
||||
Replace these values before starting the stack:
|
||||
|
||||
- `pangolin.example.com` with your dashboard hostname
|
||||
- `example.com` with your base domain
|
||||
- `replace-with-a-long-random-secret` with a strong random secret
|
||||
- `admin@example.com` in `traefik_config.yml` with your Let's Encrypt email
|
||||
|
||||
Generate a secret with:
|
||||
|
||||
```bash
|
||||
openssl rand -hex 32
|
||||
```
|
||||
|
||||
<Warning>
|
||||
Do not reuse a weak or short `server.secret`. If you need to rotate it later, use `pangctl rotate-server-secret`. See the [container CLI tool guide](/self-host/advanced/container-cli-tool#rotate-server-secret).
|
||||
Please note you will need to run `podman exec ...` instead of `docker exec ...`.
|
||||
</Warning>
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
### Optional Email Configuration
|
||||
|
||||
If you want Pangolin to send email, add this block to `config/config.yml` and set `flags.require_email_verification` to `true`:
|
||||
|
||||
```yaml title="config/config.yml"
|
||||
email:
|
||||
smtp_host: "smtp.example.com"
|
||||
smtp_port: 587
|
||||
smtp_user: "smtp-user"
|
||||
smtp_pass: "smtp-password"
|
||||
no_reply: "noreply@example.com"
|
||||
```
|
||||
|
||||
### Optional Geo-blocking Configuration
|
||||
|
||||
If you want geo-blocking, download the MaxMind database and add this line under `server`:
|
||||
|
||||
```yaml title="config/config.yml"
|
||||
server:
|
||||
maxmind_db_path: "./config/GeoLite2-Country.mmdb"
|
||||
```
|
||||
|
||||
See [Enable Geo-blocking](/self-host/advanced/enable-geoblocking) for the full process.
|
||||
|
||||
## Start the Stack
|
||||
|
||||
<Steps>
|
||||
<Step title="Reload and start the services">
|
||||
```bash
|
||||
systemctl --user daemon-reload
|
||||
systemctl --user start services-pod
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Enable lingering so that services stay up after you log out">
|
||||
```bash
|
||||
loginctl enable-linger $USER
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Watch the logs">
|
||||
```bash
|
||||
podman logs -f pangolin traefik gerbil
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Verify the containers are healthy">
|
||||
```bash
|
||||
podman ps -a
|
||||
```
|
||||
|
||||
`pangolin`, `traefik`, and `gerbil` should all report as running after the first startup finishes.
|
||||
</Step>
|
||||
|
||||
<Step title="Get the setup token from the Pangolin logs">
|
||||
Check the Pangolin container logs:
|
||||
|
||||
```bash
|
||||
podman logs pangolin
|
||||
```
|
||||
|
||||
Pangolin prints a setup token to stdout on first boot. Copy that token before continuing.
|
||||
</Step>
|
||||
|
||||
<Step title="Open the initial setup page">
|
||||
Visit:
|
||||
|
||||
```text
|
||||
https://pangolin.example.com/auth/initial-setup
|
||||
```
|
||||
|
||||
Replace the hostname with your real dashboard domain, then use the setup token from the Pangolin logs to register the first admin account.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Verify the Setup
|
||||
|
||||
You should expect the following on a healthy first install:
|
||||
|
||||
- `podman ps -a` shows `pangolin`, `traefik`, and `gerbil` as running.
|
||||
- `podman logs pangolin` includes the one-time setup token for the first admin account.
|
||||
- Visiting `https://<your-dashboard-domain>/auth/initial-setup` loads the setup page.
|
||||
- `config/db/db.sqlite` exists after Pangolin starts.
|
||||
- `config/key` exists after Gerbil starts.
|
||||
|
||||
<Tip>
|
||||
The first Let's Encrypt certificate request can take a short while. If the page initially shows a certificate warning, wait a minute and refresh.
|
||||
</Tip>
|
||||
|
||||
## If Something Fails
|
||||
|
||||
- If the setup page does not load, confirm your DNS record points to the server and ports `80` and `443` are reachable.
|
||||
- If you cannot complete first-time signup, check `podman logs pangolin` and copy the setup token printed by Pangolin.
|
||||
- If certificates are not issued, confirm `admin@example.com` was replaced and that nothing else is already bound to ports `80` or `443` (or whatever alternate ports you selected on the host).
|
||||
- If `pangolin` never becomes healthy, inspect `podman logs -f pangolin`.
|
||||
- If tunneling does not work, inspect `podman logs -f gerbil` and confirm UDP ports `51820` and `21820` are open.
|
||||
- If Traefik serves the wrong host, re-check every `pangolin.example.com` replacement in both Traefik files and `config/config.yml`.
|
||||
|
||||
## Without Tunneling
|
||||
|
||||
If you do not want Gerbil:
|
||||
|
||||
- Remove the gerbil service.
|
||||
- Remove the `gerbil` block from `config/config.yml`.
|
||||
|
||||
That mode is covered in more detail in [Without Tunneling](/self-host/advanced/without-tunneling).
|
||||
@@ -0,0 +1,392 @@
|
||||
---
|
||||
title: "Unraid Deployment"
|
||||
description: "Deploy Pangolin on Unraid for local reverse proxy and tunneling"
|
||||
---
|
||||
|
||||
## 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
|
||||
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="/images/pangolin_config.png"
|
||||
alt="Pangolin configuration settings in Unraid"
|
||||
/>
|
||||
</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"
|
||||
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.4.1"
|
||||
|
||||
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"
|
||||
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="/images/traefik_repo.png"
|
||||
width="400"
|
||||
alt="Traefik repository selection in Community Apps"
|
||||
/>
|
||||
</Frame>
|
||||
|
||||
#### 3. Configure Traefik
|
||||
|
||||
<Frame caption="Traefik configuration settings in Unraid">
|
||||
<img
|
||||
src="/images/traefik_config.png"
|
||||
alt="Traefik configuration settings in Unraid"
|
||||
/>
|
||||
</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
|
||||
--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="/images/gerbil_config.png"
|
||||
alt="Gerbil configuration settings in Unraid"
|
||||
/>
|
||||
</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
|
||||
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
|
||||
--net=container:Gerbil
|
||||
```
|
||||
|
||||
Then, set "Network Type" to "None".
|
||||
|
||||
<Frame caption="Traefik networking configuration through Gerbil">
|
||||
<img
|
||||
src="/images/traefik_networking.png"
|
||||
alt="Traefik networking configuration through Gerbil"
|
||||
/>
|
||||
</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="/images/gerbil_logs.png"
|
||||
alt="Gerbil logs showing WireGuard interface startup"
|
||||
/>
|
||||
</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,80 @@
|
||||
---
|
||||
title: "Purchase a License Key"
|
||||
description: "How to buy an Enterprise license key for Pangolin from the dashboard"
|
||||
---
|
||||
|
||||
Buy an Enterprise license key directly from the Pangolin dashboard. You choose your tier, complete checkout, and get your key right away — in the dashboard and by email.
|
||||
|
||||
## License tiers
|
||||
|
||||
You can purchase a **self-serve license online at any time** through [app.pangolin.net](https://app.pangolin.net). Two tiers are available for self-serve checkout:
|
||||
|
||||
- **Starter** — For smaller teams getting started with self-hosted Enterprise Edition
|
||||
- **Scale** — For growing teams that need higher limits and more advanced capabilities
|
||||
|
||||
See the [Self-Hosted pricing page](https://pangolin.net/pricing#Self-Hosted) for a full comparison of features and limits included with each tier.
|
||||
|
||||
<Info>
|
||||
Need more users, more sites, or special add-ons — such as compliance packages, SLA support, pay-by-invoice, or bank transfer? Contact [sales@pangolin.net](mailto:sales@pangolin.net) for a custom quote.
|
||||
</Info>
|
||||
|
||||
## How to buy a license key
|
||||
|
||||
<Steps>
|
||||
<Step title="Sign in or create an account">
|
||||
Go to [app.pangolin.net](https://app.pangolin.net) and log in, or create an account if you don’t have one yet.
|
||||
</Step>
|
||||
|
||||
<Step title="Open the License section">
|
||||
In the dashboard, open the **License** section from the sidebar.
|
||||
</Step>
|
||||
|
||||
<Step title="Generate a license key">
|
||||
Click **Generate license key**, choose your tier, and fill in the required details.
|
||||
</Step>
|
||||
|
||||
<Step title="Go to checkout">
|
||||
Click **Continue to checkout**.
|
||||
</Step>
|
||||
|
||||
<Step title="Enter payment details and submit">
|
||||
Enter your payment information and submit the order.
|
||||
</Step>
|
||||
|
||||
<Step title="Get your key">
|
||||
Your license key appears in the **License key** table in the dashboard, and we send a confirmation email with the key as well.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## After purchase
|
||||
|
||||
- **Cancel or change your subscription:** Use the **Billing** page in the dashboard.
|
||||
- **Need help?** Email [support@pangolin.net](mailto:support@pangolin.net).
|
||||
|
||||
## Frequently asked questions
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="I reset my host and can’t re-register my key. What do I do?">
|
||||
A license is tied to one host (by host ID) at a time. If you reset your instance and need to use the same key on a new host, contact us at [support@pangolin.net](mailto:support@pangolin.net) and we’ll reset it for you.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="How do I cancel my subscription?">
|
||||
Open the **Billing** page in the dashboard and click **Modify License Subscription**. You’ll be taken to the Stripe billing page where you can manage or cancel your subscription. If you run into any issues, email us.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I try Enterprise before buying?">
|
||||
Businesses can request a **free limited trial** of Enterprise Edition by contacting [sales@pangolin.net](mailto:sales@pangolin.net). Include your organization details and what you'd like to evaluate.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I pay monthly instead of yearly?">
|
||||
Self-serve licenses (Starter and Scale) are sold as annual subscriptions paid by card at checkout. If you need monthly billing, pay-by-invoice, or bank transfer, contact [sales@pangolin.net](mailto:sales@pangolin.net).
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="I need more users, sites, or custom add-ons. What are my options?">
|
||||
Self-serve checkout covers **Starter** and **Scale** tiers only. For higher limits, compliance packages, SLA support, pay-by-invoice, bank transfer, or other custom arrangements, contact [sales@pangolin.net](mailto:sales@pangolin.net).
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I buy more license keys for additional hosts?">
|
||||
Yes. Purchase additional self-serve keys through the dashboard, or contact [sales@pangolin.net](mailto:sales@pangolin.net) if you need multiple keys added to your account under a custom agreement.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
@@ -0,0 +1,140 @@
|
||||
---
|
||||
title: "Quick Install Guide"
|
||||
description: "Deploy your own fully self-hosted instance of Pangolin Community Edition"
|
||||
---
|
||||
|
||||
## 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
|
||||
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
|
||||
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>
|
||||
</Steps>
|
||||
|
||||
## Post-Installation Setup
|
||||
|
||||
Once installation completes successfully, you'll see:
|
||||
|
||||
```
|
||||
Installation complete!
|
||||
|
||||
To complete the initial setup, please visit:
|
||||
https://<your-dashboard-domain>/auth/initial-setup
|
||||
```
|
||||
|
||||
<Steps>
|
||||
<Step title="Access the dashboard">
|
||||
Navigate to the URL shown in the installer output and enter the setup token displayed by the installer. If you chose not to start the containers during installation, retrieve the token from the Pangolin container logs when it first starts.
|
||||
|
||||
```
|
||||
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="Get the setup token from the Pangolin logs">
|
||||
Check the Pangolin container logs and copy the setup token printed to stdout:
|
||||
|
||||
```bash
|
||||
sudo docker compose logs pangolin
|
||||
```
|
||||
|
||||
You will need that token on the initial setup page to register the first admin account.
|
||||
</Step>
|
||||
|
||||
<Step title="Create admin account">
|
||||
Complete the initial admin user setup:
|
||||
|
||||
- Paste the setup token from the Pangolin logs
|
||||
- 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,144 @@
|
||||
---
|
||||
title: "Supporter Program"
|
||||
description: "Support Pangolin development and remove UI elements with a supporter key"
|
||||
---
|
||||
|
||||
<Warning>
|
||||
**Supporter Program Phased Out**
|
||||
|
||||
The supporter program has been phased out in Pangolin v1.19. You can continue to use your supporter key on older Pangolin instances. New Pangolin instances skip the key prompt.
|
||||
</Warning>
|
||||
|
||||
<Check>
|
||||
Enterprise Edition is **free** for personal use. See [Enterprise Edition](/self-host/enterprise-edition) to apply for a license.
|
||||
</Check>
|
||||
|
||||
|
||||
## 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="/images/supporter-tiers.png"
|
||||
alt="Supporter tier comparison showing Limited vs Full Supporter benefits"
|
||||
/>
|
||||
</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="/images/redeem-key.png"
|
||||
alt="Pangolin supporter key redemption interface"
|
||||
/>
|
||||
</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>
|
||||
@@ -0,0 +1,42 @@
|
||||
---
|
||||
title: "Telemetry"
|
||||
description: "Understanding Pangolin's anonymous usage data collection"
|
||||
---
|
||||
|
||||
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
|
||||
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.
|
||||
Reference in New Issue
Block a user