port mintlify to fumadocs

This commit is contained in:
miloschwartz
2026-09-25 15:31:51 -04:00
parent dc54fb1017
commit 63199a588c
373 changed files with 13533 additions and 4577 deletions
+48
View File
@@ -0,0 +1,48 @@
---
title: "Automatic Site Updates"
description: "Let sites check for and install updates on their own"
---
<Note>
Only available in [Pangolin Cloud](https://app.pangolin.net/auth/signup) and [Enterprise Edition](/self-host/enterprise-edition).
</Note>
Sites can automatically check for new versions, download the latest release, and restart to begin using it. This is useful when you manage many remote connectors and do not want to update each one by hand.
## Supported Installation Methods
Automatic updates are only supported for sites installed with the [binary installation method](/manage/sites/install-site).
If you run a site in Docker, Kubernetes, or another container orchestration platform, updates are handled by that platform. Pangolin cannot coordinate automatic updates for containerized deployments.
## How It Works
When automatic updates are enabled, the site periodically checks for a newer version. If one is available, it downloads the latest release and restarts itself. After the restart, the site reconnects to Pangolin on the new version.
For safety, Pangolin waits **24 hours** after a release is published before sites pull that version. This gives time for early issues to surface before a fleet of sites updates.
## Enable Automatic Updates
<Note>
Automatic updates are disabled by default. You must opt in at the organization or site level.
</Note>
You can control automatic updates at two levels:
- **Organization**: Enable automatic updates for all sites under **Organization Settings**.
- **Per site**: Enable or disable automatic updates on an individual site.
These settings work together:
- Turn updates **off** for the organization and enable them on specific sites only.
- Turn updates **on** for the organization and disable them on specific sites that should stay on a fixed version.
## Fleet Deployments
Automatic updates are especially useful when you run many sites across edge networks. For example, if you have dozens or hundreds of Raspberry Pis or other edge devices running a site, you would otherwise need to update each site every time a new release ships.
With automatic updates enabled, each site in the fleet checks for new versions on its own, downloads the latest release after the 24-hour safety window, restarts, and reconnects to Pangolin without manual intervention.
## Use With Caution
Automatic updates trade convenience for control. If you need predictable change windows or want to validate a release before rolling it out, keep automatic updates disabled and [update sites manually](/manage/sites/update-site) on your own schedule.
@@ -0,0 +1,671 @@
---
title: "Configure Sites"
description: "Configure Pangolin site connector setting"
---
## Flags
<Note>
Almost every setting below can also be set via an environment variable (**Env var**) or in the [config file](#config-file) (**Config file key**) instead of as a CLI flag. Values are resolved with priority `CLI flag > environment variable > config file > default`, so a flag always overrides an env var, which always overrides what's saved on disk. This makes environment variables and config files particularly useful for containerized environments where CLI flags are less convenient.
</Note>
<ResponseField name="id" type="string" required>
Site ID generated by Pangolin to identify the client.
**Example**: `31frd0uzbjvp721`
**Env var**: `SITE_ID`
**Config file key**: `id`
**Legacy env var**: `NEWT_SECRET`
</ResponseField>
<ResponseField name="secret" type="string" required>
A unique secret used to authenticate the site with the websocket.
**Example**: `h51mmlknrvrwv8s4r1i210azhumt6isgbpyavxodibx1k2d6`
**Env var**: `SITE_SECRET`
**Config file key**: `secret`
**Legacy env var**: `NEWT_SECRET`
<Warning>
Keep this secret private and secure. It's used for authentication.
</Warning>
</ResponseField>
<ResponseField name="endpoint" type="string" required>
The endpoint where the Pangolin server resides for websocket connections.
**Example**: `https://pangolin.example.com`
**Env var**: `PANGOLIN_ENDPOINT`
**Config file key**: `endpoint`
</ResponseField>
<ResponseField name="name" type="string">
Site name to use when provisioning with a provisioning key. Supports `{{env.VARIABLE_NAME}}` templating from the process environment. If omitted, Pangolin assigns a random animal-based name (changeable in the dashboard).
**Example**: `my-edge-site` or `'{{env.SERIAL_NUMBER}}-edge'`
**Env var**: `SITE_NAME`
**Config file key**: `name`
</ResponseField>
<ResponseField name="provisioning-blueprint-file" type="string">
Path to a blueprint YAML file applied **once** at provisioning (imperative bootstrap). Unlike `--blueprint-file`, the site does not keep reapplying it, so resources you edit in the dashboard are not overwritten on later runs. See [Site provisioning keys](/manage/sites/site-provisioning).
**Example**: `/path/to/bootstrap.yaml`
**Env var**: `PROVISIONING_BLUEPRINT_FILE`
**Config file key**: `provisioningBlueprintFile`
</ResponseField>
<ResponseField name="provisioning-key" type="string">
Provisioning key from Pangolin (alternative to a `provisioningKey` field inside the config file). The site exchanges it once for site credentials, then persists `id` and `secret` to the config file and ignores the key on later runs. This flag is not written into the config file. See [Site provisioning keys](/manage/sites/site-provisioning).
**Example**: `spk_...`
**Env var**: `SITE_PROVISIONING_KEY`
**Config file key**: `provisioningKey`
**Legacy env var**: `NEWT_PROVISIONING_KEY`
</ResponseField>
<ResponseField name="config-file" type="string">
Path to a JSON file where the site reads and persists settings (`endpoint`, `id`, `secret`, optional `provisioningKey`, and virtually every other flag below). When you use [site provisioning](/manage/sites/site-provisioning), the site writes `id` and `secret` into this file after a successful exchange. See [Config File](#config-file) for the default path on each platform.
**Example**: `/var/site.json`
**Env var**: `CONFIG_FILE`
</ResponseField>
<ResponseField name="dns" type="string">
DNS server to use for resolving the endpoint.
**Default**: `9.9.9.9`
**Env var**: `DNS`
**Config file key**: `dns`
</ResponseField>
<ResponseField name="log-level" type="string">
The log level to use for the site output.
**Options**: `DEBUG`, `INFO`, `WARN`, `ERROR`, `FATAL`
**Default**: `INFO`
**Env var**: `LOG_LEVEL`
**Config file key**: `logLevel`
</ResponseField>
<ResponseField name="ping-interval" type="string">
Interval for pinging the server.
**Default**: `15s`
**Env var**: `PING_INTERVAL`
**Config file key**: `pingInterval`
</ResponseField>
<ResponseField name="ping-timeout" type="string">
Timeout for each ping.
**Default**: `7s`
**Env var**: `PING_TIMEOUT`
**Config file key**: `pingTimeout`
</ResponseField>
<ResponseField name="udp-proxy-idle-timeout" type="string">
Idle timeout for UDP proxied client flows before cleanup.
**Default**: `90s`
**Env var**: `SITE_UDP_PROXY_IDLE_TIMEOUT`
**Config file key**: `udpProxyIdleTimeout`
**Legacy env var**: `NEWT_UDP_PROXY_IDLE_TIMEOUT`
</ResponseField>
<ResponseField name="docker-socket" type="string">
Set the Docker socket path for container discovery integration.
**Example**: `/var/run/docker.sock`
**Env var**: `DOCKER_SOCKET`
**Config file key**: `dockerSocket`
</ResponseField>
<ResponseField name="docker-enforce-network-validation" type="boolean">
Validate the container target is on the same network as the the site process.
**Default**: `false`
**Env var**: `DOCKER_ENFORCE_NETWORK_VALIDATION`
**Config file key**: `dockerEnforceNetworkValidation`
</ResponseField>
<ResponseField name="health-file" type="string">
Check if connection to WireGuard server (Pangolin) is ok. Creates a file if ok, removes it if not ok. Can be used with Docker healthcheck to restart the site.
**Example**: `/tmp/healthy`
**Env var**: `HEALTH_FILE`
**Config file key**: `healthFile`
</ResponseField>
<ResponseField name="updown" type="string">
Script to be called when targets are added or removed.
**Example**: `/path/to/updown.sh`
**Env var**: `UPDOWN_SCRIPT`
**Config file key**: `updownScript`
</ResponseField>
<ResponseField name="blueprint-file" type="string">
Path to a blueprint file that defines Pangolin resources and settings. This mode is declarative: the site keeps applying the file, and it remains the source of truth (dashboard changes can be overwritten on the next apply). For a one-time bootstrap blueprint with provisioning keys, use `--provisioning-blueprint-file` instead.
**Example**: `/path/to/blueprint.yaml`
**Env var**: `BLUEPRINT_FILE`
**Config file key**: `blueprintFile`
</ResponseField>
<ResponseField name="no-cloud" type="boolean">
Don't fail over to the cloud when using managed nodes in Pangolin Cloud.
**Default**: `false`
**Env var**: `NO_CLOUD`
**Config file key**: `noCloud`
</ResponseField>
<ResponseField name="disable-clients" type="boolean">
Disable clients on the WireGuard interface.
**Default**: `false` (clients enabled)
**Env var**: `DISABLE_CLIENTS`
**Config file key**: `disableClients`
</ResponseField>
<ResponseField name="disable-ssh" type="boolean">
Disable SSH auth daemon and native SSH mode (remote auth daemon integrations still work).
**Default**: `false`
**Env var**: `DISABLE_SSH`
**Config file key**: `disableSsh`
</ResponseField>
<ResponseField name="metrics" type="boolean">
Enable Prometheus /metrics exporter.
**Default**: `false`
**Env var**: `SITE_METRICS_PROMETHEUS_ENABLED`
**Config file key**: `metrics`
**Legacy env var**: `NEWT_METRICS_PROMETHEUS_ENABLED`
</ResponseField>
<ResponseField name="otlp" type="boolean">
Enable OTLP exporters (metrics/traces) to OTEL_EXPORTER_OTLP_ENDPOINT.
**Default**: `false`
**Env var**: `SITE_METRICS_OTLP_ENABLED`
**Config file key**: `otlp`
**Legacy env var**: `NEWT_METRICS_OTLP_ENABLED`
</ResponseField>
<ResponseField name="metrics-admin-addr" type="string">
Admin/metrics bind address.
**Default**: `127.0.0.1:2112`
**Env var**: `SITE_ADMIN_ADDR`
**Config file key**: `metricsAdminAddr`
**Legacy env var**: `NEWT_ADMIN_ADDR`
</ResponseField>
<ResponseField name="metrics-async-bytes" type="boolean">
Enable async bytes counting (background flush; lower hot path overhead).
**Default**: `false`
**Env var**: `SITE_METRICS_ASYNC_BYTES`
**Config file key**: `metricsAsyncBytes`
**Legacy env var**: `NEWT_METRICS_ASYNC_BYTES`
</ResponseField>
<ResponseField name="pprof" type="boolean">
Enable pprof debug endpoints on the admin server.
**Default**: `false`
**Env var**: `SITE_PPROF_ENABLED`
**Config file key**: `pprof`
**Legacy env var**: `NEWT_PPROF_ENABLED`
</ResponseField>
<ResponseField name="region" type="string">
Optional region resource attribute for telemetry and metrics.
**Example**: `us-west-2`
**Env var**: `SITE_REGION`
**Config file key**: `region`
**Legacy env var**: `NEWT_REGION`
</ResponseField>
<ResponseField name="enforce-hc-cert" type="boolean">
Enforce certificate validation for health checks.
**Default**: `false` (accepts any cert)
**Env var**: `ENFORCE_HC_CERT`
**Config file key**: `enforceHcCert`
</ResponseField>
<ResponseField name="tls-client-cert-file" type="string">
Path to client certificate file (PEM/DER format) for mTLS.
**Example**: `/path/to/client.crt`
**Env var**: `TLS_CLIENT_CERT`
**Config file key**: `tlsClientCertFile`
</ResponseField>
<ResponseField name="tls-client-key" type="string">
Path to client private key file (PEM/DER format) for mTLS.
**Example**: `/path/to/client.key`
**Env var**: `TLS_CLIENT_KEY`
**Config file key**: `tlsClientKey`
</ResponseField>
<ResponseField name="tls-client-ca" type="string">
Path to CA certificate file for validating remote certificates (can be specified multiple times).
**Example**: `/path/to/ca.crt`
**Env var**: `TLS_CLIENT_CAS` (comma-separated list)
**Config file key**: `tlsClientCa` (array of strings in the config file)
</ResponseField>
<ResponseField name="tls-client-cert" type="string">
Path to client certificate (PKCS12 format) - DEPRECATED: use `--tls-client-cert-file` and `--tls-client-key` instead.
**Example**: `/path/to/client.p12`
**Env var**: `TLS_CLIENT_CERT_PKCS12`
**Config file key**: `tlsClientCert`
</ResponseField>
<ResponseField name="ad-pre-shared-key" type="string">
Pre-shared key for auth daemon authentication.
**Env var**: `AD_KEY`
**Config file key**: `adPreSharedKey`
</ResponseField>
<ResponseField name="ad-principals-file" type="string">
Path to the principals file for auth daemon.
**Default**: `/var/run/auth-daemon/principals`
**Env var**: `AD_PRINCIPALS_FILE`
**Config file key**: `adPrincipalsFile`
</ResponseField>
<ResponseField name="ad-ca-cert-path" type="string">
Path to the CA certificate file for auth daemon.
**Default**: `/etc/ssh/ca.pem`
**Env var**: `AD_CA_CERT_PATH`
**Config file key**: `adCaCertPath`
</ResponseField>
<ResponseField name="ad-generate-random-password" type="boolean">
Generate a random password for authenticated users.
**Default**: `false`
**Env var**: `AD_GENERATE_RANDOM_PASSWORD`
**Config file key**: `adGenerateRandomPassword`
</ResponseField>
<ResponseField name="version" type="boolean">
Print the Newt (package) version and exit.
</ResponseField>
<ResponseField name="show-config" type="boolean">
Print the fully resolved configuration (including which source — default, config file, environment, or CLI — each value came from) and exit. Secrets are masked. Useful for confirming what a config file actually loaded.
**Default**: `false`
</ResponseField>
<ResponseField name="prefer-endpoint" type="string">
Prefer this endpoint for the connection (if set, will override the endpoint from the server).
**Example**: `https://preferred.endpoint.com`
**Config file key**: `preferEndpoint`
</ResponseField>
<ResponseField name="interface" type="string">
Name of the WireGuard interface.
**Default**: `pangolin`
**Env var**: `INTERFACE`
**Config file key**: `interface`
</ResponseField>
<ResponseField name="port" type="integer">
Port for the peers to connect to the site on. This can be used to keep a static port open in firewalls instead of default random ports.
**Example**: `34534`
**Env var**: `PORT`
**Config file key**: `port`
</ResponseField>
<ResponseField name="native" type="boolean">
Use a native WireGuard interface for client tunnels instead of the userspace netstack implementation.
<Note>
Most users should not use this feature.
</Note>
**Default**: `false`
**Env var**: `USE_NATIVE_INTERFACE`
**Config file key**: `native`
</ResponseField>
<ResponseField name="native-main" type="boolean">
Use a native WireGuard interface for the main tunnel (instead of netstack).
<Note>
Most users should not use this feature.
</Note>
**Default**: `false`
**Env var**: `USE_NATIVE_MAIN_INTERFACE`
**Config file key**: `nativeMain`
</ResponseField>
<ResponseField name="interface-main" type="string">
Name of the native main tunnel WireGuard interface (used with `--native-main`).
<Note>
Most users should not use this feature.
</Note>
**Default**: `pangolin`
**Env var**: `INTERFACE_MAIN`
**Config file key**: `interfaceMain`
</ResponseField>
<ResponseField name="mtu" type="integer">
MTU for the internal WireGuard interface.
<Note>
Most users should not use this feature.
</Note>
**Default**: `1280`
**Env var**: `MTU`
**Config file key**: `mtu`
</ResponseField>
## Config File
The site can be configured entirely with a JSON config file instead of (or in addition to) CLI flags and environment variables. Almost every flag documented above has a matching camelCase key in this file — see the **Config file key** on each flag for its exact name.
You can use `--config-file` or the `CONFIG_FILE` environment variable to point the site at a specific path. The site also writes to this file: when [site provisioning](/manage/sites/site-provisioning) succeeds, the resolved `id` and `secret` are persisted here so you don't need to pass `--id`/`--secret` (or a provisioning key) on every subsequent run.
### Default locations
If `--config-file`/`CONFIG_FILE` isn't set, the site reads and writes the config file at a platform-specific default path:
| Platform | Default path |
| --- | --- |
| macOS | `~/.config/pangolin/site.json` |
| Windows | `C:\Users\<Username>\.config\pangolin\site.json` |
| Linux / others | `~/.config/pangolin/site.json` |
### Example
```json
{
"endpoint": "https://app.pangolin.net",
"id": "spmzu8rbpzj1qq6",
"secret": "f6v61mjutwme2kkydbw3fjo227zl60a2tsf5psw9r25hgae3",
"name": "my-edge-site",
"dns": "9.9.9.9",
"logLevel": "INFO",
"interface": "pangolin",
"mtu": 1280,
"port": 34534,
"pingInterval": "15s",
"pingTimeout": "7s",
"udpProxyIdleTimeout": "90s",
"disableClients": false,
"disableSsh": false,
"noCloud": false,
"dockerSocket": "unix:///var/run/docker.sock",
"dockerEnforceNetworkValidation": false,
"healthFile": "/tmp/healthy",
"blueprintFile": "/path/to/blueprint.yaml",
"tlsClientCertFile": "",
"tlsClientKey": "",
"tlsClientCa": []
}
```
<Note>
You only need to include the keys you want to set — anything omitted falls back to an environment variable, then its built-in default. Run `pangolin --show-config` to see every resolved setting alongside where it came from (config file, environment, CLI, or default), which is useful for confirming what a config file actually loaded.
</Note>
<Warning>
Fields like `secret`, `tlsClientKey`, and `adPreSharedKey` are sensitive. Restrict file permissions on the config file accordingly.
</Warning>
## Docker Socket Integration
The site can integrate with the Docker socket to provide remote inspection of Docker containers. This allows Pangolin to query and retrieve detailed information about containers running on the site, including metadata, network configuration, port mappings, and more.
**Configuration:**
You can specify the Docker socket path using the `--docker-socket` CLI argument or by setting the `DOCKER_SOCKET` environment variable. If the Docker socket is not available or accessible, the site will gracefully disable Docker integration and continue normal operation.
Supported values include:
- Local UNIX socket (default):
>You must mount the socket file into the container using a volume, so the site can access it.
`unix:///var/run/docker.sock`
- TCP socket (e.g., via Docker Socket Proxy):
`tcp://localhost:2375`
- HTTP/HTTPS endpoints (e.g., remote Docker APIs):
`http://your-host:2375`
- SSH connections (experimental, requires SSH setup):
`ssh://user@host`
```yaml
services:
pangolin-site:
image: fosrl/pangolin-cli
container_name: pangolin-site
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
- PANGOLIN_ENDPOINT=https://example.com
- SITE_ID=2ix2t8xk22ubpfy
- SITE_SECRET=nnisrfsdfc7prqsp9ewo1dvtvci50j5uiqotez00dgap0ii2
- DOCKER_SOCKET=unix:///var/run/docker.sock
```
<Note>
If the Docker socket is not available or accessible, the site will gracefully disable Docker integration and continue normal operation.
</Note>
**Hostnames vs IPs**
When the Docker Socket Integration is used, depending on the network which the site is run with, either the hostname (generally considered the container name) or the IP address of the container will be sent to Pangolin:
- **Running in Network Mode 'host'**: IP addresses will be used
- **Running in Network Mode 'bridge'**: IP addresses will be used
- **Running in docker-compose without a network specification**: Docker compose creates a network for the compose by default, hostnames will be used
- **Running on docker-compose with defined network**: Hostnames will be used
**Docker Enforce Network Validation**
When run as a Docker container, the site can validate that the target being provided is on the same network as the site container and only return containers directly accessible by the site. Validation will be carried out against either the hostname/IP Address and the Port number to ensure the running container is exposing the ports to the site.
Validation is `false` by default. It can be enabled via setting the `--docker-enforce-network-validation` CLI argument or by setting the `DOCKER_ENFORCE_NETWORK_VALIDATION` environment variable.
<Warning>
If the site container is run with a network mode of `host`, this feature will not work. Running in `host` mode causes the container to share its resources with the host machine, making it impossible to retrieve specific host container information for network validation.
</Warning>
## Updown Scripts
You can pass in an updown script for the site to call when it is adding or removing a target:
```bash
--updown "python3 test.py"
```
The script will be called with arguments when a target is added or removed:
```bash
python3 test.py add tcp localhost:8556
python3 test.py remove tcp localhost:8556
```
<Info>
Returning a string from the script in the format of a target (`ip:dst` so `10.0.0.1:8080`) will override the target and use this value instead to proxy.
</Info>
<Note>
You can look at `updown.py` as a reference script to get started!
</Note>
## mTLS Authentication
The site supports mutual TLS (mTLS) authentication if the server is configured to request a client certificate. You can use either a PKCS12 (.p12/.pfx) file or split PEM files for the client cert, private key, and CA.
### Option 1: PKCS12 (Legacy)
<Note>
This is the original method and still supported.
</Note>
**Requirements:**
- File must contain:
- Client private key
- Public certificate
- CA certificate
- Encrypted `.p12` files are **not supported**
**Example:**
```bash
pangolin \
--id 31frd0uzbjvp721 \
--secret h51mmlknrvrwv8s4r1i210azhumt6isgbpyavxodibx1k2d6 \
--endpoint https://example.com \
--tls-client-cert ./client.p12
```
### Option 2: Split PEM Files (Preferred)
You can now provide separate files for:
- `--tls-client-cert-file`: client certificate (`.crt` or `.pem`)
- `--tls-client-key`: client private key (`.key` or `.pem`)
- `--tls-client-ca`: CA cert to verify the server (can be specified multiple times)
**Example:**
```bash
pangolin \
--id 31frd0uzbjvp721 \
--secret h51mmlknrvrwv8s4r1i210azhumt6isgbpyavxodibx1k2d6 \
--endpoint https://example.com \
--tls-client-cert-file ./client.crt \
--tls-client-key ./client.key \
--tls-client-ca ./ca.crt
```
+53
View File
@@ -0,0 +1,53 @@
---
title: "Site Credentials"
description: "Understanding how site credentials work and how they can be rotated & regenerated"
---
## Understanding Credentials
Every site is provisioned with a unique identifier (ID), secret, and endpoint. The site uses the combination of these three to establish a secure, encrypted connection to the server.
### ID
Example: `ln8yqs6w85la5zg`
The ID represents the site connection type in the system. Every Pangolin Site has an ID.
This value is not a secret and it is okay if made publically available.
### Secret
Example: `tfpwoc580jf1l1glfagix0o97p8kirjogdflqg604n0tr3to`
The secret represents the "password" of the site. This secret must match the secret hashed in the system for the relevant ID.
<Note>
This is a _secret_! Only share it with trusted people and be sure to store it safely and securely.
</Note>
When the site connects, it uses this secret as a first handshake with the server. The server then passes temporary session credentials back to the site before it can initiate a websocket connection. Once the websocket connection is established, ephemeral keys are used to establish tunnels using WireGuard.
### Endpoint
Example: `https://app.pangolin.net` or `https://pangolin.my-server.com`
The endpoint is how the site knows which server to connect to. This is the fully qualified hostname of the Pangolin server (the URL you use to access the dashboard). For Pangolin cloud, the endpoint is `https://app.pangolin.net`. The site uses this endpoint to establish a websocket connection and receive control messages from the server.
## Provisioning keys at scale
If you deploy many sites (for example IoT devices, golden images, or scripted installs), managing a unique ID and secret per host before first boot can be awkward. **[Site provisioning keys](/manage/sites/site-provisioning)** let each site exchange a single long-lived token for its own ID and secret on first connect, so you do not have to pre-create and distribute credentials for every machine individually.
## Rotating and Regenerating Credentials
<Note>
This is an [Enterprise Edition](/self-host/enterprise-edition)-only feature.
</Note>
Site credentials can be regenerated. Regenerating credentials will completely invalidate the previous ID and secret. Use this feature if you have lost the secret and need to reset the credentials, or if you wish to rotate credentials on a regular basis for extra security.
To regenerate credentials, visit Sites > Your Site > Credentials in the Pangolin admin dashboard.
### Regenerate vs. Regenerate and Disconnect
Regenerate simply recreates the credentials and invalidates the old ones. The site will remain connected until you restart it with the new credentials.
Regenerate and Disconnect recreates the credentials and invalides the old ones. The site will instantly disconnect and will require you to restart it with the new credentials.
+348
View File
@@ -0,0 +1,348 @@
---
title: "Install Newt"
description: "Install Newt as a binary or Docker container"
---
Newt is the lighter-weight site connector. It provides the same tunneling and NAT traversal as a Pangolin Site, without the rest of the [Pangolin CLI](/manage/sites/install-site) bundled in.
For new sites, install with the Pangolin CLI. Existing Newt deployments keep working. Leave them as they are, or switch to the CLI when you want to. Newt continues to be provided in all of its current forms. Use Newt directly when you need the smallest possible binary or container. The dashboard reports which agent a site is using, whether that is the CLI or Newt, and the version.
You must first create a site and copy the Newt config in Pangolin before running Newt.
<Note>
Deploying Newt in Kubernetes instead? See the dedicated [Kubernetes](/manage/sites/kubernetes/helm) docs: install guides for [Helm](/manage/sites/kubernetes/helm) and [Kustomize](/manage/sites/kubernetes/kustomize), a full [Configuration](/manage/sites/kubernetes/configuration) reference, and [Troubleshooting](/manage/sites/kubernetes/troubleshooting).
</Note>
## Binary Installation
### Quick Install (Recommended)
Use this command to automatically install Newt. It detects your system architecture automatically and always pulls the latest version, adding Newt to your PATH:
```bash
curl -fsSL https://static.pangolin.net/get-newt.sh | bash
```
#### Windows
To run Newt on Windows, use the latest installer from [GitHub releases](https://github.com/fosrl/newt/releases/latest).
### Manual Download
Binaries for Linux, macOS, and Windows are available in the [GitHub releases](https://github.com/fosrl/newt/releases/latest) for ARM and AMD64 (x86_64) architectures.
Download and install manually:
```bash
wget -O newt "https://github.com/fosrl/newt/releases/download/{version}/newt_{architecture}" && chmod +x ./newt
```
<Note>
Replace `{version}` with the desired version and `{architecture}` with your architecture. Check the [release notes](https://github.com/fosrl/newt/releases) for the latest information.
</Note>
### Running Newt
Run Newt with the configuration from Pangolin:
```bash
newt \
--id 31frd0uzbjvp721 \
--secret h51mmlknrvrwv8s4r1i210azhumt6isgbpyavxodibx1k2d6 \
--endpoint https://app.pangolin.net
```
### Systemd Service
Create `/etc/newt/newt.env` with the initial values from your Pangolin site configuration:
```bash title="/etc/newt/newt.env"
NEWT_ID=31frd0uzbjvp721
NEWT_SECRET=h51mmlknrvrwv8s4r1i210azhumt6isgbpyavxodibx1k2d6
PANGOLIN_ENDPOINT=https://app.pangolin.net
```
Create the directory, write the file, and restrict its permissions:
```bash
sudo install -d -m 0755 /etc/newt
sudo editor /etc/newt/newt.env
sudo chmod 600 /etc/newt/newt.env
```
Then create a systemd service. Newt reads those environment variables automatically, so `ExecStart` does not need to repeat them as flags:
```ini title="/etc/systemd/system/newt.service"
[Unit]
Description=Newt
Wants=network-online.target
After=network-online.target
[Service]
Type=simple
User=root
Group=root
EnvironmentFile=/etc/newt/newt.env
ExecStart=/usr/local/bin/newt
Restart=always
RestartSec=2
UMask=0077
PrivateTmp=true
[Install]
WantedBy=multi-user.target
```
<Warning>
Make sure the binary exists at `/usr/local/bin/newt` before daemon reload and starting the service:
```bash
which newt
```
</Warning>
Reload systemd and enable the service:
```bash
sudo systemctl daemon-reload
sudo systemctl enable --now newt
sudo systemctl status newt
```
If you later change `/etc/newt/newt.env`, restart the service so Newt picks up the new values.
See [Configure Sites](/manage/sites/configure-site) for more environment variables.
## Docker Installation
### Pull the Image
Pull the latest Newt image from Docker Hub:
```bash
docker pull fosrl/newt:latest
```
### Run with Docker
Run Newt with CLI arguments from Pangolin:
```bash
docker run -it fosrl/newt --id 31frd0uzbjvp721 \
--secret h51mmlknrvrwv8s4r1i210azhumt6isgbpyavxodibx1k2d6 \
--endpoint https://app.pangolin.net
```
### Docker Compose
#### Environment Variables (Recommended)
```yaml title="docker-compose.yml"
services:
newt:
image: fosrl/newt
container_name: newt
restart: unless-stopped
environment:
- PANGOLIN_ENDPOINT=https://app.pangolin.net
- NEWT_ID=2ix2t8xk22ubpfy
- NEWT_SECRET=nnisrfsdfc7prqsp9ewo1dvtvci50j5uiqotez00dgap0ii2
```
#### Config File Injected as Compose Secret
A safer but slightly more complex way is to use [Compose Secrets](https://docs.docker.com/compose/how-tos/use-secrets/). First, create a `JSON` file containing your configuration:
```json title="newt-config.secret"
{
"id": "2ix2t8xk22ubpfy",
"secret": "nnisrfsdfc7prqsp9ewo1dvtvci50j5uiqotez00dgap0ii2",
"endpoint": "https://app.pangolin.net"
}
```
Then register and reference the secret in your `docker-compose.yml`:
```yaml title="docker-compose.yml"
services:
newt:
image: fosrl/newt
container_name: newt
restart: unless-stopped
environment:
- CONFIG_FILE=/run/secrets/newt-config
secrets:
- newt-config
secrets:
newt-config:
file: ./newt-config.secret
```
This allows you to separate sensitive secrets from plain configuration, improving security when storing or sharing your `docker-compose.yml` anywhere else.
#### CLI Arguments
```yaml title="docker-compose.yml"
services:
newt:
image: fosrl/newt
container_name: newt
restart: unless-stopped
command:
- --id 31frd0uzbjvp721
- --secret h51mmlknrvrwv8s4r1i210azhumt6isgbpyavxodibx1k2d6
- --endpoint https://app.pangolin.net
```
#### Docker Healthcheck
Newt supports healthchecks as described in [Configure Sites](/manage/sites/configure-site#param-health-file). This file can be used to run a healthcheck from within Docker Compose.
Define the `HEALTH_FILE` environment variable as well as the healthcheck itself:
```yaml
environment:
- HEALTH_FILE=/tmp/healthy
healthcheck:
test: ["CMD-SHELL", "[ -f /tmp/healthy ]"]
interval: 30s
timeout: 5s
start_period: 30s
retries: 3
```
Start the service:
```bash
docker compose up -d
```
## Platform-Specific Installation
### Kubernetes
Running Newt in a Kubernetes cluster is covered separately from the Docker instructions above, since it uses a dedicated Helm chart rather than a plain `docker run` or Compose file. See:
<CardGroup cols={2}>
<Card title="Helm" href="/manage/sites/kubernetes/helm" icon="box">
Quick-start guide for installing Newt with Helm.
</Card>
<Card title="Kustomize" href="/manage/sites/kubernetes/kustomize" icon="layer-group">
Install Newt with rendered manifests and Kustomize overlays.
</Card>
<Card title="Configuration" href="/manage/sites/kubernetes/configuration" icon="sliders">
Full configuration reference for Helm and Kustomize workflows.
</Card>
<Card title="Troubleshooting" href="/manage/sites/kubernetes/troubleshooting" icon="circle-question">
Debug Newt deployment and connection issues in Kubernetes.
</Card>
</CardGroup>
### Unraid
Newt is available in the Unraid Community Applications store. Search for "Newt" and follow the installation prompts. Enter the ID, secret, and endpoint from Pangolin in the template fields.
<Frame caption="Newt available in Unraid Community Applications store">
<img src="/images/unraid_store.png" alt="Newt on CA" />
</Frame>
### Portainer and Other UIs
Container management UIs like Portainer typically allow passing commands and environment variables to containers similar to Docker Compose. Look for a commands or arguments configuration section and follow the relevant guides.
### Windows Service
On Windows, Newt can be run as a service or normally as a binary in a session.
When running as a binary and not installing the service, you can use the same CLI arguments as you would on any other platform.
If you are installing the service, we recommend writing the config into the config file location (`%PROGRAMDATA%\newt\newt-client\config.json`) first, following the [config file format](/manage/sites/install-newt#config-file-injected-as-compose-secret), with the provisioning key. Alternatively, you can install the service and start it with CLI args, which will also work. For example:
```
newt install
newt start --endpoint https://app.pangolin.net --provisioning-key <provisioning-key>
```
This will provision a new service, start it, and create the config file for future starts.
#### Service Management Commands
```
# Install the service
newt.exe install
# Start the service
newt.exe start
# Stop the service
newt.exe stop
# Check service status
newt.exe status
# Remove the service
newt.exe remove
# Show help
newt.exe service-help
```
Note running the service requires credentials in `%PROGRAMDATA%\newt\newt-client\config.json`.
#### Service Configuration
When running as a service, Newt will read configuration from environment variables or you can modify the service to include command-line arguments:
1. Install the service: `newt.exe install`
2. Set the credentials in `%PROGRAMDATA%\newt\newt-client\config.json`. Hint: if you run Newt once with `--id` and `--secret` this file will be populated.
3. Start the service: `newt.exe start`
#### Service Logs
When running as a service, logs are written to:
- Windows Event Log (Application log, source: "newtWireguardService")
- Log files in: `%PROGRAMDATA%\newt\logs\newt.log`
You can view the Windows Event Log using Event Viewer or PowerShell:
```powershell
Get-EventLog -LogName Application -Source "newtWireguardService" -Newest 10
```
### Advantech Router App
Download the correct version of the router app for your device from the [GitHub releases](https://github.com/fosrl/newt/releases/latest). You can find more information about router apps along with the right version information for your hardware on the [Advantech engineering portal](https://icr.advantech.com/products/software/router-apps).
To install the router app, log into your Advantech router and navigate to the Router Apps section. Upload the downloaded `.tgz` file and follow the prompts to install.
<Frame caption="Screenshot of installing the router app on an Advantech router UI">
<img src="/images/advantech_router_app_install.png" alt="Advantech router UI showing Newt router app installation" width="400"/>
</Frame>
After installation, click on the router app link at the top of the page to configure the app with your Newt credentials from Pangolin. Once you have entered the credentials, save and start the app. The router will now be connected to your Pangolin site and you can manage it like any other Newt site in the dashboard.
<Frame caption="Screenshot of configuring the router app on an Advantech router UI">
<img src="/images/advantech_router_app_configure.png" alt="Advantech router UI showing Newt credential configuration" width="400"/>
</Frame>
A complete config file is located at `/etc/newt/settings` on the router. You can edit this file directly to change credentials or add additional configuration options. After making changes, restart the router app to apply the new configuration. An example settings file can be found at: https://github.com/fosrl/newt/blob/main/packages/advantech/merge/etc/defaults
## Updating Newt
If you used the auto installer, run it again to pull the latest version:
```bash
curl -fsSL https://static.pangolin.net/get-newt.sh | bash
```
For Docker, pull the latest image and recreate the container:
```bash
docker pull fosrl/newt:latest
docker compose up -d
```
Find release notes and manual binaries in the [GitHub releases](https://github.com/fosrl/newt/releases).
+271
View File
@@ -0,0 +1,271 @@
---
title: "Install Sites"
description: "Install the site connector as a binary or Docker container"
---
The site connector can be installed as either a static binary executable or a Docker container via the CLI. You must first create a site and copy the ID and secret config from Pangolin before running it.
<Note>
The Pangolin CLI is the recommended way to install a new site. Existing Newt deployments keep working. For Newt-specific install commands, see [Install Newt](/manage/sites/install-newt).
</Note>
## Binary Installation (Linux, macOS, Windows)
### Install
Use this command to automatically install the CLI. It detects your system architecture automatically and always pulls the latest version, adding the CLI to your PATH:
```bash
curl -fsSL https://static.pangolin.net/get-cli.sh | bash
```
To run a site on Windows, [download the latest installer](https://github.com/fosrl/cli/releases/latest/download/pangolin-cli_windows_installer.msi).
Binaries for Linux, macOS, and Windows are available in the [GitHub releases](https://github.com/fosrl/cli/releases/latest) for ARM and AMD64 (x86_64) architectures.
### Running the Site
Run the site connector with the configuration from Pangolin:
```bash
pangolin up site \
--id 31frd0uzbjvp721 \
--secret h51mmlknrvrwv8s4r1i210azhumt6isgbpyavxodibx1k2d6 \
--endpoint https://app.pangolin.net
```
### Run as a Service
The CLI can install and manage a service on your host machine for you. This supports Windows services, MacOS's launchd, and Linux's systemd to create a persistent site connection from that host.
```bash
sudo pangolin service install site \
--id 31frd0uzbjvp721 \
--secret h51mmlknrvrwv8s4r1i210azhumt6isgbpyavxodibx1k2d6 \
--endpoint https://app.pangolin.net
```
Check the service status:
```bash
sudo pangolin service status site
```
And to get the logs:
```bash
sudo pangolin service logs site
```
### Manual Systemd Service
Create `/etc/pangolin/pangolin-site.env` with the initial values from your Pangolin site configuration:
```bash title="/etc/pangolin/pangolin-site.env"
SITE_ID=31frd0uzbjvp721
SITE_SECRET=h51mmlknrvrwv8s4r1i210azhumt6isgbpyavxodibx1k2d6
PANGOLIN_ENDPOINT=https://app.pangolin.net
```
Create the directory, write the file, and restrict its permissions:
```bash
sudo install -d -m 0755 /etc/pangolin
sudo editor /etc/pangolin/pangolin-site.env
sudo chmod 600 /etc/pangolin/pangolin-site.env
```
Then create a systemd service. The site connector reads those environment variables automatically, so `ExecStart` does not need to repeat them as flags:
```ini title="/etc/systemd/system/pangolin-site.service"
[Unit]
Description=Pangolin Site
Wants=network-online.target
After=network-online.target
[Service]
Type=simple
User=root
Group=root
EnvironmentFile=/etc/pangolin/pangolin-site.env
ExecStart=/usr/local/bin/pangolin up site
Restart=always
RestartSec=2
UMask=0077
PrivateTmp=true
[Install]
WantedBy=multi-user.target
```
<Warning>
Make sure the binary exists at `/usr/local/bin/pangolin` before daemon reload and starting the service: `~$ which pangolin`
</Warning>
Reload systemd and enable the service:
```bash
sudo systemctl daemon-reload
sudo systemctl enable --now pangolin-site
sudo systemctl status pangolin-site
```
If you later change `/etc/pangolin/pangolin-site.env`, restart the service so the site connector picks up the new values.
See [Configure Sites](/manage/sites/configure-site) for more environment variables.
## Docker Installation
### Pull the Image
Pull the latest image from Docker Hub:
```bash
docker pull fosrl/pangolin-cli:latest
```
### Run with Docker
Run the site connector with CLI arguments from Pangolin:
```bash
docker run -dit --network host fosrl/pangolin-cli up site --id 31frd0uzbjvp721 \
--secret h51mmlknrvrwv8s4r1i210azhumt6isgbpyavxodibx1k2d6 \
--endpoint https://app.pangolin.net
```
### Docker Compose
#### Environment Variables (Recommended)
```yaml title="docker-compose.yml"
services:
pangolin-site:
image: fosrl/pangolin-cli
container_name: pangolin-site
restart: unless-stopped
environment:
- PANGOLIN_ENDPOINT=https://app.pangolin.net
- SITE_ID=2ix2t8xk22ubpfy
- SITE_SECRET=nnisrfsdfc7prqsp9ewo1dvtvci50j5uiqotez00dgap0ii2
```
#### Config File Injected as Compose Secret
A safer but slightly more complex way is to use [Compose Secrets](https://docs.docker.com/compose/how-tos/use-secrets/). First, create a `JSON` file containing your configuration:
```json title="pangolin-site-config.secret"
{
"id": "2ix2t8xk22ubpfy",
"secret": "nnisrfsdfc7prqsp9ewo1dvtvci50j5uiqotez00dgap0ii2",
"endpoint": "https://app.pangolin.net"
}
```
Then register and reference the secret in your `docker-compose.yml`:
```yaml title="docker-compose.yml"
services:
pangolin-site:
image: fosrl/pangolin-cli
container_name: pangolin-site
restart: unless-stopped
command:
- up
- site
environment:
- CONFIG_FILE=/run/secrets/pangolin-site-config
secrets:
- pangolin-site-config
secrets:
pangolin-site-config:
file: ./pangolin-site-config.secret
```
This allows you to separate sensitive secrets from plain configuration, improving security when storing or sharing your `docker-compose.yml` anywhere else.
#### CLI Arguments
```yaml title="docker-compose.yml"
services:
pangolin-site:
image: fosrl/pangolin-cli
container_name: pangolin-site
restart: unless-stopped
command:
- up
- site
- --id 31frd0uzbjvp721
- --secret h51mmlknrvrwv8s4r1i210azhumt6isgbpyavxodibx1k2d6
- --endpoint https://app.pangolin.net
```
#### Docker Healthcheck
The site connector supports healthchecks as described in [Configure Sites](/manage/sites/configure-site#param-health-file). This file can be used to do a
healthcheck from within docker (compose file).
We need to define the `HEALTH_FILE` environment variable as well as the healthcheck itself:
```bash
environment:
- HEALTH_FILE=/tmp/healthy
healthcheck:
test: ["CMD-SHELL", "[ -f /tmp/healthy ]"]
interval: 30s
timeout: 5s
start_period: 30s
retries: 3
```
Start the service:
```bash
docker compose up -d
```
## Platform-Specific Installation
### Unraid
Pangolin Site is available in the Unraid Community Applications store. This template runs the Pangolin CLI (`fosrl/pangolin-cli`) and is the recommended way to install a site on Unraid. Use it instead of the Newt Community Applications listing for new sites.
Search for **Pangolin Site** and install the listing from **Fossorial's Repository**. That is the officially supported template.
<Frame caption="Pangolin Site in the Unraid Community Applications store">
<img src="/images/pangolin-site-community-apps.png" alt="Pangolin Site in the Unraid Community Applications store from Fossorial's Repository" />
</Frame>
Follow the installation prompts, then enter the site ID, secret, and endpoint from Pangolin in the template fields.
<Frame caption="Pangolin Site Unraid template with site credentials">
<img src="/images/pangolin-site-template.png" alt="Unraid Pangolin Site template showing PANGOLIN_ENDPOINT, SITE_ID, and SITE_SECRET" />
</Frame>
### Kubernetes
<Note>
The Kubernetes install still uses the legacy Newt for the Helm chart, namespace, and secret keys shown below.
</Note>
Running a site in a Kubernetes cluster is covered separately from the Docker instructions above, since it uses a dedicated Helm chart rather than a plain `docker run` or Compose file. See:
<CardGroup cols={2}>
<Card title="Helm" href="/manage/sites/kubernetes/helm" icon="box">
Quick-start guide for installing a site with Helm.
</Card>
<Card title="Kustomize" href="/manage/sites/kubernetes/kustomize" icon="layer-group">
Install a site with rendered manifests and Kustomize overlays.
</Card>
<Card title="Configuration" href="/manage/sites/kubernetes/configuration" icon="sliders">
Full configuration reference for Helm and Kustomize workflows.
</Card>
<Card title="Troubleshooting" href="/manage/sites/kubernetes/troubleshooting" icon="circle-question">
Debug site deployment and connection issues in Kubernetes.
</Card>
</CardGroup>
### Newt
Advantech routers, Windows services, and other Newt-only install methods are documented on [Install Newt](/manage/sites/install-newt). Use Newt when you want the smaller standalone connector instead of the Pangolin CLI.
@@ -0,0 +1,765 @@
---
title: "Configuration"
description: "Configuration reference for Newt Kubernetes deployments."
---
This page covers the main Newt 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/newt/README.md" />
<Card title="values.yaml" href="https://github.com/fosrl/helm-charts/blob/main/charts/newt/values.yaml" />
<Card title="values.schema.json" href="https://github.com/fosrl/helm-charts/blob/main/charts/newt/values.schema.json" />
</CardGroup>
## Version context
This page is aligned with the Newt Helm chart `1.4.0`.
| Item | Value |
| --- | --- |
| Chart version | `1.4.0` |
| App version | `1.12.3` |
| Kubernetes version | `>=1.30.14-0` |
| Default image | `docker.io/fosrl/newt:1.12.3` |
Chart `1.4.0` also publishes the Newt image metadata for Docker Hub and GHCR and includes Artifact Hub signing metadata.
## Configuration sections
<AccordionGroup>
<Accordion title="Image and global defaults" defaultOpen>
Use `global.image` to control the Newt container image used by all instances.
```yaml
global:
image:
registry: docker.io
repository: fosrl/newt
tag: ""
digest: ""
imagePullPolicy: IfNotPresent
imagePullSecrets: []
logLevel: INFO
```
Recommendations:
- Leave `tag` empty to use the chart `appVersion`.
- Use `digest` when you need immutable image pinning.
- Use `imagePullSecrets` when pulling from a private registry.
- Use per-instance overrides only when `allowGlobalOverride` is enabled for that instance.
</Accordion>
<Accordion title="Namespace and Pod Security Admission">
The chart can render Namespace resources, including Pod Security Admission labels.
```yaml
namespace:
create: false
name: ""
labels: {}
podSecurity:
enforce: ""
warn: ""
audit: ""
```
Recommended production pattern:
1. Create the namespace manually.
2. Apply required Pod Security Admission labels or policy labels.
3. Install the chart into that namespace.
```bash
kubectl create namespace pangolin
```
Example namespace labels:
```bash
kubectl label namespace pangolin \
pod-security.kubernetes.io/enforce=baseline \
pod-security.kubernetes.io/audit=restricted \
pod-security.kubernetes.io/warn=restricted
```
Per-instance namespace overrides are available when `allowGlobalOverride: true` is set:
```yaml
newtInstances:
- name: main-tunnel
allowGlobalOverride: true
namespace:
name: pangolin
create: false
labels: {}
podSecurity:
enforce: ""
warn: ""
audit: ""
```
<Note>
Creating the namespace manually is recommended when your cluster uses Pod Security Admission, policy labels, admission webhooks, or namespace annotations.
</Note>
</Accordion>
<Accordion title="Credentials and authentication">
For production, use an existing Kubernetes Secret.
```yaml
newtInstances:
- name: main-tunnel
enabled: true
auth:
existingSecretName: newt-auth
```
Create the Secret before installing the chart:
```bash
kubectl create secret generic newt-auth \
--namespace pangolin \
--from-literal=PANGOLIN_ENDPOINT=https://pangolin.example.com \
--from-literal=NEWT_ID=<newt-id> \
--from-literal=NEWT_SECRET=<newt-secret>
```
The default Secret keys are:
```yaml
PANGOLIN_ENDPOINT
NEWT_ID
NEWT_SECRET
```
Use `auth.keys.*` only when your Secret uses different key names:
```yaml
newtInstances:
- name: main-tunnel
enabled: true
auth:
existingSecretName: newt-auth
keys:
endpointKey: PANGOLIN_ENDPOINT
idKey: NEWT_ID
secretKey: NEWT_SECRET
```
`auth.keys.*` are Secret key names, not credential values.
Inline credentials are supported, but should only be used for local testing:
```yaml
newtInstances:
- name: main-tunnel
enabled: true
auth:
pangolinEndpoint: "https://pangolin.example.com"
id: "<newt-id>"
secret: "<newt-secret>"
```
<Warning>
Inline credentials can appear in rendered manifests and Helm release history. Use `auth.existingSecretName` for production.
</Warning>
<Warning>
Do not commit plaintext credentials to Git. For GitOps workflows, use encrypted or external secret backends such as SOPS, Sealed Secrets, External Secrets Operator, Vault, or Infisical.
</Warning>
Chart `1.4.0` also includes `auth.createSecret` and `auth.envVarsDirect` modes for generated Secret and direct environment-variable workflows. Use these only when they match your operational model.
</Accordion>
<Accordion title="Provisioning">
Provisioning supports installs where Newt bootstraps credentials from a provisioning key.
Use provisioning when Newt should bootstrap credentials from a provisioning key instead of using a static `NEWT_ID` and `NEWT_SECRET`.
```yaml
newtInstances:
- name: main-tunnel
enabled: true
auth:
pangolinEndpoint: https://pangolin.example.com
provisioningKey: "<provisioning-key>"
newtName: "my-site"
configPersistence:
enabled: true
type: emptyDir
mountPath: /var/lib/newt
fileName: config.json
```
Provisioning requires writable config persistence so Newt can store the generated configuration.
For durable storage, use an existing PVC:
```yaml
newtInstances:
- name: main-tunnel
enabled: true
auth:
pangolinEndpoint: https://pangolin.example.com
provisioningKey: "<provisioning-key>"
newtName: "my-site"
configPersistence:
enabled: true
type: persistentVolumeClaim
existingClaim: my-newt-config
mountPath: /var/lib/newt
fileName: config.json
```
You can also provide a provisioning blueprint:
```yaml
newtInstances:
- name: main-tunnel
enabled: true
auth:
pangolinEndpoint: https://pangolin.example.com
provisioningKey: "<provisioning-key>"
newtName: "my-site"
configPersistence:
enabled: true
type: emptyDir
provisioningBlueprintFile: /etc/newt/provisioning-blueprint.yaml
provisioningBlueprintData: |
version: 1
routes: []
```
</Accordion>
<Accordion title="Instance runtime settings">
Each Newt instance is configured under `newtInstances[]`.
```yaml
newtInstances:
- name: main-tunnel
enabled: true
replicas: 1
logLevel: INFO
mtu: 1280
dns: ""
pingInterval: ""
pingTimeout: ""
acceptClients: false
useNativeInterface: false
interface: newt
keepInterface: false
noCloud: false
disableClients: false
```
Key settings:
| Setting | Purpose |
| ------------------------------ | ----------------------------------------------------------- |
| `replicas` | Number of replicas for this Newt instance |
| `mtu` | WireGuard interface MTU |
| `dns` | Optional DNS server address pushed to the client |
| `pingInterval` / `pingTimeout` | Optional Newt ping timing overrides |
| `acceptClients` | Allows client connections at runtime |
| `useNativeInterface` | Uses native WireGuard interface when native mode is enabled |
| `noCloud` | Disables cloud connectivity |
| `disableClients` | Disables client connections |
<Note>
Newt 1.11 changed upstream ping defaults. Set `pingInterval` and `pingTimeout` explicitly if you need older timing behavior.
</Note>
</Accordion>
<Accordion title="Service and connectivity">
Service exposure is controlled separately from `acceptClients`.
```yaml
newtInstances:
- name: main-tunnel
enabled: true
service:
enabled: false
type: ClusterIP
port: 51820
testerPort: ""
externalTrafficPolicy: ""
loadBalancerSourceRanges: []
```
Important behavior:
- `acceptClients` does not create a Service.
- `newtInstances[].service.enabled` controls whether a Service is created.
- Tester port exposure is disabled by default unless enabled through test settings or explicit legacy tester-port configuration.
Common Service types:
| Type | Use case |
| -------------- | --------------------------------------------- |
| `ClusterIP` | Internal cluster access |
| `LoadBalancer` | External exposure through cloud load balancer |
| `NodePort` | Node-level port exposure |
</Accordion>
<Accordion title="Configuration persistence">
Use `configPersistence` when Newt needs writable configuration storage.
```yaml
newtInstances:
- name: main-tunnel
configPersistence:
enabled: false
type: emptyDir
mountPath: /var/lib/newt
fileName: config.json
existingClaim: ""
```
Storage types:
| Type | Behavior |
| ----------------------- | ----------------------------------------- |
| `emptyDir` | Ephemeral storage, recreated with the pod |
| `persistentVolumeClaim` | Durable storage using an existing PVC |
Provisioning-based installs should enable config persistence. For production provisioning, prefer a PVC over `emptyDir`.
<Warning>
`emptyDir` is recreated when a pod is replaced. Newt can require a reconnect and handshake after restart, which may briefly interrupt active traffic.
</Warning>
<Tip>
For production, prefer an existing PersistentVolumeClaim to keep writable Newt configuration across restarts and rescheduling.
</Tip>
</Accordion>
<Accordion title="Blueprints, mTLS, and scripts">
The chart supports blueprints, provisioning blueprints, mTLS certificate mounts, Docker socket mounts, and up/down scripts.
Blueprint example:
```yaml
newtInstances:
- name: main-tunnel
blueprintFile: /etc/newt/blueprint.yaml
blueprintData: |
version: 1
routes: []
```
Provisioning blueprint example:
```yaml
newtInstances:
- name: main-tunnel
provisioningBlueprintFile: /etc/newt/provisioning-blueprint.yaml
provisioningBlueprintData: |
version: 1
routes: []
```
mTLS using an existing PEM Secret:
```yaml
newtInstances:
- name: main-tunnel
mtls:
enabled: true
mode: pem
pem:
secretName: newt-mtls
clientCertPath: /certs/client.crt
clientKeyPath: /certs/client.key
caPath: /certs/ca.crt
```
Up/down scripts:
```yaml
global:
updownScripts:
route.sh: |
#!/bin/sh
echo "Newt interface changed"
newtInstances:
- name: main-tunnel
updown:
enabled: true
mountPath: /opt/newt/updown
```
<Note>
Use Secrets for certificates and sensitive script inputs. Avoid inline private keys or credentials in values files.
</Note>
</Accordion>
<Accordion title="Service accounts and RBAC">
ServiceAccount creation is enabled by default.
```yaml
serviceAccount:
create: true
name: ""
automountServiceAccountToken: false
```
RBAC is disabled by default in chart `1.4.0`:
```yaml
rbac:
create: false
clusterRole: false
```
Enable RBAC only when your selected configuration needs Kubernetes API permissions:
```yaml
rbac:
create: true
clusterRole: false
```
Per-instance ServiceAccount overrides are available when `allowGlobalOverride: true` is set:
```yaml
newtInstances:
- name: main-tunnel
allowGlobalOverride: true
serviceAccount:
create: true
name: newt-main-tunnel
automountServiceAccountToken: false
```
<Warning>
Chart `1.4.0` changed the RBAC default to `rbac.create=false`. Existing installations that relied on auto-created RBAC must opt in explicitly during upgrade.
</Warning>
</Accordion>
<Accordion title="Resources and scheduling">
Global resource requests and limits apply to Newt workloads.
```yaml
global:
resources:
requests:
cpu: 100m
memory: 128Mi
ephemeral-storage: 128Mi
limits:
cpu: 200m
memory: 256Mi
ephemeral-storage: 256Mi
```
Scheduling defaults:
```yaml
global:
priorityClassName: ""
nodeSelector: {}
tolerations: []
affinity:
nodeAffinity: {}
podAffinity: {}
podAntiAffinity: {}
topologySpreadConstraints: []
```
Pod Disruption Budget:
```yaml
global:
podDisruptionBudget:
enabled: false
minAvailable: 1
maxUnavailable: ""
```
Recommendations:
- Start with the chart defaults.
- Increase requests and limits based on traffic volume.
- Use node selectors, tolerations, affinity, or topology spread constraints when you need placement control.
- Enable a PodDisruptionBudget only when your replica count and maintenance policy support it.
<Warning>
Avoid CPU limits unless you explicitly need hard caps. CPU limits can trigger throttling even when spare node CPU exists. For most deployments, use CPU requests and memory limits as the starting point.
</Warning>
</Accordion>
<Accordion title="Health probes and Helm tests">
Health probes are disabled by default.
```yaml
global:
health:
enabled: false
path: /tmp/healthy
readinessFailureThreshold: 3
```
Per-instance health options:
```yaml
newtInstances:
- name: main-tunnel
healthFile: /tmp/healthy
enforceHcCert: false
```
Helm test jobs are disabled by default:
```yaml
global:
tests:
enabled: false
image:
repository: registry.k8s.io/kubectl
tag: "1.30.14"
pullPolicy: IfNotPresent
```
Enable tests only when you want chart test jobs and tester-port related resources.
</Accordion>
<Accordion title="Metrics and monitoring">
Metrics are disabled by default.
```yaml
global:
metrics:
enabled: false
port: 9090
path: /metrics
adminAddr: ":2112"
asyncBytes: false
region: ""
otlpEnabled: false
pprofEnabled: false
```
The default `adminAddr` is `:2112`, which listens on all interfaces and allows in-cluster scraping. Use `127.0.0.1:2112` only when scraping from other pods is not required.
Metrics Service:
```yaml
global:
metrics:
service:
enabled: false
type: ClusterIP
port: 2112
portName: metrics
```
Prometheus Operator resources:
```yaml
global:
metrics:
podMonitor:
enabled: false
serviceMonitor:
enabled: false
prometheusRule:
enabled: false
```
Example with ServiceMonitor:
```yaml
global:
metrics:
enabled: true
service:
enabled: true
serviceMonitor:
enabled: true
```
Optional pprof endpoint:
```yaml
global:
metrics:
pprofEnabled: true
```
</Accordion>
<Accordion title="Network policy">
NetworkPolicy rendering is disabled by default.
```yaml
global:
networkPolicy:
enabled: false
defaultMode: merge
components:
defaultApp:
enabled: true
dns:
enabled: false
kubeApi:
enabled: false
custom:
enabled: false
ruleSets: {}
```
Per-instance NetworkPolicy overrides:
```yaml
newtInstances:
- name: main-tunnel
networkPolicy:
enabled: null
mode: merge
useGlobalComponents:
defaultApp: true
dns: false
kubeApi: false
custom: true
components:
dns:
enabled: false
custom:
enabled: false
includeRuleSets: []
```
Modes:
| Mode | Behavior |
| --------- | ------------------------------------------------- |
| `inherit` | Use global components and rule sets only |
| `merge` | Combine global and instance-level policy settings |
| `replace` | Use only the instance-level policy settings |
Enable DNS egress rules if your default network policy blocks DNS.
</Accordion>
</AccordionGroup>
## Configuration by install method
### Helm
Use a values file:
```bash
helm upgrade --install newt fossorial/newt \
--namespace pangolin \
--values values-newt.yaml
```
Use inline values only for small tests:
```bash
helm upgrade --install newt fossorial/newt \
--namespace pangolin \
--set 'newtInstances[0].name=main-tunnel' \
--set 'newtInstances[0].auth.existingSecretName=newt-auth'
```
See [Site (newt) Helm](/manage/sites/kubernetes/helm) for the installation flow.
### Kustomize
Render the chart with Helm, then use Kustomize overlays:
```bash
helm template newt fossorial/newt \
--namespace pangolin \
--values values-newt.yaml \
> base/newt.yaml
```
Then apply an overlay:
```bash
kubectl apply -k overlays/site-a
```
See [Newt Kustomize](/manage/sites/kubernetes/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: |
newtInstances:
- name: main-tunnel
enabled: true
auth:
existingSecretName: newt-auth
```
Flux HelmRelease example:
```yaml
spec:
values:
newtInstances:
- name: main-tunnel
enabled: true
auth:
existingSecretName: newt-auth
```
See [GitOps](/self-host/manual/kubernetes/gitops/overview) for GitOps guidance.
## Next steps
<CardGroup cols={2}>
<Card title="Helm Install" href="/manage/sites/kubernetes/helm" icon="box">
Install Newt with Helm.
</Card>
<Card title="Kustomize Install" href="/manage/sites/kubernetes/kustomize" icon="layer-group">
Install Newt with rendered manifests and Kustomize overlays.
</Card>
<Card title="Troubleshooting" href="/manage/sites/kubernetes/troubleshooting" icon="circle-question">
Debug Newt deployment and connection issues.
</Card>
<Card title="GitOps" href="/self-host/manual/kubernetes/gitops/overview" icon="code-branch">
Deploy Newt with Argo CD or Flux.
</Card>
</CardGroup>
@@ -0,0 +1,410 @@
---
title: "Helm"
description: "Quick-start guide for installing Site (newt) on Kubernetes using Helm."
---
This guide installs and manages Site (newt) in Kubernetes using Helm.
See [Version Matrix](https://github.com/fosrl/helm-charts/VERSION_MATRIX.md) for chart and default app version references.
## What the chart supports
The Newt chart can deploy one or more Newt instances through `newtInstances[]`.
Newt chart `1.4.0` includes support for:
- provisioning with `NEWT_PROVISIONING_KEY` and `NEWT_NAME`
- legacy credential installs with `NEWT_ID` and `NEWT_SECRET`
- existing Kubernetes Secrets for production credentials
- writable config persistence with `emptyDir` or an existing PVC
- optional metrics, PodMonitor, ServiceMonitor, and PrometheusRule
- optional NetworkPolicy
- multi-instance deployments with per-instance overrides
The chart README lists these features for version `1.4.0`.
## Prerequisites
Before installing Newt, you need:
- Kubernetes `1.30.14` or newer
- Helm 3.x
- `kubectl` access to the target cluster
- a reachable Pangolin instance
- either:
- Newt credentials from Pangolin: `NEWT_ID` and `NEWT_SECRET`
- or a provisioning key for provisioning installs
The chart quickstart lists Kubernetes `>=1.30.14`, Helm 3.x, configured `kubectl`, and Newt credentials from Pangolin as prerequisites.
See [Prerequisites](/self-host/manual/kubernetes/prerequisites) for cluster, namespace, storage, networking, and security planning.
## Authentication options
Newt chart `1.4.0` supports three credential patterns:
| Method | Recommended for | Notes |
| --- | --- | --- |
| Existing Secret | Production | Credentials are stored in a Kubernetes Secret created outside Helm |
| Provisioning key | Provisioning installs | Requires writable config persistence |
| Inline values | Local testing only | Credentials may be stored in Helm release history |
For production, use `auth.existingSecretName` or a GitOps-safe secret workflow. The chart values explicitly warn that inline credentials can be stored in Helm release history and recommend existing Secrets for production.
## Quick install with existing Secret
This is the recommended simple production pattern.
### Step 1: Create the namespace
Create the namespace before installing the chart:
```bash
kubectl create namespace pangolin
```
If your cluster uses Pod Security Admission labels, namespace labels, or policy annotations, apply them before installing Newt.
Example:
```bash
kubectl label namespace pangolin \
pod-security.kubernetes.io/enforce=baseline \
pod-security.kubernetes.io/audit=restricted \
pod-security.kubernetes.io/warn=restricted
```
<Note>
The chart can create namespaces through `namespace.create`, but creating the namespace explicitly is recommended when your cluster uses Pod Security Admission, namespace labels, or policy annotations.
</Note>
### Step 2: Create the Newt Secret
Create a Secret with the credentials from Pangolin:
```bash
kubectl create secret generic newt-auth \
--namespace pangolin \
--from-literal=PANGOLIN_ENDPOINT=https://pangolin.example.com \
--from-literal=NEWT_ID=<your-newt-id> \
--from-literal=NEWT_SECRET=<your-newt-secret>
```
<Tip>
Get the Newt credentials from the Pangolin dashboard for the site you want this Newt instance to connect to.
</Tip>
### Step 3: Create a values file
Create `values-newt.yaml`:
```yaml
newtInstances:
- name: main-tunnel
enabled: true
auth:
existingSecretName: newt-auth
replicas: 1
```
The default Secret keys are:
```yaml
PANGOLIN_ENDPOINT
NEWT_ID
NEWT_SECRET
```
You only need to set `auth.keys.*` if your Secret uses different key names.
Example with custom Secret keys:
```yaml
newtInstances:
- name: main-tunnel
enabled: true
auth:
existingSecretName: newt-auth
keys:
endpointKey: PANGOLIN_ENDPOINT
idKey: NEWT_ID
secretKey: NEWT_SECRET
replicas: 1
```
`auth.keys.*` are key names inside the Kubernetes Secret, not the credential values themselves. ([GitHub][2])
### Step 4: Install Newt
Add the Helm repository:
```bash
helm repo add fossorial https://charts.fossorial.io
helm repo update fossorial
```
Install Newt:
```bash
helm upgrade --install newt fossorial/newt \
--namespace pangolin \
--values values-newt.yaml
```
Do not use `--create-namespace` here if you created and labeled the namespace manually.
### Step 5: Verify the deployment
Check the Helm release:
```bash
helm status newt --namespace pangolin
```
Check the pods:
```bash
kubectl get pods --namespace pangolin \
-l app.kubernetes.io/name=newt
```
Check the logs:
```bash
kubectl logs --namespace pangolin \
-l app.kubernetes.io/name=newt \
--tail=50
```
Wait for the Newt pod to become ready:
```bash
kubectl wait --for=condition=ready pod \
-l app.kubernetes.io/name=newt \
--namespace pangolin \
--timeout=60s
```
## Quick install with provisioning key
Provisioning-based installs bootstrap credentials from a provisioning key.
Provisioning requires writable config persistence so Newt can store the generated configuration. The chart quickstart explicitly notes that provisioning requires a writable `CONFIG_FILE` target and that the chart provides this through `newtInstances[x].configPersistence`. ([GitHub][3])
Create `values-newt.yaml`:
```yaml
newtInstances:
- name: main-tunnel
enabled: true
pangolinEndpoint: https://pangolin.example.com
provisioningKey: "<your-provisioning-key>"
newtName: "my-site"
configPersistence:
enabled: true
type: emptyDir
mountPath: /var/lib/newt
fileName: config.json
```
Install Newt:
```bash
helm upgrade --install newt fossorial/newt \
--namespace pangolin \
--values values-newt.yaml
```
<Note>
`emptyDir` is enough for testing, but it is ephemeral. For durable provisioning state, use `type: persistentVolumeClaim` with an existing PVC.
</Note>
Example with an existing PVC:
```yaml
newtInstances:
- name: main-tunnel
enabled: true
pangolinEndpoint: https://pangolin.example.com
provisioningKey: "<your-provisioning-key>"
newtName: "my-site"
configPersistence:
enabled: true
type: persistentVolumeClaim
existingClaim: my-newt-config
mountPath: /var/lib/newt
fileName: config.json
```
The Newt README includes both `emptyDir` and existing PVC provisioning examples. ([GitHub][4])
## Verifying connectivity
Follow the Newt logs:
```bash
kubectl logs --namespace pangolin \
-l app.kubernetes.io/name=newt \
--follow
```
In the Pangolin dashboard, verify that the site connected by this Newt instance is online.
If the pod is running but the site does not connect, check:
* `PANGOLIN_ENDPOINT`
* Newt credentials or provisioning key
* DNS resolution from inside the cluster
* outbound network access from the Newt pod
* TLS validity for the Pangolin endpoint
## Upgrade
Update the Helm repository:
```bash
helm repo update fossorial
```
Upgrade the release:
```bash
helm upgrade newt fossorial/newt \
--namespace pangolin \
--values values-newt.yaml
```
Check upgrade status:
```bash
helm status newt --namespace pangolin
helm history newt --namespace pangolin
```
Rollback to a previous revision if needed:
```bash
helm rollback newt <revision> --namespace pangolin
```
## Multiple Newt instances
You can deploy multiple Newt instances with one chart release.
Example:
```yaml
newtInstances:
- name: site-a
enabled: true
auth:
existingSecretName: newt-auth-site-a
replicas: 1
- name: site-b
enabled: true
auth:
existingSecretName: newt-auth-site-b
replicas: 1
```
Create a separate Secret for each site:
```bash
kubectl create secret generic newt-auth-site-a \
--namespace pangolin \
--from-literal=PANGOLIN_ENDPOINT=https://pangolin.example.com \
--from-literal=NEWT_ID=<site-a-newt-id> \
--from-literal=NEWT_SECRET=<site-a-newt-secret>
kubectl create secret generic newt-auth-site-b \
--namespace pangolin \
--from-literal=PANGOLIN_ENDPOINT=https://pangolin.example.com \
--from-literal=NEWT_ID=<site-b-newt-id> \
--from-literal=NEWT_SECRET=<site-b-newt-secret>
```
## Architecture notes
### Instance-based deployment
* `newtInstances[]` defines the Newt instances rendered by the chart.
* Each enabled instance creates its own workload.
* Each instance can use its own Secret, provisioning settings, resources, service settings, and network policy settings.
* Per-instance namespace and service account overrides require `allowGlobalOverride: true`.
The chart values include `newtInstances[]`, per-instance namespace settings, and per-instance service account overrides. ([GitHub][2])
### RBAC
Newt chart `1.4.0` defaults `rbac.create` to `false`. Enable RBAC only when your selected Newt configuration requires Kubernetes API permissions.
```yaml
rbac:
create: true
```
The chart changelog for `1.4.0` marks this as a breaking change: installations that relied on auto-created RBAC must explicitly enable `rbac.create=true` during upgrade. ([GitHub][1])
### Helm tests
Helm test Jobs are disabled by default.
Enable them only when you want to run chart test jobs:
```yaml
global:
tests:
enabled: true
```
The chart quickstart notes that test Jobs are gated behind `global.tests.enabled`, which defaults to `false`. ([GitHub][3])
## OCI install
The Newt chart is also published as an OCI chart in GHCR.
Pull the chart:
```bash
helm pull oci://ghcr.io/fosrl/helm-charts/newt \
--version 1.4.0
```
Install from OCI:
```bash
helm upgrade --install newt oci://ghcr.io/fosrl/helm-charts/newt \
--version 1.4.0 \
--namespace pangolin \
--values values-newt.yaml
```
OCI changes where Helm pulls the chart from. It does not change the values file or the release behavior.
## References
<CardGroup cols={2}>
<Card title="Chart README" href="https://github.com/fosrl/helm-charts/blob/main/charts/newt/README.md" icon="book" />
<Card title="values.yaml" href="https://github.com/fosrl/helm-charts/blob/main/charts/newt/values.yaml" icon="file-code" />
<Card title="values.schema.json" href="https://github.com/fosrl/helm-charts/blob/main/charts/newt/values.schema.json" icon="file-code" />
<Card title="Artifact Hub" href="https://artifacthub.io/packages/helm/fosrl/newt" icon="box" />
<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="/manage/sites/kubernetes/configuration" icon="sliders">
Review all Newt chart options.
</Card>
<Card title="Troubleshooting" href="/manage/sites/kubernetes/troubleshooting" icon="circle-question">
Debug Newt deployment and connection issues.
</Card>
<Card title="Kustomize Install" href="/manage/sites/kubernetes/kustomize" icon="layer-group">
Install Newt with rendered manifests and Kustomize overlays.
</Card>
<Card title="Pangolin Helm" href="/self-host/manual/kubernetes/pangolin/helm" icon="server">
Install the Pangolin control plane.
</Card>
</CardGroup>
@@ -0,0 +1,612 @@
---
title: "Kustomize"
description: "Deploy Newt on Kubernetes using Helm-rendered manifests and Kustomize overlays."
---
Use Kustomize when you want to manage Newt with rendered manifests, environment-specific overlays, and explicit patches in Git.
For Newt, the supported Kustomize workflow is:
1. Render the Newt Helm chart to manifests.
2. Use the rendered output as the Kustomize base.
3. Create overlays per site, cluster, or environment.
4. Apply the overlay with `kubectl apply -k` or reconcile it with Argo CD or Flux.
## When to use Kustomize for Newt
Use Kustomize if you:
- want site-specific or environment-specific overlays
- 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-site setup, use [Newt Helm](/manage/sites/kubernetes/helm).
## Supported approach
The Newt 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 Newt 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 Newt 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
newt-deployment/
├── base/
│ ├── kustomization.yaml
│ └── newt.yaml
├── overlays/
│ ├── site-a/
│ │ ├── kustomization.yaml
│ │ └── patches/
│ │ └── deployment-resources.patch.yaml
│ └── site-b/
│ ├── kustomization.yaml
│ └── patches/
│ └── deployment-resources.patch.yaml
└── values/
├── values-base.yaml
├── values-site-a.yaml
└── values-site-b.yaml
```
## Step 1: Create the namespace
Create the namespace before applying rendered manifests:
```bash
kubectl create namespace pangolin
```
If your cluster uses Pod Security Admission, namespace labels, or other policy labels, apply them before creating workloads.
Example:
```bash
kubectl label namespace pangolin \
pod-security.kubernetes.io/enforce=baseline \
pod-security.kubernetes.io/audit=restricted \
pod-security.kubernetes.io/warn=restricted
```
## Step 2: Create Newt credentials
Create a Kubernetes Secret for each Newt site or instance.
```bash
kubectl create secret generic newt-auth-site-a \
--namespace pangolin \
--from-literal=PANGOLIN_ENDPOINT=https://pangolin.example.com \
--from-literal=NEWT_ID=<site-a-newt-id> \
--from-literal=NEWT_SECRET=<site-a-newt-secret>
```
For a second site:
```bash
kubectl create secret generic newt-auth-site-b \
--namespace pangolin \
--from-literal=PANGOLIN_ENDPOINT=https://pangolin.example.com \
--from-literal=NEWT_ID=<site-b-newt-id> \
--from-literal=NEWT_SECRET=<site-b-newt-secret>
```
<Note>
Use existing Kubernetes Secrets for production. Do not commit Newt credentials into Helm values, rendered manifests, or Kustomize patches.
</Note>
## Step 3: Create base values
Create `values/values-base.yaml`:
```yaml
newtInstances:
- name: main-tunnel
enabled: true
replicas: 1
auth:
existingSecretName: newt-auth-site-a
```
This values file uses an existing Secret. The default Secret keys are:
```text
PANGOLIN_ENDPOINT
NEWT_ID
NEWT_SECRET
```
Use `auth.keys.*` only when your Secret uses different key names.
## Step 4: Render Newt to the base
Add and update the Helm repository:
```bash
helm repo add fossorial https://charts.fossorial.io
helm repo update fossorial
```
Render the Newt chart:
```bash
mkdir -p base overlays/site-a/patches overlays/site-b/patches values
helm template newt fossorial/newt \
--namespace pangolin \
--values values/values-base.yaml \
> base/newt.yaml
```
You can also render from the GHCR OCI chart:
```bash
helm template newt oci://ghcr.io/fosrl/helm-charts/newt \
--version 1.4.0 \
--namespace pangolin \
--values values/values-base.yaml \
> base/newt.yaml
```
## Step 5: Create the base kustomization
```yaml
# base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- newt.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 the rendered resource names
Before writing patches, check the generated names:
```bash
kustomize build base | grep -E "^(kind:| name:)"
```
Or list the deployments:
```bash
kustomize build base | yq '. | select(.kind == "Deployment") | .metadata.name'
```
Use the actual rendered Deployment name in your patch targets.
<Warning>
Do not assume the rendered Deployment name without checking the generated manifests. Helm naming can change with release name, chart name, `nameOverride`, or `fullnameOverride`.
</Warning>
## Step 7: Create site-specific overlays
Example overlay for Site A:
```yaml
# overlays/site-a/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
labels:
- pairs:
app.kubernetes.io/site: site-a
app.kubernetes.io/environment: production
patches:
- path: patches/deployment-resources.patch.yaml
target:
group: apps
version: v1
kind: Deployment
name: newt-main-tunnel
```
Example resource patch:
```yaml
# overlays/site-a/patches/deployment-resources.patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: newt-main-tunnel
spec:
replicas: 1
template:
spec:
containers:
- name: newt
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
memory: 256Mi
```
<Note>
Replace `newt-main-tunnel` with the actual Deployment name from your rendered manifests.
</Note>
Example overlay for Site B with a different Secret is usually better handled by rendering a second base with a different values file.
Create `values/values-site-b.yaml`:
```yaml
newtInstances:
- name: main-tunnel
enabled: true
replicas: 1
auth:
existingSecretName: newt-auth-site-b
```
Then render a separate base for Site B:
```bash
mkdir -p site-b/base
helm template newt-site-b fossorial/newt \
--namespace pangolin \
--values values/values-site-b.yaml \
> site-b/base/newt.yaml
```
<Info>
For different credentials, endpoints, provisioning keys, or instance names, prefer separate Helm-rendered bases. Use Kustomize patches for environment-level changes such as labels, annotations, resources, scheduling, or NetworkPolicy adjustments.
</Info>
## Common Kustomize patches for Newt
### Patch resource requests and limits
```yaml
# overlays/site-a/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patches:
- path: patches/resources.patch.yaml
target:
group: apps
version: v1
kind: Deployment
name: newt-main-tunnel
```
```yaml
# overlays/site-a/patches/resources.patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: newt-main-tunnel
spec:
template:
spec:
containers:
- name: newt
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
memory: 512Mi
```
### Patch log level
Prefer configuring log level through Helm values before rendering. If you still need a manifest patch, patch the generated environment variable carefully after inspecting the rendered Deployment.
Example JSON6902-style patch:
```yaml
# overlays/site-a/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patches:
- target:
group: apps
version: v1
kind: Deployment
name: newt-main-tunnel
patch: |-
- op: add
path: /spec/template/spec/containers/0/env/-
value:
name: LOG_LEVEL
value: DEBUG
```
<Warning>
Only use index-based JSON patches after checking the rendered manifest. Container order and environment variable layout can change between chart versions.
</Warning>
### Add node affinity
```yaml
# overlays/site-a/patches/node-affinity.patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: newt-main-tunnel
spec:
template:
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: site
operator: In
values:
- site-a
```
Reference the patch:
```yaml
# overlays/site-a/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patches:
- path: patches/node-affinity.patch.yaml
target:
group: apps
version: v1
kind: Deployment
name: newt-main-tunnel
```
### Add annotations
```yaml
# overlays/site-a/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patches:
- target:
group: apps
version: v1
kind: Deployment
name: newt-main-tunnel
patch: |-
- op: add
path: /metadata/annotations
value:
example.com/owner: platform
```
## Do not rename rendered Helm resources by default
Avoid Kustomize options such as `namePrefix` and `nameSuffix` for Helm-rendered bases unless you have validated every generated reference.
Renaming rendered resources can break:
* Service selectors
* Secret references
* ConfigMap references
* ServiceAccount references
* NetworkPolicy selectors
* Prometheus monitor selectors
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/site-a
```
Compare with the live cluster:
```bash
kustomize build overlays/site-a | kubectl diff -f -
```
Apply the overlay:
```bash
kubectl apply -k overlays/site-a
```
Verify the deployment:
```bash
kubectl get pods --namespace pangolin \
-l app.kubernetes.io/name=newt
kubectl logs --namespace pangolin \
-l app.kubernetes.io/name=newt \
--tail=50
```
## Updating the rendered base
When upgrading the Newt chart, re-render the base and review the changes.
```bash
helm repo update fossorial
```
Render the updated chart output:
```bash
helm template newt fossorial/newt \
--namespace pangolin \
--values values/values-base.yaml \
> base/newt.yaml
```
Or with OCI:
```bash
helm template newt oci://ghcr.io/fosrl/helm-charts/newt \
--version 1.4.0 \
--namespace pangolin \
--values values/values-base.yaml \
> base/newt.yaml
```
Validate the overlay:
```bash
kustomize build overlays/site-a
```
Review the diff:
```bash
git diff
kustomize build overlays/site-a | kubectl diff -f -
```
Commit the updated base and overlays:
```bash
git add base/ overlays/ values/
git commit -m "Update Newt rendered manifests"
```
Apply after review:
```bash
kubectl apply -k overlays/site-a
```
## Ownership model
Do not run `helm upgrade` against a release that is managed by Kustomize.
Avoid this pattern:
```bash
helm upgrade newt fossorial/newt --namespace pangolin
kubectl apply -k overlays/site-a
```
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. |
## Validation
Validate Kustomize output:
```bash
kustomize build overlays/site-a
```
Run a server-side dry run:
```bash
kustomize build overlays/site-a | kubectl apply -f - --dry-run=server
```
Preview live changes:
```bash
kustomize build overlays/site-a | kubectl diff -f -
```
Check live resources:
```bash
kubectl get all --namespace pangolin
kubectl get events --namespace pangolin --sort-by=.lastTimestamp
```
## 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
```
### Newt does not connect
Check logs:
```bash
kubectl logs --namespace pangolin \
-l app.kubernetes.io/name=newt \
--tail=100
```
Verify:
* the Secret exists in the same namespace
* `PANGOLIN_ENDPOINT` is reachable from the pod
* `NEWT_ID` and `NEWT_SECRET` are correct
* outbound DNS and HTTPS are allowed
* TLS certificates for the Pangolin endpoint are valid
## Next steps
<CardGroup cols={2}>
<Card title="Helm Install" href="/manage/sites/kubernetes/helm" icon="box">
Install Newt with Helm.
</Card>
<Card title="Configuration" href="/manage/sites/kubernetes/configuration" icon="sliders">
Review Newt chart options.
</Card>
<Card title="Troubleshooting" href="/manage/sites/kubernetes/troubleshooting" icon="circle-question">
Debug Newt deployment and connection issues.
</Card>
<Card title="GitOps" href="/self-host/manual/kubernetes/gitops/overview" icon="code-branch">
Deploy Kustomize overlays with Argo CD or Flux.
</Card>
</CardGroup>
@@ -0,0 +1,702 @@
---
title: "Troubleshooting"
description: "Diagnose and resolve common Newt Kubernetes deployment issues."
---
Use this guide to troubleshoot Newt Kubernetes deployments installed with Helm, Kustomize, Argo CD, or Flux.
Start with the basic checks, then move to the section that matches the symptom.
## Quick checks
Set the namespace and release name used by your installation:
```bash
export NEWT_NAMESPACE=pangolin
export NEWT_RELEASE=newt
```
Check the Helm release:
```bash
helm status "$NEWT_RELEASE" --namespace "$NEWT_NAMESPACE"
helm history "$NEWT_RELEASE" --namespace "$NEWT_NAMESPACE"
```
Check Newt pods:
```bash
kubectl get pods --namespace "$NEWT_NAMESPACE" \
-l app.kubernetes.io/name=newt
```
Check recent events:
```bash
kubectl get events --namespace "$NEWT_NAMESPACE" \
--sort-by=.lastTimestamp
```
Check logs:
```bash
kubectl logs --namespace "$NEWT_NAMESPACE" \
-l app.kubernetes.io/name=newt \
--tail=100
```
Check the applied Helm values:
```bash
helm get values "$NEWT_RELEASE" --namespace "$NEWT_NAMESPACE"
```
<Note>
Do not assume the pod or Deployment name. Chart-generated names can change with the Helm release name, instance name, `nameOverride`, or `fullnameOverride`.
</Note>
## Get the generated resource names
List Newt resources:
```bash
kubectl get deploy,sts,svc,secret,cm --namespace "$NEWT_NAMESPACE" \
-l app.kubernetes.io/name=newt
```
List pods with labels:
```bash
kubectl get pods --namespace "$NEWT_NAMESPACE" \
-l app.kubernetes.io/name=newt \
--show-labels
```
Store the first Newt pod name:
```bash
export NEWT_POD="$(kubectl get pod --namespace "$NEWT_NAMESPACE" \
-l app.kubernetes.io/name=newt \
-o jsonpath='{.items[0].metadata.name}')"
```
Then use:
```bash
echo "$NEWT_POD"
```
## Pod fails to start
### Symptoms
```text
STATUS RESTARTS
CrashLoopBackOff 5
Error 3
CreateContainerConfigError
ImagePullBackOff
```
### Check pod details
```bash
kubectl describe pod "$NEWT_POD" --namespace "$NEWT_NAMESPACE"
```
Check logs:
```bash
kubectl logs "$NEWT_POD" --namespace "$NEWT_NAMESPACE" --tail=100
```
If the container restarts quickly, check the previous logs:
```bash
kubectl logs "$NEWT_POD" --namespace "$NEWT_NAMESPACE" --previous --tail=100
```
### Common causes
| Symptom | Likely cause | Check |
| ----------------------------------- | ------------------------------------------------------ | ---------------------------------------------------------- |
| `Secret "..." not found` | Secret name does not match `auth.existingSecretName` | `kubectl get secret -n "$NEWT_NAMESPACE"` |
| Missing env var or empty credential | Secret exists but key names do not match `auth.keys.*` | `kubectl describe secret <secret> -n "$NEWT_NAMESPACE"` |
| Authentication failure | Wrong `NEWT_ID`, `NEWT_SECRET`, or provisioning key | Check credentials in Pangolin |
| Endpoint connection errors | `PANGOLIN_ENDPOINT` is wrong or unreachable | Test DNS and HTTPS from the pod |
| Image pull failure | Registry or image settings are wrong | `kubectl describe pod` |
## Secret issues
### Verify the Secret exists
```bash
kubectl get secret newt-auth --namespace "$NEWT_NAMESPACE"
```
### Check Secret keys
```bash
kubectl describe secret newt-auth --namespace "$NEWT_NAMESPACE"
```
The default keys are:
```text
PANGOLIN_ENDPOINT
NEWT_ID
NEWT_SECRET
```
If your Secret uses different key names, map them in values:
```yaml
newtInstances:
- name: main-tunnel
enabled: true
auth:
existingSecretName: newt-auth
keys:
endpointKey: PANGOLIN_ENDPOINT
idKey: NEWT_ID
secretKey: NEWT_SECRET
```
<Warning>
Do not paste decoded secrets into issue reports, logs, screenshots, or public repositories.
</Warning>
### Check which Secret the pod uses
```bash
kubectl get pod "$NEWT_POD" --namespace "$NEWT_NAMESPACE" \
-o jsonpath='{range .spec.containers[*].envFrom[*]}{.secretRef.name}{"\n"}{end}'
```
Also inspect explicit Secret references:
```bash
kubectl get pod "$NEWT_POD" --namespace "$NEWT_NAMESPACE" -o yaml | grep -A5 -B2 secretKeyRef
```
## Newt cannot reach Pangolin
### Test DNS from the Newt pod
```bash
kubectl exec "$NEWT_POD" --namespace "$NEWT_NAMESPACE" -- \
nslookup pangolin.example.com
```
### Test HTTPS from the Newt pod
```bash
kubectl exec "$NEWT_POD" --namespace "$NEWT_NAMESPACE" -- \
wget -S -O- https://pangolin.example.com 2>&1 | head -40
```
Depending on the image, `curl`, `wget`, `nc`, or `nslookup` may not be available. If needed, run a temporary debug pod in the same namespace:
```bash
kubectl run net-debug \
--namespace "$NEWT_NAMESPACE" \
--rm -it \
--image=curlimages/curl:latest \
--restart=Never \
-- sh
```
Then test:
```bash
curl -vk https://pangolin.example.com
```
### Common causes
| Problem | What to check |
| -------------------------------- | --------------------------------------------------------- |
| DNS fails | CoreDNS, NetworkPolicy egress to DNS, wrong hostname |
| HTTPS fails | ingress, TLS certificate, firewall, proxy, wrong endpoint |
| TLS verification fails | certificate chain, hostname mismatch, private CA |
| Works locally but not in cluster | egress policies, proxy settings, DNS split-horizon |
## Newt pod is running but site is offline
Check logs:
```bash
kubectl logs "$NEWT_POD" --namespace "$NEWT_NAMESPACE" --tail=200
```
Check the site in the Pangolin dashboard.
Verify:
* the site credentials belong to the same site
* the site was not deleted or regenerated in Pangolin
* `PANGOLIN_ENDPOINT` points to the correct Pangolin URL
* the cluster can resolve and reach the Pangolin endpoint
* outbound HTTPS is allowed from the Newt namespace
* the Secret is in the same namespace as the Newt workload
If you use provisioning, also verify:
* `provisioningKey` is valid
* `newtName` is set as expected
* `configPersistence.enabled=true`
* the configured `CONFIG_FILE` path is writable
## Provisioning issues
Provisioning requires writable config persistence.
### Symptoms
* Newt starts but does not keep generated credentials after restart.
* Newt provisions repeatedly.
* Logs mention config file or write errors.
* Pod restarts cause the site to appear as a new or unconfigured instance.
### Check values
```bash
helm get values "$NEWT_RELEASE" --namespace "$NEWT_NAMESPACE"
```
Provisioning example:
```yaml
newtInstances:
- name: main-tunnel
enabled: true
auth:
pangolinEndpoint: https://pangolin.example.com
provisioningKey: "<provisioning-key>"
newtName: "my-site"
configPersistence:
enabled: true
type: emptyDir
mountPath: /var/lib/newt
fileName: config.json
```
For durable state, use an existing PVC:
```yaml
newtInstances:
- name: main-tunnel
enabled: true
auth:
pangolinEndpoint: https://pangolin.example.com
provisioningKey: "<provisioning-key>"
newtName: "my-site"
configPersistence:
enabled: true
type: persistentVolumeClaim
existingClaim: my-newt-config
mountPath: /var/lib/newt
fileName: config.json
```
<Note>
`emptyDir` is recreated when the pod is recreated. Use a PVC if the generated configuration must survive pod replacement.
</Note>
## Service not created or not reachable
### Important behavior
`acceptClients` does not create a Service.
A Service is created through:
```yaml
newtInstances:
- name: main-tunnel
service:
enabled: true
```
The chart also has `service.enabledWhenAcceptClients`, but runtime client behavior and Service rendering should still be verified in the rendered manifests.
### Check Services
```bash
kubectl get svc --namespace "$NEWT_NAMESPACE" \
-l app.kubernetes.io/name=newt
```
Describe the Service:
```bash
kubectl describe svc <service-name> --namespace "$NEWT_NAMESPACE"
```
### LoadBalancer stuck in pending
```text
EXTERNAL-IP <pending>
```
Common causes:
* the cluster has no cloud load balancer integration
* bare-metal cluster without MetalLB or equivalent
* cloud provider quota or permission issue
* invalid `loadBalancerClass`
* invalid `loadBalancerSourceRanges`
For bare-metal clusters, use MetalLB or another load balancer implementation, or use `NodePort` if appropriate.
## Metrics scraping does not work
Metrics are disabled by default.
Enable metrics:
```yaml
global:
metrics:
enabled: true
```
The chart default admin address is:
```yaml
global:
metrics:
adminAddr: ":2112"
```
This listens on all interfaces and allows in-cluster scraping. Do not set it to `127.0.0.1:2112` if Prometheus scrapes from another pod.
### Metrics Service
Enable the metrics Service:
```yaml
global:
metrics:
enabled: true
service:
enabled: true
port: 2112
```
### ServiceMonitor
If you use Prometheus Operator:
```yaml
global:
metrics:
enabled: true
service:
enabled: true
serviceMonitor:
enabled: true
```
Check resources:
```bash
kubectl get svc,podmonitor,servicemonitor,prometheusrule \
--namespace "$NEWT_NAMESPACE" \
-l app.kubernetes.io/name=newt
```
<Note>
The chart has separate metrics values for container port, admin address, and metrics Service port. Check the rendered manifest when changing these values.
</Note>
## NetworkPolicy blocks traffic
If NetworkPolicy is enabled, check that the policy allows required egress.
Newt usually needs egress to:
* DNS
* Pangolin endpoint over HTTPS
* any tunnel or connectivity endpoints used by your deployment
Check policies:
```bash
kubectl get networkpolicy --namespace "$NEWT_NAMESPACE"
kubectl describe networkpolicy --namespace "$NEWT_NAMESPACE"
```
If DNS is blocked, enable or add DNS egress rules.
Example:
```yaml
global:
networkPolicy:
enabled: true
components:
dns:
enabled: true
```
If HTTPS egress is blocked, add an appropriate custom egress rule for your environment.
## Multiple Newt instances conflict
### Symptoms
* Multiple pods run, but only one site connects.
* Both instances use the same credentials.
* A site appears to flap between instances.
* Logs show authentication or registration conflicts.
### Check values
```bash
helm get values "$NEWT_RELEASE" --namespace "$NEWT_NAMESPACE"
```
Each instance should use its own credentials or provisioning identity:
```yaml
newtInstances:
- name: site-a
enabled: true
auth:
existingSecretName: newt-auth-site-a
- name: site-b
enabled: true
auth:
existingSecretName: newt-auth-site-b
```
Create separate Secrets:
```bash
kubectl create secret generic newt-auth-site-a \
--namespace "$NEWT_NAMESPACE" \
--from-literal=PANGOLIN_ENDPOINT=https://pangolin.example.com \
--from-literal=NEWT_ID=<site-a-newt-id> \
--from-literal=NEWT_SECRET=<site-a-newt-secret>
kubectl create secret generic newt-auth-site-b \
--namespace "$NEWT_NAMESPACE" \
--from-literal=PANGOLIN_ENDPOINT=https://pangolin.example.com \
--from-literal=NEWT_ID=<site-b-newt-id> \
--from-literal=NEWT_SECRET=<site-b-newt-secret>
```
## RBAC or service account issues
Chart `1.4.0` disables RBAC creation by default.
Check service account and RBAC:
```bash
kubectl get serviceaccount,role,rolebinding \
--namespace "$NEWT_NAMESPACE" \
-l app.kubernetes.io/name=newt
```
If your configuration requires Kubernetes API access, enable RBAC:
```yaml
rbac:
create: true
clusterRole: false
```
For most Newt deployments, RBAC is not required.
## High CPU or memory usage
Check resource usage:
```bash
kubectl top pod --namespace "$NEWT_NAMESPACE" \
-l app.kubernetes.io/name=newt
```
Check current resource settings:
```bash
kubectl get pod "$NEWT_POD" --namespace "$NEWT_NAMESPACE" \
-o jsonpath='{.spec.containers[0].resources}'
```
Tune resources in values:
```yaml
newtInstances:
- name: main-tunnel
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 1000m
memory: 512Mi
```
Then upgrade:
```bash
helm upgrade "$NEWT_RELEASE" fossorial/newt \
--namespace "$NEWT_NAMESPACE" \
--values values-newt.yaml
```
Common causes of high usage:
* high tunnel traffic
* too low resource limits
* repeated reconnect loops
* excessive debug logging
* MTU or network path issues
## MTU issues
### Symptoms
* Connections establish but large transfers fail.
* Some websites or services work, others hang.
* Logs show repeated reconnects.
* Throughput is much lower than expected.
Newt defaults to MTU `1280`.
Try another MTU only after confirming basic connectivity:
```yaml
newtInstances:
- name: main-tunnel
mtu: 1280
```
Upgrade after changing values:
```bash
helm upgrade "$NEWT_RELEASE" fossorial/newt \
--namespace "$NEWT_NAMESPACE" \
--values values-newt.yaml
```
## Helm debugging
Preview an upgrade:
```bash
helm upgrade "$NEWT_RELEASE" fossorial/newt \
--namespace "$NEWT_NAMESPACE" \
--values values-newt.yaml \
--dry-run
```
Render the chart locally:
```bash
helm template "$NEWT_RELEASE" fossorial/newt \
--namespace "$NEWT_NAMESPACE" \
--values values-newt.yaml
```
Show rendered manifests from the live release:
```bash
helm get manifest "$NEWT_RELEASE" --namespace "$NEWT_NAMESPACE"
```
Show values from the live release:
```bash
helm get values "$NEWT_RELEASE" --namespace "$NEWT_NAMESPACE"
```
Rollback:
```bash
helm rollback "$NEWT_RELEASE" <revision> --namespace "$NEWT_NAMESPACE"
```
## Kustomize debugging
Validate the overlay:
```bash
kustomize build overlays/site-a
```
Run a server-side dry run:
```bash
kustomize build overlays/site-a | kubectl apply -f - --dry-run=server
```
Preview live changes:
```bash
kustomize build overlays/site-a | kubectl diff -f -
```
If a patch does not apply, inspect generated resource names:
```bash
kustomize build base | grep -E "^(kind:| name:)"
```
## Collect diagnostics
Collect logs and resource information:
```bash
kubectl logs --namespace "$NEWT_NAMESPACE" \
-l app.kubernetes.io/name=newt \
--tail=200 > newt-logs.txt
kubectl get pods --namespace "$NEWT_NAMESPACE" \
-l app.kubernetes.io/name=newt \
-o yaml > newt-pods.yaml
kubectl get events --namespace "$NEWT_NAMESPACE" \
--sort-by=.lastTimestamp > newt-events.txt
helm get values "$NEWT_RELEASE" \
--namespace "$NEWT_NAMESPACE" > newt-helm-values.yaml
helm get manifest "$NEWT_RELEASE" \
--namespace "$NEWT_NAMESPACE" > newt-helm-manifest.yaml
```
If using Kustomize:
```bash
kustomize build overlays/site-a > newt-kustomize-output.yaml
```
Before sharing diagnostics, remove:
* Newt credentials
* provisioning keys
* TLS private keys
* tokens
* passwords
* internal hostnames if sensitive
## Next steps
<CardGroup cols={2}>
<Card title="Configuration" href="/manage/sites/kubernetes/configuration" icon="sliders">
Review Newt chart options.
</Card>
<Card title="Helm Install" href="/manage/sites/kubernetes/helm" icon="box">
Install Newt with Helm.
</Card>
<Card title="Kustomize Install" href="/manage/sites/kubernetes/kustomize" icon="layer-group">
Install Newt with rendered manifests and Kustomize overlays.
</Card>
<Card title="GitOps" href="/self-host/manual/kubernetes/gitops/overview" icon="code-branch">
Deploy Newt with Argo CD or Flux.
</Card>
</CardGroup>
@@ -0,0 +1,148 @@
---
title: "Site Provisioning Keys"
description: "Use long-lived provisioning tokens to bootstrap Pangolin sites at scale without pre-creating ID-secret pairs for every host"
---
## Why provisioning keys exist
As described in [Site credentials](/manage/sites/credentials), each Pangolin site authenticates with an ID and secret (random strings you get when a site is first created) plus an endpoint pointing at your Pangolin server. That model is simple for a handful of sites, but it breaks down quickly when you must issue and distribute unique credentials for many machines.
**IoT and edge fleets** are the classic case: hundreds or thousands of devices each need their own site identity. Before provisioning keys, you typically scripted against the API to mint an ID-secret pair per device, then pushed those secrets through your device-management or OTA layer so each unit could connect. That works, but it multiplies secret-handling paths and makes rotation and auditing harder.
The same friction shows up in other scenarios:
- **Golden images and OS images**: You want one trusted image (or cloud-init payload) shared across a class of machines, not a unique secret baked into every build artifact. A single provisioning key in the image, or injected at first boot, lets each instance obtain its own credentials the first time the site starts.
- **Scripted and CI-driven installs**: Ansible, Terraform, cloud-init, or installer scripts can drop the same provisioning key everywhere (or fetch it from a vault once) instead of coordinating “create site N, copy credentials to host N” for every node.
- **Developer and lab environments**: Spin up VMs or containers repeatedly without clicking through the dashboard for each site; tear them down and provision again with bounded keys (usage limits and expiry; see below).
- **MSP and multi-customer rollouts**: Standardize your onboarding bundle (endpoint + provisioning key + blueprint) while still giving each customer site isolated credentials after exchange.
With provisioning keys, you create one long-lived token in Pangolin, embed it in your image or distribute it with a single script, and each site exchanges that token for its own [site ID and secret](/manage/sites/credentials) on first connect.
## How provisioning works
You can supply the provisioning key in either of two ways:
**1. Config file**
Put the key in a JSON config file in a `provisioningKey` field (the value is the key string from Pangolin, often shown with an `spk` prefix) and point the site at that file:
```bash
pangolin up site --config-file /var/site.json
```
```json
{
"endpoint": "https://app.pangolin.net",
"provisioningKey": "spk_..."
}
```
**2. Command line**
Pass the key with **`--provisioning-key`** instead of storing it in the file. You still use **`--config-file`** so the site has a path to write and persist settings; see [Configure Sites](/manage/sites/configure-site) for the rest):
```bash
pangolin up site --config-file /var/site.json --endpoint https://app.pangolin.net --provisioning-key 'spk_...'
```
**After the site is provisioned**, the site writes the new `id` and `secret` into that config file. On later runs, the site uses those credentials and ignores the provisioning key (CLI flag or file field), because valid ID and secret are already present. If you used `provisioningKey` in JSON, that entry is removed when the file is updated, so the long-lived token is not left on disk. If you pass `--provisioning-key` instead, the site does not write that key into the config file, but anything that launched the site with the flag may still contain it.
From there the site authenticates over the websocket, optionally applies a blueprint if you passed one, then brings the tunnel online. The high-level sequence is summarized below.
<Frame>
<img src="/images/site-provisioning-flow.png" alt="Flow: provision with pre-shared key, exchange for ID and secret, apply YAML, pending approval, admin approves" />
</Frame>
<Note>
[Pangolin Blueprints](/manage/blueprints) are not required when using provisioning keys. You can provision with the key only and manage resources in the dashboard afterward. Blueprints are optional but convenient when you want resources and settings created automatically from YAML as soon as the site connects.
</Note>
### `--provisioning-blueprint-file` vs `--blueprint-file`
When you run a site with a provisioning key, you can attach a blueprint YAML file in two different ways:
| Flag | Behavior |
|------|------------|
| **`--blueprint-file`** | **Declarative.** The blueprint is the ongoing source of truth. The site keeps applying it, and changes you make in the dashboard can be overwritten the next time the blueprint is applied. |
| **`--provisioning-blueprint-file`** | **Imperative (bootstrap only).** The file is applied once, at provisioning time. After that, the site does not keep reapplying it. You can edit resources in the dashboard and those edits will not be overridden by that YAML on later runs. |
Use **`--provisioning-blueprint-file`** when you want automation to create an initial set of resources (for example from a fleet template) but you intend to manage or tune them in the UI afterward. Use `--blueprint-file` when you want the file to remain authoritative, the same as on a normal site without provisioning keys.
```bash
pangolin up site --config-file /var/site.json --provisioning-blueprint-file /path/to/bootstrap.yaml
```
If you do use blueprints together with provisioning keys, you get a repeatable pattern for large fleets: one key (with appropriate limits), a blueprint file, and optional environment-specific values so each host gets distinct resource names or domains without maintaining separate YAML per device. Pick the flag above based on whether that YAML should keep governing the site or only run at first connect.
### Blueprint example and environment templating
Blueprints can reference environment variables using `{{env.VARIABLE_NAME}}` syntax. At apply time, those placeholders are filled from the process environment running the site (for example a serial number, hostname, or customer slug exported before start). That lets one blueprint drive many sites: each host sets `SERIAL_NUMBER`, `CUSTOMER_ID`, or similar, and the resolved YAML defines unique site names, domains, or role assignments.
Below, `{{env.SERIAL_NUMBER}}` ties the private resource’s site field and the public resource’s hostname to the same per-device identity:
```yaml
private-resources:
ssh-resource:
name: SSH Server
mode: host
destination: localhost
site: "{{env.SERIAL_NUMBER}}-site"
tcp-ports: "22,3389"
udp-ports: "*"
disable-icmp: false
roles:
- Customer1
- DevOps
users:
- user@example.com
public-resources:
secure-resource:
name: Web Resource
protocol: http
full-domain: "{{env.SERIAL_NUMBER}}.example.com"
auth:
sso-enabled: true
sso-roles:
- Member
- Admin
sso-users:
- user@example.com
```
Use whatever variables match your deployment (for example asset tags or cloud instance IDs). Ensure those variables are set in the environment where the site runs before it applies the blueprint. For more on blueprint structure and applying YAML from a site, see the [Blueprints](/manage/blueprints) documentation.
### Optional site name (`--name`)
You can pass `--name` to the site when provisioning so the new site gets a specific name. The value supports the same `{{env.VARIABLE_NAME}}` templating as blueprints: placeholders are expanded from the environment where the site runs before the site is created (for example per-device serials or hostnames).
```bash
pangolin up site --config-file /var/site.json --name '{{env.SERIAL_NUMBER}}-edge'
```
If you omit `--name`, Pangolin assigns a random animal-based name, which you can change later in the dashboard. Explicit or templated names help when your automation or blueprint references the site by a stable label.
## Limits, expiry, and security model
Provisioning keys support a maximum usage count and an expiration time. For example, to roll out 250 devices over a week, set max usage to `250` and expiry to one week. When either limit is reached, the key becomes inactive and the server rejects further exchange attempts.
<Tip>
Provisioning keys are not API keys. They cannot authorize arbitrary Pangolin API calls; they exist only to bootstrap sites through the provisioning exchange.
</Tip>
## Creating a key and pending approval
In the Pangolin admin UI, create a provisioning key from the provisioning settings (including usage and expiration as needed). The flow is illustrated below.
<Frame>
<img src="/images/create-provisioning-key.png" alt="Create a provisioning key in the Pangolin dashboard" />
</Frame>
Optionally, sites provisioned with a key can be placed into a pending state. They appear under the Pending Sites tab on the provisioning page so administrators can review new sites and approve them before they are treated as fully active in production.
When a site is pending, any resources created for that site from a blueprint are pending as well. Pending resources are hidden from the main resources table and disabled by default, so end users cannot access them. That keeps the provisioned site and its blueprint resources in a staging setup until an admin confirms everything looks correct.
Admins can still inspect pending resources from the site: open **Resources** on the site row in the sites table, or go to the site’s edit page. Pending resources can be edited like any other resource. You can review configuration, enable a resource temporarily, and connect to verify that access works before promoting the site.
Approving a pending site marks all of its associated pending resources as not pending and enables them, moving the staged setup into production. Rejecting a pending site deletes all associated pending resources along with the site.
<Frame>
<img src="/images/pending-sites.png" alt="Pending sites listed for admin review" />
</Frame>
@@ -0,0 +1,56 @@
---
title: "Understanding Sites"
description: "Create a site to connect to a remote network and expose resources"
---
A site is a connection to a network where your resources live. Pangolin uses sites to make public and private resources available to users. Every resource belongs to one or more sites.
A Pangolin Site is the software connector that establishes this connection and routes traffic to targets on remote networks. In engineering contexts, and in some install commands, it is sometimes referred to as Newt. New installs should use the [Pangolin CLI](/manage/sites/install-site). Newt remains available as a [standalone connector](/manage/sites/install-newt) if you want a smaller binary or container.
## The Basics
- **Tunneled sites should always run behind a firewall.** Do not expose a site directly to the public internet.
- **Users do not connect to a site directly.** Admins define public (browser-based) or private resources on the site's network, and users connect to those resources.
- **You can run one or multiple sites per network.** You need at least one site to provide access, but you can run multiple sites in the same network for redundancy.
- **Sites are software-defined proxies and deny traffic by default.** Deploying a site does not automatically expose hosts. Admins must define resources and assign access.
## Site Types
Pangolin supports three different types of sites, each designed for different use cases and deployment scenarios.
### Pangolin Site (Recommended)
This site type exposes resources on a remote network through a managed tunnel and websocket connection. It requires the Pangolin Site connector on the remote network. This is the easiest setup and does not require NAT configuration.
Use Pangolin Sites in most deployments. This is the primary site type and supports the broadest feature set.
Pangolin Sites support (not limited to):
- Public proxied resources
- Protocol awareness (HTTP/HTTPS, SSH, RDP, VNC)
- Private resources (ZTNA)
- Load balancing and multi-site routing
- Health checking
- Docker socket scanning
- And more
### Local Site
Use this to expose resources on the same host as your Pangolin server (self-hosted only). No tunnels are created. Required ports must be open on the Pangolin host.
Use local sites when the resource runs on the same machine as your self-hosted Pangolin instance.
Local sites do not support:
- Private resources
- Health checking
- Docker socket scanning
### Basic WireGuard Site
This option is self-hosted only. It uses a raw WireGuard connection without a Pangolin Site connector, so there is no websocket control channel and setup is more manual. NAT is required to reach targets on other hosts in the remote network. Without NAT, you can expose only resources on the WireGuard peer host itself.
In general, use Basic WireGuard sites only for specific advanced use cases.
Basic WireGuard sites do not support:
- Using LAN-style addresses as targets
- Private resources
- Health checking
- Docker socket scanning
+43
View File
@@ -0,0 +1,43 @@
---
title: "Update Sites"
description: "Update your site to the latest version"
---
The update process depends on how you originally installed the site.
Find the latest version in the [GitHub releases](https://github.com/fosrl/cli/releases).
<Note>
If you installed Newt instead of the Pangolin CLI, see [Install Newt](/manage/sites/install-newt) for Newt-specific commands.
</Note>
## Docker Installation
Update the image version in your `docker-compose.yml`:
```yaml title="docker-compose.yml"
services:
pangolin-site:
image: fosrl/pangolin-cli:{version} # Replace {version} with the latest version tag
# ... rest of config
```
Then pull and restart:
```bash
docker compose pull pangolin-site
docker compose up -d pangolin-site
```
## Binary Installation
### Auto Installer (Recommended)
If you used the auto installer, simply run it again.
```bash
curl -fsSL https://static.pangolin.net/get-cli.sh | bash
```
### Manual Installation
Download the latest binary for your system from [GitHub releases](https://github.com/fosrl/cli/releases) and replace your existing binary.