--- title: "Blueprints" description: "Define Pangolin resources and site settings declaratively with YAML or container labels" --- Blueprints let you define Pangolin resources as code. Instead of configuring every site, target, and access rule manually in the dashboard, you describe the desired state in YAML or container labels and let Pangolin apply it consistently. Use blueprints when you want: - Repeatable rollouts across many sites - Version control for infrastructure and access settings - A source of truth that can be reviewed, templated, and automated Some features in this documentation are marked with **(EE)**, which means they require [Enterprise Edition](/self-host/enterprise-edition). ## Blueprint Mental Model A blueprint can contain up to four top-level sections: - **`public-resources`**: Internet-facing HTTP, TCP, UDP, SSH, RDP, or VNC resources - **`private-resources`**: Client-only access to hosts or CIDR ranges - **`public-policies`**: Reusable authentication and access policy objects - **`sites`**: Site-level settings such as container label discovery ```yaml public-resources: : ... private-resources: : ... public-policies: : ... sites: : ... ``` Think of the resource key as your stable ID inside the blueprint. The fields under that key describe what Pangolin should create or maintain. These keys are the same idea as the **Identifier** shown in the Pangolin app, called `niceId` in the API. Choose readable, stable keys such as `warehouse-west` or `web-app`; blueprints use them to match and update the same sites and resources on later applies. For more detail, see [What is an identifier?](/manage/common-api-routes#what-is-an-identifier). ## Choose A Format Pangolin supports two blueprint formats: ### YAML Use YAML when you want a readable file that can be committed to git, applied through a site, pasted into the UI, or sent through the API. ### Container Labels Use container labels when the resource definition should live inside your Compose stack. This is especially useful when a container and its Pangolin resource should be managed together. ## How YAML Blueprints Are Applied Paste YAML into **Settings > Blueprints** in the Pangolin dashboard. Blueprint creation page in the Pangolin dashboard Run a site with `--blueprint-file` to keep the file declarative and continuously applied: ```bash newt --blueprint-file /path/to/blueprint.yaml ``` If you only want a one-time bootstrap during provisioning, use [`--provisioning-blueprint-file`](/manage/sites/site-provisioning) instead. Apply a blueprint through the Pangolin API with an API key. See the [API documentation](https://api.pangolin.net/v1/docs/#/Organization/put_org__orgId__blueprint). `PUT /org/{orgId}/blueprint` ```json { "blueprint": "base64-encoded-json-content" } ``` [Python example](https://github.com/fosrl/pangolin/blob/dev/blueprint.py) Apply a blueprint directly from the Pangolin CLI when you want a one-off apply from a terminal, CI job, or local automation. **Using your logged-in user account** First log in and select the organization you want the blueprint applied to: ```bash pangolin login pangolin select org --org ``` Then apply the file: ```bash pangolin apply blueprint --file /path/to/blueprint.yaml ``` The CLI uses your active account and selected organization. You can optionally set the saved blueprint name: ```bash pangolin apply blueprint --file /path/to/blueprint.yaml --name production ``` **Using an Integration API key** For non-interactive automation, pass an Integration API key, API endpoint, and organization ID together: ```bash pangolin apply blueprint \ --file /path/to/blueprint.yaml \ --api-key \ --endpoint https://api.example.com \ --org ``` For Pangolin Cloud, use `https://api.pangolin.net` as the endpoint. For self-hosted Pangolin, use your API host, for example `https://api.your-domain.com`. See [Integration API](/manage/integration-api) for creating API keys and enabling the API on self-hosted deployments. You can also pipe a blueprint through stdin. When using stdin, provide `--name` because there is no filename to derive it from: ```bash render-blueprint | pangolin apply blueprint --file - --name production ``` `--blueprint-file` on a site and container labels behave as an ongoing source of truth. Dashboard edits can be overwritten the next time the blueprint is applied. UI, API, and CLI applies are typically one-off operations. ## Quick Start YAML Example This example shows three common top-level sections in one file: ```yaml public-resources: web-app: name: Web App mode: http full-domain: app.example.com auth: sso-enabled: true whitelist-users: - admin@example.com targets: - site: my-site hostname: app port: 8080 method: http healthcheck: hostname: app port: 8080 path: /health private-resources: ssh-host: name: SSH Host mode: host sites: - my-site destination: 192.168.1.10 tcp-ports: "22" roles: - DevOps sites: my-site: name: My Site docker-socket-enabled: true ``` ## Public Resources Public resources expose services through Pangolin. - Use **`http`** for websites, APIs, and dashboards - Use **`tcp`** or **`udp`** for raw public services bound to a port on the Pangolin server - Use **`ssh`**, **`rdp`**, or **`vnc`** for protocol-specific resources - Use **`inference`** for an [AI Gateway](/manage/ai/overview) resource that proxies requests to one or more attached AI providers Use `mode` for new blueprints. `protocol` is still accepted for backward compatibility and is normalized to `mode`. ### HTTP Resource Example ```yaml public-resources: app: name: App mode: http full-domain: app.example.com host-header: app.internal tls-server-name: app.internal headers: - name: X-Env value: production rules: - action: allow match: country value: US - action: deny match: path value: /admin auth: sso-enabled: true whitelist-users: - admin@example.com targets: - site: my-site hostname: app port: 8080 method: http ``` When applying a blueprint from a site (using `--blueprint-file` or container labels), `site` on each target is optional. If omitted, the target is assigned to the site that applied the blueprint. ### Raw TCP Or UDP Example ```yaml public-resources: mqtt: name: Mosquitto MQTT mode: tcp proxy-port: 1883 targets: - site: my-site hostname: mqtt-server port: 1883 ``` For raw resources: - `proxy-port` is required - Target `method` must not be set - `auth` is not supported ### Targets-Only Resources A public resource can contain only `targets`. This is useful when you want to add or manage targets for an existing resource definition without repeating all resource-level fields. ```yaml public-resources: extra-targets: targets: - site: secondary-site hostname: app-2 port: 8080 method: http - site: tertiary-site hostname: app-3 port: 8080 method: http ``` When a resource is targets-only, `name` and `mode` are not required. ### Authentication Example Authentication is configured inside `auth` and is supported only for HTTP resources. ```yaml public-resources: secure-app: name: Secure App mode: http full-domain: secure.example.com auth: pincode: 123456 password: strong-password basic-auth: user: demo password: change-me sso-enabled: true sso-roles: - Member sso-users: - user@example.com whitelist-users: - admin@example.com ``` ### Maintenance Page **(EE)** The `maintenance` object lets you present a maintenance page for a public HTTP resource. ```yaml public-resources: app: name: App mode: http full-domain: app.example.com maintenance: enabled: true type: automatic title: Scheduled Maintenance message: We are upgrading the service. estimated-time: 2 hours targets: - site: my-site hostname: app port: 8080 method: http ``` Maintenance `type` values: - **`forced`**: Always show the maintenance page - **`automatic`**: Show it only when all targets are unhealthy or the sites are offline ### AI Gateway (Inference) Example Set `mode: inference` to turn a public resource into an [AI Gateway](/manage/ai/overview) that proxies requests to one or more AI providers already configured in the organization. Inference resources do not take `targets`. ```yaml public-resources: ai-gateway: name: AI Gateway mode: inference full-domain: ai.example.com ai-providers: - provider: openai-prod access-mode: select models: - gpt-4o - provider: anthropic-prod access-mode: inherit ai-budget: - amount: 100 unit: usd period: monthly enforcement: hard ``` - `ai-providers` references AI providers by their identifier and is only allowed when `mode: inference`. - `access-mode: select` limits the resource to the listed `models`, which must already be on the provider's allow list. `access-mode: inherit` uses the provider's own allow and block lists. See [Model Routing](/manage/ai/providers/model-routing) for details. - `ai-budget` is a list of spend or usage limits enforced on the resource. Only one budget per `unit`/`period` combination is allowed. ## Private Resources Private resources define what Pangolin clients can reach after they connect to your organization. - Use **`mode: host`** for a single host or DNS name - Use **`mode: cidr`** for an entire network range - Use **`mode: http`** to expose an internal HTTP endpoint to clients via a private domain - Use **`mode: ssh`** for SSH access workflows (including native auth-daemon mode) - Use **`mode: inference`** for a private [AI Gateway](/manage/ai/overview) resource reachable only by Pangolin clients, not the public internet When applying a blueprint from a site (using `--blueprint-file` or container labels), `sites` is optional. If omitted, the resource is assigned to the site that applied the blueprint. ```yaml private-resources: internal-net: name: Internal Network mode: cidr destination: 10.0.0.0/24 sites: - my-site tcp-ports: "22,443,8000-9000" udp-ports: "53,123" disable-icmp: false alias: "*.internal.example.com" roles: - Developer users: - user@example.com machines: - machine-id-1 internal-app: name: Internal App mode: http destination: 10.0.0.5 destination-port: 8080 sites: - my-site full-domain: app.internal.example.com ssl: true scheme: https roles: - Member ``` ## Resource Labels Attach labels to public and private resources to organize and filter them in the dashboard. These are the same labels manageable from **Settings > Labels** - not to be confused with the [Docker container labels](#container-labels-format) used to define blueprints from Compose. ```yaml public-resources: app: name: App mode: http full-domain: app.example.com labels: - production - team-infra targets: - site: my-site hostname: app port: 8080 method: http ``` - Label names are matched case-insensitively against existing labels in the organization. - If a label with that name doesn't already exist yet, Pangolin creates it automatically. - Each apply replaces the resource's full label list with the list in the blueprint. - Labels are not synced for [targets-only](#targets-only-resources) public resources - omitting `labels` on a targets-only update leaves existing labels untouched. ## Container Labels Format Container labels are the same blueprint schema flattened into dot-separated keys: - Start every label with `pangolin.` - Keep the same object path as YAML - Use array indexes for lists, such as `[0]` Example YAML: ```yaml public-resources: my-app: headers: - name: X-Env value: prod ``` Equivalent Compose labels: ```yaml labels: - pangolin.public-resources.my-app.headers[0].name=X-Env - pangolin.public-resources.my-app.headers[0].value=prod ``` Container labels are continuously applied. Treat the Compose file as the source of truth because dashboard edits can be overwritten. ### Enable Container Label Discovery To use container labels, the site must be able to read the Docker socket: ```bash newt --docker-socket /var/run/docker.sock ``` Or with an environment variable: ```bash DOCKER_SOCKET=/var/run/docker.sock ``` ### Docker Compose Example ```yaml services: newt: image: fosrl/pangolin-cli container_name: pangolin-site restart: unless-stopped volumes: - /var/run/docker.sock:/var/run/docker.sock environment: - PANGOLIN_ENDPOINT=https://app.pangolin.net - SITE_ID=h1rbsgku89wf9z3 - SITE_SECRET=z7g54mbcwkglpx1aau9gb8mzcccoof2fdbs97keoakg2pp5z - DOCKER_SOCKET=/var/run/docker.sock nginx1: image: nginxdemos/hello container_name: nginx1 labels: - pangolin.public-resources.nginx.name=nginx - pangolin.public-resources.nginx.full-domain=nginx.fosrl.io - pangolin.public-resources.nginx.protocol=http - pangolin.public-resources.nginx.headers[0].name=X-Example-Header - pangolin.public-resources.nginx.headers[0].value=example-value - pangolin.public-resources.nginx.targets[0].method=http - pangolin.public-resources.nginx.targets[0].path=/path - pangolin.public-resources.nginx.targets[0].path-match=prefix nginx2: image: nginxdemos/hello container_name: nginx2 labels: - pangolin.public-resources.nginx.targets[1].method=http - pangolin.public-resources.nginx.targets[1].hostname=nginx2 - pangolin.public-resources.nginx.targets[1].port=80 networks: default: name: pangolin_default ``` This creates a single Pangolin resource with multiple targets: Example resource ### Container Label Behavior If `hostname` or `port` are not set explicitly, Pangolin can detect them from the container configuration. The hostname typically defaults to the container name, and port detection is based on the container's `expose` configuration. If no `site` is specified on a target (public resource) or on a private resource, it is assigned to the site that applied the blueprint, whether through container labels or `--blueprint-file`. Labels from multiple containers can be merged into one logical resource, which is useful when different containers contribute different targets. ## Configuration Reference Use this section when you need the full schema. The order below mirrors the blueprint structure rather than the dashboard UI. ### Top-Level Object ```yaml public-resources: app: name: App mode: http full-domain: app.example.com targets: - hostname: app port: 80 method: http private-resources: ssh-host: name: SSH Host mode: host sites: - my-site destination: 192.168.1.10 tcp-ports: "22" public-policies: default-member: name: Default Member Policy sso: true apply-rules: false rules: [] sites: my-site: name: My Site docker-socket-enabled: true ``` Public proxy resources keyed by resource ID. YAML: `public-resources: { web-app: { ... } }` Container label: `pangolin.public-resources.web-app.name=Web App` Private resources keyed by resource ID. YAML: `private-resources: { internal-net: { ... } }` Container label: `pangolin.private-resources.internal-net.mode=cidr` Reusable policy definitions keyed by policy ID. YAML: `public-policies: { default-member: { ... } }` Container label: `pangolin.public-policies.default-member.name=Default Member Policy` Site-level settings keyed by site ID. YAML: `sites: { my-site: { name: My Site } }` Container label: `pangolin.sites.my-site.name=My Site` Display name for the site. YAML: `name: My Site` Container label: `pangolin.sites.my-site.name=My Site` Enables blueprint discovery from container labels for that site. **Default**: `true` YAML: `docker-socket-enabled: true` Container label: `pangolin.sites.my-site.docker-socket-enabled=true` ### Public Resource Object (`public-resources`) ```yaml public-resources: web-app: name: Web App mode: http policy: default-member full-domain: app.example.com enabled: true labels: - production host-header: internal.example.local tls-server-name: internal.example.local headers: - name: X-Env value: prod rules: - action: allow match: country value: US - action: deny match: region value: 019 auth: pincode: 123456 sso-enabled: true whitelist-users: - admin@example.com maintenance: enabled: true type: automatic targets: - site: my-site hostname: app port: 8080 method: http path: / path-match: prefix rewrite-path: / rewrite-match: prefix healthcheck: hostname: app port: 8080 path: /health ``` A single public resource definition. Human-readable resource name. Required unless the resource is targets-only. YAML: `name: Web App` Container label: `pangolin.public-resources.web-app.name=Web App` Resource mode. Preferred over `protocol`. **Options**: `http`, `tcp`, `udp`, `ssh`, `rdp`, `vnc`, `inference` `inference` turns the resource into an [AI Gateway](/manage/ai/overview) that proxies to attached `ai-providers` instead of `targets`. YAML: `mode: http` Container label: `pangolin.public-resources.web-app.mode=http` Deprecated resource type field. Use `mode` instead. **Options**: `http`, `tcp`, `udp`, `ssh`, `rdp`, `vnc` YAML: `protocol: http` Container label: `pangolin.public-resources.web-app.protocol=http` Policy key to attach from `public-policies`. YAML: `policy: default-member` Container label: `pangolin.public-resources.web-app.policy=default-member` Optional SSL/TLS flag present in the schema. YAML: `ssl: true` Container label: `pangolin.public-resources.web-app.ssl=true` Public hostname for HTTP resources. Required when `protocol: http`. YAML: `full-domain: app.example.com` Container label: `pangolin.public-resources.web-app.full-domain=app.example.com` Public port for raw TCP or UDP resources. Required when `mode` is `tcp` or `udp`. YAML: `proxy-port: 3000` Container label: `pangolin.public-resources.raw-api.proxy-port=3000` Enables Proxy Protocol on TCP resources. YAML: `proxy-protocol: true` Container label: `pangolin.public-resources.raw-api.proxy-protocol=true` Proxy Protocol version when `proxy-protocol` is enabled. **Minimum**: `1` YAML: `proxy-protocol-version: 1` Container label: `pangolin.public-resources.raw-api.proxy-protocol-version=1` Disables the resource without removing it. YAML: `enabled: true` Container label: `pangolin.public-resources.web-app.enabled=true` [Resource labels](#resource-labels) to attach, by name. Labels that don't already exist in the organization are created automatically. Not synced when the resource is [targets-only](#targets-only-resources). Container labels for arrays must include an index (`[0]`, `[1]`, ...). YAML: `labels: [production]` Container label: `pangolin.public-resources.web-app.labels[0]=production` AI providers attached to an [AI Gateway](/manage/ai/overview) resource. Only allowed when `mode: inference`. Container labels for arrays must include an index (`[0]`, `[1]`, ...). YAML: `ai-providers: [{ provider: openai-prod, access-mode: inherit }]` Container label: `pangolin.public-resources.ai-gateway.ai-providers[0].provider=openai-prod` Identifier of an AI provider already configured in the organization. YAML: `provider: openai-prod` Container label: `pangolin.public-resources.ai-gateway.ai-providers[0].provider=openai-prod` How the resource uses the provider's model lists. `select` requires `models` to be set; `inherit` requires `models` to be empty. **Options**: `inherit`, `select` **Default**: `inherit` YAML: `access-mode: select` Container label: `pangolin.public-resources.ai-gateway.ai-providers[0].access-mode=select` Enables or disables this provider attachment. **Default**: `true` YAML: `enabled: true` Container label: `pangolin.public-resources.ai-gateway.ai-providers[0].enabled=true` Subset of the provider's allow-listed model keys exposed by this resource. Only valid when `access-mode: select`. Container labels for arrays must include an index (`[0]`, `[1]`, ...). YAML: `models: [gpt-4o]` Container label: `pangolin.public-resources.ai-gateway.ai-providers[0].models[0]=gpt-4o` Spend or usage budgets enforced on an [AI Gateway](/manage/ai/overview) resource. Each entry must use a unique `unit`/`period` combination. Container labels for arrays must include an index (`[0]`, `[1]`, ...). YAML: `ai-budget: [{ amount: 100, unit: usd, period: monthly }]` Container label: `pangolin.public-resources.ai-gateway.ai-budget[0].amount=100` Budget limit. Must be positive. YAML: `amount: 100` Container label: `pangolin.public-resources.ai-gateway.ai-budget[0].amount=100` Unit the budget is measured in. **Options**: `usd`, `tokens` YAML: `unit: usd` Container label: `pangolin.public-resources.ai-gateway.ai-budget[0].unit=usd` Rolling window the budget resets on. **Options**: `hourly`, `daily`, `weekly`, `monthly`, `yearly`, `lifetime` **Default**: `monthly` YAML: `period: monthly` Container label: `pangolin.public-resources.ai-gateway.ai-budget[0].period=monthly` What happens once the budget is exceeded. `hard` blocks further requests; `soft` allows them to continue. **Options**: `hard`, `soft` **Default**: `hard` YAML: `enforcement: hard` Container label: `pangolin.public-resources.ai-gateway.ai-budget[0].enforcement=hard` Enables or disables this budget. **Default**: `true` YAML: `enabled: true` Container label: `pangolin.public-resources.ai-gateway.ai-budget[0].enabled=true` Overrides the upstream `Host` header sent to the target. YAML: `host-header: internal.example.local` Container label: `pangolin.public-resources.web-app.host-header=internal.example.local` Overrides the TLS SNI hostname used for upstream TLS connections. YAML: `tls-server-name: internal.example.local` Container label: `pangolin.public-resources.web-app.tls-server-name=internal.example.local` Static headers added to proxied requests. Container labels for arrays must include an index (`[0]`, `[1]`, ...). YAML: `headers: [{ name: X-Env, value: prod }]` Container label: `pangolin.public-resources.web-app.headers[0].name=X-Env` Header name. YAML: `name: X-Env` Container label: `pangolin.public-resources.web-app.headers[0].name=X-Env` Header value. YAML: `value: prod` Container label: `pangolin.public-resources.web-app.headers[0].value=prod` Ordered access rules for public resources. Container labels for arrays must include an index (`[0]`, `[1]`, ...). YAML: `rules: [{ action: allow, match: country, value: US }]` Container label: `pangolin.public-resources.web-app.rules[0].action=allow` What Pangolin should do when the rule matches. **Options**: `allow`, `deny`, `pass` YAML: `action: allow` Container label: `pangolin.public-resources.web-app.rules[0].action=allow` Match type for the rule. **Options**: `cidr`, `path`, `ip`, `country`, `asn`, `region` YAML: `match: country` Container label: `pangolin.public-resources.web-app.rules[0].match=country` Value to compare against, such as an IP, CIDR, path, two letter country code, ASN, or region. YAML: `value: US` Container label: `pangolin.public-resources.web-app.rules[0].value=US` When using match: region, the value must be a supported region code. The following region IDs are available, based on the United Nations geoscheme:
  • 002: Africa
    • 015: Northern Africa
    • 014: Eastern Africa
    • 017: Middle Africa
    • 018: Southern Africa
    • 011: Western Africa
  • 019: Americas
    • 029: Caribbean
    • 013: Central America
    • 005: South America
    • 021: Northern America
  • 142: Asia
    • 143: Central Asia
    • 030: Eastern Asia
    • 035: South-Eastern Asia
    • 034: Southern Asia
    • 145: Western Asia
  • 150: Europe
    • 151: Eastern Europe
    • 154: Northern Europe
    • 039: Southern Europe
    • 155: Western Europe
  • 009: Oceania
    • 053: Australia and New Zealand
    • 054: Melanesia
    • 057: Micronesia
    • 061: Polynesia
Use the top-level code (e.g., 019 for all Americas) or a subregion code (e.g., 021 for Northern America) as needed.
Explicit rule priority. If omitted, priority is assigned from the rule order. YAML: `priority: 10` Container label: `pangolin.public-resources.web-app.rules[0].priority=10` Enables or disables the rule. **Default**: `true` YAML: `enabled: true` Container label: `pangolin.public-resources.web-app.rules[0].enabled=true`
Authentication settings for HTTP resources. Not allowed for `tcp` or `udp`. Numeric PIN required before access is granted. YAML: `pincode: 123456` Container label: `pangolin.public-resources.web-app.auth.pincode=123456` Shared password gate for the resource. YAML: `password: super-secret` Container label: `pangolin.public-resources.web-app.auth.password=super-secret` Enables Pangolin sign-in for the resource. YAML: `sso-enabled: true` Container label: `pangolin.public-resources.web-app.auth.sso-enabled=true` Roles allowed through SSO. Container labels for arrays must include an index (`[0]`, `[1]`, ...). YAML: `sso-roles: [Member]` Container label: `pangolin.public-resources.web-app.auth.sso-roles[0]=Member` Specific user identifiers allowed through SSO. Container labels for arrays must include an index (`[0]`, `[1]`, ...). YAML: `sso-users: [user@example.com]` Container label: `pangolin.public-resources.web-app.auth.sso-users[0]=user@example.com` Whitelisted emails or patterns allowed for email-based access flows. Container labels for arrays must include an index (`[0]`, `[1]`, ...). YAML: `whitelist-users: [admin@example.com]` Container label: `pangolin.public-resources.web-app.auth.whitelist-users[0]=admin@example.com` Identity provider ID to redirect to automatically. YAML: `auto-login-idp: 1` Container label: `pangolin.public-resources.web-app.auth.auto-login-idp=1` HTTP basic auth settings. Basic auth username. YAML: `user: demo` Container label: `pangolin.public-resources.web-app.auth.basic-auth.user=demo` Basic auth password. YAML: `password: change-me` Container label: `pangolin.public-resources.web-app.auth.basic-auth.password=change-me` Compatibility flag for basic auth behavior. **Default**: `true` YAML: `extendedCompatibility: true` Container label: `pangolin.public-resources.web-app.auth.basic-auth.extendedCompatibility=true` Maintenance page configuration for public resources **(EE)**. YAML: `maintenance: { enabled: true, type: forced }` Container label: `pangolin.public-resources.web-app.maintenance.enabled=true` Turns the maintenance page feature on for the resource. When Pangolin should show the page. **Options**: `forced`, `automatic` Main heading shown on the maintenance page. Message shown to users while the resource is unavailable. Optional estimate for when the service will return. Auth-daemon configuration for supported modes. PAM behavior. Passthrough will require entering the username, password, or private key when connecting. Push will sync a Pangolin user with the configuration from the roles onto the auth-daemon and not prompt a log in. **Options**: `passthrough`, `push` **Default**: `passthrough` Auth-daemon mode. To use the built in ssh server inside of the site connector use `native`. For external ssh servers, use `remote` or `site` depending on where the server is. **Options**: `site`, `remote`, `native` **Default**: `site` Remote auth-daemon port. Required when `auth-daemon.mode` is `remote`. Backend destinations for the resource. Not allowed when `mode: inference`. Container labels for arrays must include an index (`[0]`, `[1]`, ...). YAML: `targets: [{ hostname: app, port: 8080, method: http }]` Container label: `pangolin.public-resources.web-app.targets[0].hostname=app` Site that hosts the target. Optional when deploying from a site. If omitted, the target is assigned to the site that applied the blueprint. YAML: `site: my-site` Container label: `pangolin.public-resources.web-app.targets[0].site=my-site` Upstream protocol for HTTP resources. **Options**: `http`, `https`, `h2c` YAML: `method: http` Container label: `pangolin.public-resources.web-app.targets[0].method=http` Target hostname or IP address. YAML: `hostname: app` Container label: `pangolin.public-resources.web-app.targets[0].hostname=app` Target port. YAML: `port: 8080` Container label: `pangolin.public-resources.web-app.targets[0].port=8080` Disables the target without deleting it. YAML: `enabled: true` Container label: `pangolin.public-resources.web-app.targets[0].enabled=true` Internal port override used in container-oriented setups. YAML: `internal-port: 8080` Container label: `pangolin.public-resources.web-app.targets[0].internal-port=8080` Path condition used for HTTP routing. YAML: `path: /` Container label: `pangolin.public-resources.web-app.targets[0].path=/` Matching mode for `path`. **Options**: `exact`, `prefix`, `regex` Replacement path or prefix used during path rewriting. YAML: `rewrite-path: /` Container label: `pangolin.public-resources.web-app.targets[0].rewrite-path=/` Deprecated alias for `rewrite-path`. Rewrite mode. **Options**: `exact`, `prefix`, `regex`, `stripPrefix` Target priority used in routing decisions. **Range**: `1-1000` **Default**: `100` YAML: `priority: 100` Container label: `pangolin.public-resources.web-app.targets[0].priority=100` Health monitoring for the target. Hostname or IP used for the health check. Port used for the health check. Enables health checking for the target. HTTP path to check, such as `/health`. Scheme to use for the check. Health check mode supported by the schema. Check interval while the target is healthy. Check interval while the target is unhealthy. Deprecated alias for `unhealthy-interval`. Timeout for each health check attempt. Headers sent with the health check request. Container labels for arrays must include an index (`[0]`, `[1]`, ...). YAML: `headers: [{ name: X-Health-Check, value: true }]` Container label: `pangolin.public-resources.web-app.targets[0].healthcheck.headers[0].name=X-Health-Check` Header name. Header value. Whether redirects should be followed. Deprecated alias for `follow-redirects`. HTTP method for the check request. Expected HTTP status code.
### Private Resource Object (`private-resources`) ```yaml private-resources: internal-net: name: Internal Network mode: cidr sites: - my-site destination: 10.0.0.0/24 enabled: true tcp-ports: "22,443" udp-ports: "53" disable-icmp: false alias: "*.internal.example.com" roles: - Member users: - user@example.com machines: - machine-id-1 labels: - production internal-app: name: Internal App mode: http sites: - my-site destination: 10.0.0.5 destination-port: 8080 full-domain: app.internal.example.com ssl: true scheme: https enabled: true ``` A single private resource definition. Display name for the resource. YAML: `name: Internal Network` Container label: `pangolin.private-resources.internal-net.name=Internal Network` Private resource type. **Options**: `host`, `cidr`, `http`, `ssh`, `inference` - `host`: A single host or IP. If `destination` is a domain, `alias` is required. - `cidr`: An entire IPv4 or IPv6 CIDR range. - `http`: An internal HTTP endpoint exposed to clients via `full-domain`. - `ssh`: SSH access resource. `destination` may be omitted only when `auth-daemon.mode` is `native` (or when `auth-daemon` is omitted). - `inference`: A private [AI Gateway](/manage/ai/overview) exposed to clients via `full-domain`, proxying to attached `ai-providers` instead of a `destination`. YAML: `mode: cidr` Container label: `pangolin.private-resources.internal-net.mode=cidr` Sites that host the resource. Optional when deploying from a site. If omitted, the resource is assigned to the site that applied the blueprint. Container labels for arrays must include an index (`[0]`, `[1]`, ...). YAML: `sites: [my-site]` Container label: `pangolin.private-resources.internal-net.sites[0]=my-site` Deprecated. Use `sites` instead. YAML: `site: my-site` Container label: `pangolin.private-resources.internal-net.site=my-site` Host, IP, or CIDR block the client should reach. The accepted format depends on `mode`: - `host`: a valid IPv4/IPv6 address, or a hostname/domain (when using a domain, `alias` must also be set) - `cidr`: a valid IPv4 or IPv6 CIDR block - `http`: a host or IP for the upstream HTTP endpoint - `ssh`: optional only for `auth-daemon.mode: native`; required otherwise - `inference`: not used; the resource proxies to `ai-providers` instead YAML: `destination: 10.0.0.0/24` Container label: `pangolin.private-resources.internal-net.destination=10.0.0.0/24` Auth-daemon configuration. PAM behavior. Passthrough will require entering the username, password, or private key when connecting. Push will sync a Pangolin user with the configuration from the roles onto the auth-daemon and not prompt a log in. **Options**: `passthrough`, `push` **Default**: `passthrough` Auth-daemon mode. To use the built in ssh server inside of the site connector use `native`. For external ssh servers, use `remote` or `site` depending on where the server is. **Options**: `site`, `remote`, `native` **Default**: `site` Remote auth-daemon port. Required when `auth-daemon.mode` is `remote`. Upstream port for the destination. Typically used with `mode: http` to point at the internal HTTP endpoint's port. YAML: `destination-port: 8080` Container label: `pangolin.private-resources.internal-app.destination-port=8080` Internal domain used to expose an HTTP private resource to clients. Applies when `mode: http`. YAML: `full-domain: app.internal.example.com` Container label: `pangolin.private-resources.internal-app.full-domain=app.internal.example.com` Whether SSL/TLS should be used when serving the private HTTP resource. YAML: `ssl: true` Container label: `pangolin.private-resources.internal-app.ssl=true` Upstream scheme used by Pangolin to reach the destination for HTTP private resources. **Options**: `http`, `https` YAML: `scheme: https` Container label: `pangolin.private-resources.internal-app.scheme=https` Disables the resource without removing it. YAML: `enabled: true` Container label: `pangolin.private-resources.internal-net.enabled=true` Allowed TCP ports or ranges. Use comma-separated values for multiple ports or ranges, such as `22,443,8000-9000` or `*` for all ports or leave empty for no ports. **Default**: `*` YAML: `tcp-ports: "22,443"` Container label: `pangolin.private-resources.internal-net.tcp-ports=22,443` Allowed UDP ports or ranges. Use comma-separated values for multiple ports or ranges, such as `22,443,8000-9000` or `*` for all ports or leave empty for no ports. **Default**: `*` YAML: `udp-ports: "53"` Container label: `pangolin.private-resources.internal-net.udp-ports=53` Prevents ICMP traffic such as ping. **Default**: `false` YAML: `disable-icmp: false` Container label: `pangolin.private-resources.internal-net.disable-icmp=false` Internal DNS alias for the resource. Must be a fully qualified domain name and may include wildcards (`*`, `?`), e.g. `example.com`, `*.example.com`, or `host-0?.example.internal`. Required when `mode: host` and `destination` is a domain. YAML: `alias: "*.internal.example.com"` Container label: `pangolin.private-resources.internal-net.alias=*.internal.example.com` Roles allowed to access the resource. The `Admin` role is reserved and cannot be listed here. Container labels for arrays must include an index (`[0]`, `[1]`, ...). YAML: `roles: [Member]` Container label: `pangolin.private-resources.internal-net.roles[0]=Member` Individual users allowed to access the resource. Container labels for arrays must include an index (`[0]`, `[1]`, ...). YAML: `users: [user@example.com]` Container label: `pangolin.private-resources.internal-net.users[0]=user@example.com` Machine identities allowed to access the resource. Container labels for arrays must include an index (`[0]`, `[1]`, ...). YAML: `machines: [machine-id-1]` Container label: `pangolin.private-resources.internal-net.machines[0]=machine-id-1` [Resource labels](#resource-labels) to attach, by name. Labels that don't already exist in the organization are created automatically. Container labels for arrays must include an index (`[0]`, `[1]`, ...). YAML: `labels: [production]` Container label: `pangolin.private-resources.internal-net.labels[0]=production` AI providers attached to an [AI Gateway](/manage/ai/overview) resource. Only allowed when `mode: inference`. Same shape as `ai-providers` on [public resources](#public-resource-object-public-resources). Container labels for arrays must include an index (`[0]`, `[1]`, ...). YAML: `ai-providers: [{ provider: openai-prod, access-mode: inherit }]` Container label: `pangolin.private-resources.ai-gateway.ai-providers[0].provider=openai-prod` Spend or usage budgets enforced on an [AI Gateway](/manage/ai/overview) resource. Each entry must use a unique `unit`/`period` combination. Same shape as `ai-budget` on [public resources](#public-resource-object-public-resources). Container labels for arrays must include an index (`[0]`, `[1]`, ...). YAML: `ai-budget: [{ amount: 100, unit: usd, period: monthly }]` Container label: `pangolin.private-resources.ai-gateway.ai-budget[0].amount=100` ### Resource Policy Object (`public-policies`) ```yaml public-policies: default-member: name: Default Member Policy sso: true auto-login-idp: null sso-roles: - Member sso-users: [] password: null pincode: null basic-auth: null email-whitelist-enabled: false whitelist-users: - "*@example.com" apply-rules: true rules: - action: allow match: country value: US enabled: true ``` A reusable policy definition that can be referenced by public resources via `policy`. Display name for the policy. Enables SSO for the policy. **Default**: `true` Identity provider ID to redirect to automatically. Roles allowed by this policy. `Admin` is not allowed. Specific users allowed by this policy. Optional shared password. Optional 6-digit PIN. Optional basic-auth gate. Basic auth username. Basic auth password. Compatibility flag for basic auth behavior. **Default**: `true` Enables email whitelist matching. **Default**: `false` Allowed email addresses or wildcard-domain entries like `*@example.com`. Enables rule evaluation. **Default**: `false` Policy rule list using the same rule schema as public resource rules. ## Validation Rules And Constraints ### Core Rules 1. A public resource can be **targets-only**. In that case it may contain only `targets`, and `name` plus `mode` are not required. 2. For full public resources, `name` plus (`mode` or deprecated `protocol`) are required. 3. When mode/protocol is `http`, the resource must have `full-domain` and each target must include `method`. 4. When mode/protocol is `tcp` or `udp`, the resource must have `proxy-port`, targets must not include `method`, and `auth` is not allowed. 5. `proxy-protocol` and `proxy-protocol-version` are only valid when mode/protocol is `tcp`. 6. If `auth-daemon.mode` is `remote`, `auth-daemon.port` is required. 7. In private resources, `destination` is required unless `mode: ssh` with native auth-daemon mode, or `mode: inference`. 8. `full-domain` values must be unique across public resources. 9. `proxy-port` values must be unique per protocol within `public-resources`. TCP `3000` and UDP `3000` can coexist, but two TCP resources cannot both use `3000`. 10. `alias` values must be unique across private resources in the blueprint. 11. `mode: inference` (public or private) requires `full-domain`, must not have `targets`, and is the only mode that may set `ai-providers`. 12. `ai-budget` entries on a resource must each use a unique `unit`/`period` combination. ### Common Validation Errors ### "Admin role cannot be included in sso-roles" `Admin` is reserved and cannot be used in `auth.sso-roles`. ### "Duplicate 'full-domain' values found" Every public HTTP resource must have its own unique `full-domain`. ### "Duplicate 'proxy-port' values found in public-resources" Two public resources with the same protocol cannot reuse the same `proxy-port`. ### "When protocol is 'http', all targets must have a 'method' field" Each HTTP target must specify `http`, `https`, or `h2c`. ### "When protocol is 'tcp' or 'udp', targets must not have a 'method' field" Raw targets do not use HTTP methods. ### "When protocol is 'tcp' or 'udp', 'auth' must not be provided" Authentication settings apply only to HTTP public resources. ### "'proxy-protocol' and 'proxy-protocol-version' can only be set when mode is 'tcp'" Only TCP public resources can define proxy protocol behavior. ### "port is required when auth-daemon mode is 'remote'" Set `auth-daemon.port` whenever `auth-daemon.mode: remote` is used. ### "destination is required unless mode is 'ssh' with auth-daemon mode 'native'" For private SSH resources, `destination` can be omitted only for native auth-daemon mode. ### "Resource must either be targets-only or have both 'name' and 'protocol' fields" Provide both fields for a full public resource definition, or remove everything except `targets`. ### "Duplicate 'alias' values found in private-resources" Private resource aliases must be unique within the blueprint. ### "Destination must be a valid IP address or valid domain AND alias is required" In `host` mode, the destination must be a valid host or IP. If you use a domain, provide `alias` as well. ### "Destination must be a valid CIDR notation for cidr mode" In `cidr` mode, `destination` must be a valid CIDR block such as `10.0.0.0/24`. ### "Admin role cannot be included in roles" `Admin` is reserved and cannot be used in private resource `roles`. ### "'ai-providers' can only be set when mode is 'inference'" `ai-providers` is only valid on public or private resources with `mode: inference`. ### "When mode is 'inference', 'targets' must not be provided" Public resources with `mode: inference` proxy to `ai-providers` and cannot also define `targets`. ### "'ai-budget' entries must not overlap: only one budget per unit/period combination is allowed" Each entry in `ai-budget` must use a distinct `unit`/`period` pair, such as one `usd`/`monthly` budget and one `tokens`/`daily` budget on the same resource. ### "'models' can only be set on a provider with access-mode 'select'" Set `models` only when the `ai-providers` entry uses `access-mode: select`. Entries using `access-mode: inherit` must leave `models` empty.