# Introduction to Pangolin (/) > Connect and protect your users with an open-source networking and security platform Pangolin is an open-source SASE platform with one mission: connect and protect your users, wherever they are. It treats networking and security as a single system. Identity, sites, access control, privileged access, and an identity-aware AI gateway share one policy model. The idea is the same as platforms like Cloudflare One, Zscaler, and Prisma Access, but Pangolin is open, self-hostable, and built to stay light enough that administrators actually enjoy running it. Pangolin Dashboard ## Why Pangolin Exists [#why-pangolin-exists] Legacy SASE platforms got the idea right: connectivity and security belong together. They delivered it as a heavyweight, closed, cloud-locked stack. Pangolin does that unification in the open, on infrastructure you control, and simple enough to deploy yourself. * **Open source and auditable.** You can see how traffic is handled and how access decisions get made. * **Networking and security as one platform.** Connecting users and protecting them happen together, with one identity and policy model. * **Lightweight by design.** The control plane runs on a modest server. A user-space connector sits in your private networks. * **Zero trust from day one.** Access is granted per resource, with identity providers, roles, and audit logs. * **Run it your way.** Self-host or use [Pangolin Cloud](https://app.pangolin.net/auth/signup). ## Core Concepts [#core-concepts] Pangolin is organized around a few pieces you will use everywhere: 1. A **server** is the control plane. It holds identity, policy, and coordination. You can use [Pangolin Cloud](/about/pangolin-cloud-vs-self-hosted) or [self-host](/self-host/quick-install) the same software. 2. **[Sites](/manage/sites/understanding-sites)** connect the networks where your applications and infrastructure live. 3. **[Resources](/manage/resources/understanding-resources)** are the specific things users are allowed to reach: web apps, SSH, desktops, private hosts, and AI providers. 4. **[Clients](/manage/clients/understanding-clients)** give devices a private path to those resources. Learn the fundamentals: server, sites, resources, clients, and how they fit together. ## Explore the Platform [#explore-the-platform] Connect private networks with lightweight connectors so authorized users can reach what lives there. Define the apps, hosts, SSH sessions, desktops, and AI endpoints users can access. Use built-in users or your identity provider. Grant roles per resource and keep an audit trail. Put identity, budgets, and session history in front of cloud and self-hosted model APIs. ## Keep Reading [#keep-reading] Choose a managed control plane or run the same software on your own infrastructure. How Pangolin relates to traditional reverse proxies and VPNs, and what it does beyond either. How Pangolin's identity-aware AI Gateway compares to dedicated LLM gateways. A technical picture of the control plane, nodes, connectors, and how traffic moves. ## Deployment Options [#deployment-options] See [Cloud vs. Self-Hosted](/about/pangolin-cloud-vs-self-hosted) for the differences, including [remote nodes](/manage/remote-node/understanding-nodes) as a hybrid. A managed control plane. Install sites and clients, then define resources. Optionally self-host a node to keep traffic on your network. Run a fully isolated Pangolin server. Install Community Edition or Enterprise Edition with the quick installer. --- # How Pangolin Works (/about/how-pangolin-works) > Learn about the fundamentals of Pangolin and how they work together. This page explains Pangolin's concepts: the server, sites, resources, users, and clients. Read it first. For a more technical picture of the software components and how they interact, see [System Architecture](/development/system-architecture) after this page. ## Basic Steps [#basic-steps] Access remote networks using site connectors. Pangolin's lightweight connectors use intelligent routing and NAT traversal to make any network anywhere available. Once a site is created, you can access resources on that network from anywhere. Create resources for the things users should reach on the network of your site. Each resource follows a protocol: HTTPS in the browser, an IP or network range over the client, SSH, remote desktop, LLM access, and more. Resources can be public or fully private. Authenticated users access resources via the protocol of the resource. Commonly, public resources are accessed through a web browser, while private resources are accessed through a Pangolin client. The same users, roles, and policies apply across both. Users don't think about connecting to a site, they just access resources and Pangolin routes to the right connector automatically. ## Key Concepts [#key-concepts] Pangolin relies on several components that work together to provide secure networking. Each component has a specific role in ensuring that only authenticated users can access the resources they are authorized to use. ### Pangolin Server [#pangolin-server] The Pangolin server is the central coordination component for your network. It stores configuration changes, manages access policies, and coordinates connections between clients and sites. The server handles user authentication, generates access control lists that determine what resources each user can reach, hosts the UI and API, and more. It is the brains of your Pangolin network. You can use [Pangolin Cloud](https://app.pangolin.net/auth/signup), which is fully managed, or you can self-host your own Pangolin server for complete control over your infrastructure and data. See [Pangolin Cloud vs. Self-Hosted](/about/pangolin-cloud-vs-self-hosted). Fastest way to get started with the fully managed control plane. Learn how to deploy your own self-hosted Pangolin server. ### Sites [#sites] Sites connect remote networks to your Pangolin server. They use Pangolin Site connectors (sometimes referred by their engineering name, "Newt") to create secure tunnels from remote networks back to Pangolin. Sites let you expose [resources](/manage/resources/understanding-resources) on those networks to authorized users. Sites are an expected to entirely be managed by administrators and are typically set-and-forget. Users don't need to know about sites, they just access resources that are available on the site. Manage Sites page in the Pangolin dashboard Sites run behind firewalls on remote networks. They maintain outbound connections to the Pangolin server. By default, sites block all traffic until you define resources and grant access. This ensures that just [deploying a site](/manage/sites/install-site) does not expose any network resources. Create Site page in the Pangolin dashboard When [private resources](/manage/resources/understanding-resources#private-resource-types) (VPN-like access) are used, [clients](/manage/clients/understanding-clients) connect directly to the site connector using peer-to-peer (P2P) [NAT traversal](/manage/clients/nat-traversal). If the client is on the same network as the site connector, it will use the [local network address](/manage/clients/nat-traversal#same-network-detection). The site connector is very intelligent and handles tunnel creation, NAT traversal, and routing. It makes remote networks available without requiring complex firewall rules or public IP addresses. They also unlock browser-based [SSH](/manage/resources/public/ssh), [RDP](/manage/resources/public/rdp), and [VNC](/manage/resources/public/vnc) resources, [private HTTP](/manage/resources/private/private-http) with edge TLS termination, intelligent [multi-site routing](/manage/resources/private/multi-site-routing) when the same resource is reachable from more than one location, and much more. Learn about sites, how they work, and how to install and configure them. ### Resources [#resources] Pangolin is resource-based. A resource is the unit of access: you define it, grant users and roles, and Pangolin routes only authorized traffic. Users connect to resources, not to sites. Resources follow different protocols depending on what you are exposing (for example, but not limited to): * An HTTPS app, available in the browser * An IP address, reached through the Pangolin client * A network range, reachable through the Pangolin client * An [AI Gateway](/manage/ai/overview) resource for LLM access * Remote desktop * SSH The access model stays the same while the protocol changes. [Public resources](/manage/resources/understanding-resources#public-resource-types) are publically available proxies to your Pangolin server. Often, these are exposed as a public FQDN, like a website available in the web-browser or an API. Manage Public Resources page in the Pangolin dashboard [Private resources](/manage/resources/understanding-resources#private-resource-types) require a [Pangolin client](/manage/clients/platforms) connection and stay off the public internet. They are for VPN-like, fully private access to resources on your remote network. Manage Private Resources page in the Pangolin dashboard You must define resources and assign access before users can reach them. By default, no resources are available on sites. This ensures that only explicitly defined resources can be accessed. #### Resource Launcher [#resource-launcher] The Resource Launcher is the dashboard your users (non-admins) see. It lists every resource they are allowed to access in one place, grouped by site or label, with search and filters. Users open a web app, copy a hostname, or launch a private resource from that hub. Resource Launcher in the Pangolin dashboard Selecting a resource always expands a panel with the details that matter for that type: URL or alias, how to connect, and any extra setup. The screenshot below is an [AI Gateway](/manage/ai/overview) example. The same panel is used for every resource. Resource Launcher detail panel showing a private SSH resource as an example. Find, launch, and save views of the resources you can access. Learn about public and private resources and how to create them. ### Users and Roles [#users-and-roles] Identity lives in the same control plane as sites and resources. You manage your team in one place: Pangolin users, [identity providers](/manage/identity-providers/add-an-idp), and the [roles](/manage/access-control/create-user#roles) that grant access. Use Pangolin's built-in users, or bring Google Workspace, Microsoft Entra ID, Okta, or any OIDC provider. Users authenticate once. That identity applies to the dashboard, browser resources, the Pangolin client, the AI Gateway, and everything else. Roles group people for RBAC. You assign roles on each resource, so access follows the team rather than a separate list per protocol. A user's effective access is the union of what their roles can reach. The same users and roles apply to public resources, private resources, and AI Gateway resources. Manage Users page in the Pangolin dashboard Add internal or external users and assign roles for resource access. Let users sign in with Google, Microsoft Entra ID, Okta, or any OIDC provider. ### Clients [#clients] Clients are software components installed on user devices or machines. They let users and automated systems connect directly to sites to access [private resources](/manage/resources/understanding-resources#private-resource-types) through a secure tunnel. Clients also enforce access control and security at the edge. Users authenticate through the client with their [user credentials](/manage/access-control/create-user) in a web login flow. [Machines](/manage/clients/machine-clients) connect with machine client credentials. Once connected, users can reach all resources their account has access to. The client handles [routing](/manage/clients/nat-traversal) decisions and establishes encrypted tunnels to the appropriate [sites](/manage/sites/understanding-sites). User Devices page in the Pangolin dashboard Clients are available on [all major platforms](/manage/clients/platforms). They work transparently with applications, so no application configuration is required. Get the client for Mac, Windows, Linux, iOS, and Android. Learn how user and machine clients connect to private resources. ### AI Gateway [#ai-gateway] An [AI Gateway](/manage/ai/overview) is a special resource type for LLM access. It is protocol-aware, in the same way an HTTPS resource understands HTTP. Create Public Resource form with Type set to AI Gateway Pangolin already inspects HTTPS traffic to apply identity, access rules, [request logs](/manage/analytics/request), and analytics. An AI Gateway resource does the equivalent for model APIs. Because the gateway understands the LLM protocol, it can attribute each call to a user, record [cost and token usage](/manage/ai/usage-analytics), keep [chat session history](/manage/ai/session-logs), and enforce who can use which [models](/manage/ai/providers/model-routing) and [budgets](/manage/ai/budgets). Access is identity-based. You grant users and roles on the resource the same way you do for HTTPS or SSH. Pangolin then decides which models that identity may call and whether a budget still allows the request. AI Gateway session logs in the Pangolin dashboard A [private AI Gateway](/manage/resources/private/ai-gateway) uses the [Pangolin client](/manage/clients/platforms) the same way every other private resource does. The client running on the end user's device already authenticated that user. Coding agents on that device call the resource over the tunnel, and Pangolin attributes the request to the connected identity. That eliminates provider API keys on the laptop: the upstream key stays on the [provider](/manage/ai/providers/overview). Set up providers, resources, and identity-based access for coding agents and AI clients. ### Remote Nodes [#remote-nodes] Remote nodes are self-hosted Pangolin servers that you control while using Pangolin Cloud for management and coordination. You maintain complete control over your infrastructure and data flow, while the cloud handles the control plane, DNS, certificate management, and backups. You can deploy multiple remote nodes for high availability and automatic failover. If your nodes become unavailable, traffic can optionally fail over to cloud infrastructure until you restore service. Learn about remote nodes and how they provide high availability and simplified operations. ## System Architecture [#system-architecture] For a more technical picture of the software components and how they interact, see [System Architecture](/development/system-architecture). --- # Pangolin vs. Proxy vs. VPN (/about/pangolin-vs-reverse-proxy-vs-vpn) > What are the similarities and differences between Pangolin and traditional reverse proxies and VPNs? Pangolin combines the capabilities of both a reverse proxy and a VPN into a single platform. It provides reverse proxy functionality through public resources and VPN functionality through private resources, all with zero-trust access control and distributed architecture. ## What Each Solution Provides [#what-each-solution-provides] **Reverse Proxies** expose web applications to the internet. They handle SSL termination, load balancing, and basic authentication. Users access applications through web browsers using domain names. They are usually protocol-aware of HTTPS only. **VPNs** create encrypted tunnels that give users access to entire private networks as if they were on the local network. Users install client software and connect to a VPN server. Once connected, they can access any resource on the network they have network-level access to. **Pangolin** provides both capabilities, and several things neither traditional tool does on its own. [Public resources](/manage/resources/understanding-resources#public-resource-types) are publicly served proxies. That is often a website in a browser, but it can also be an API or an [AI Gateway](/manage/ai/overview). [Private resources](/manage/resources/understanding-resources#private-resource-types) work like a zero-trust VPN, giving users access to specific hosts or network ranges when connected with a client. ## What Pangolin Does Differently [#what-pangolin-does-differently] | Capability | Traditional reverse proxy | Traditional VPN | Pangolin | | --------------------------------- | ------------------------- | --------------- | ------------------------------------------------------------------ | | Publicly served proxies | Web apps | No | Yes: HTTP/HTTPS, APIs, [AI Gateway](/manage/ai/overview) | | Protocol-aware proxies | HTTPS | No | Yes: HTTPS, SSH, RDP, VNC, AI providers, and more | | Browser SSH, RDP, VNC | No | No | Yes — public SSH, RDP, VNC | | Client-only private access | No | Yes | Yes — host, CIDR, SSH, private HTTP | | Per-resource access control | Limited | Network-wide | Yes — users and roles per resource | | No open inbound ports | No | Sometimes | Yes — outbound site tunnels | | TLS at the network edge (private) | No | No | Yes — [private HTTP/HTTPS](/manage/resources/private/private-http) | | Multi-site routing and failover | Uncommon | Uncommon | Yes — automatic site selection | ## Reverse Proxy Capabilities [#reverse-proxy-capabilities] Pangolin's public resources function as reverse proxies. They are publicly served, not limited to web browsers. A traditional reverse proxy is usually protocol-aware of HTTPS only. Pangolin understands many protocols, including HTTPS, SSH, RDP, VNC, and AI provider APIs, so identity, access rules, and logging apply to each the same way. **HTTP/HTTPS** resources expose web applications and APIs through domain names with automatic SSL certificates. A browser is the common client; any HTTP client can call the same FQDN. Identity-aware access control supports SSO, MFA, and rules based on user identity, roles, geographic location, IP addresses, and URL paths. **AI Gateway** resources are public proxies for LLM APIs. Coding agents call the FQDN instead of the upstream provider. Pangolin authenticates the caller and applies identity, model access, and budgets. See [AI Gateway](/manage/ai/overview). **SSH, RDP, and VNC** resources render full sessions in the browser. Users get a terminal, Windows desktop, or VNC display without installing SSH clients or remote desktop software—while still passing through Pangolin authentication first. **TCP and UDP** resources bind to a port on the Pangolin server for raw protocol proxying when you need a public pipe without a domain name or auth layer. Unlike traditional reverse proxies, Pangolin does not require public IP addresses or open ports on your network. Sites create outbound tunnels to Pangolin, so your applications remain behind firewalls. ## VPN Capabilities [#vpn-capabilities] Pangolin's private resources function like a zero-trust VPN—but with tighter scope than a traditional VPN. **Host and CIDR** resources route traffic to specific machines or subnets over the tunnel. Users only reach what you explicitly grant them, with optional per-resource port restrictions—not an entire flat network. **Private HTTP/HTTPS** resources behave like a reverse proxy that only exists on the tunnel. TLS terminates at your [site edge over peer-to-peer transport](/manage/resources/private/private-http)—the application is never reachable from the public internet, only from connected clients with valid access. **[Private SSH](/manage/resources/private/ssh)** resources provide terminal access via `pangolin ssh`, with optional automatic user provisioning from Pangolin identity—no manual key distribution required. Clients work transparently with applications. No application configuration is required. Users connect once and can access all their authorized resources. The client handles routing and establishes encrypted tunnels automatically. ## Why Pangolin Combines Both [#why-pangolin-combines-both] Many organizations need both reverse proxy and VPN capabilities. You might want to expose a customer portal through a browser while also giving developers SSH access to internal servers and a private HTTPS dashboard that never touches the public internet. With Pangolin, you use one platform for all of these. Public resources handle publicly served access: websites in a browser, APIs, [AI Gateway](/manage/ai/overview) resources, and SSH, RDP, and VNC sessions. Private resources handle tunnel-only access to hosts, subnets, internal HTTPS apps, and CLI SSH. Both use the same authentication system, access control policies, and infrastructure. This unified approach simplifies management. You configure users, roles, and access policies once. Those policies apply to both public and private resources. You do not need to maintain separate systems for reverse proxy and VPN access. ## Infrastructure and Availability [#infrastructure-and-availability] Traditional reverse proxies and VPNs typically run on a single server. If that server fails, all access is lost. They also require public IP addresses and open ports, which adds complexity and security concerns. Pangolin uses a distributed architecture with multiple nodes. If one node fails, traffic automatically routes to another node. Sites create outbound tunnels, so your networks do not need public IP addresses or open ports. When a resource is reachable from multiple site connectors, Pangolin selects the healthiest path based on latency and availability—users connect to the resource, not to a specific site. You can deploy multiple remote nodes for high availability. If your nodes become unavailable, traffic can optionally fail over to cloud infrastructure until you restore service. ## When to Use Each Solution [#when-to-use-each-solution] Use a traditional reverse proxy if you only need to expose web applications over HTTP/HTTPS, you have a public IP address, and you do not need advanced access control or high availability. Use a traditional VPN if you need broad network access, you can accept the security risks of flat network visibility, and you do not need application-specific access control. Use Pangolin if you need both reverse proxy and VPN capabilities, publicly served APIs and AI gateways, browser SSH/RDP/VNC, private HTTPS with edge TLS termination, granular per-resource access control, multi-site routing, high availability, or outbound-only connectivity without open ports on your networks. --- # Pangolin vs. Bifrost vs. LiteLLM (/about/pangolin-vs-bifrost-vs-litellm) > What are the similarities and differences between Pangolin's AI Gateway and traditional LLM gateways like Bifrost and LiteLLM? Pangolin's [AI Gateway](/manage/ai/overview) does the work you would expect from a traditional LLM gateway: one URL in front of many providers, model routing, usage tracking, and access control. The difference is how it is built. It is a protocol-aware Pangolin resource, so identity, tunnels, and governance are the same system you already use for HTTPS and SSH. ## Why Use a Gateway [#why-use-a-gateway] Coding agents and AI clients want a provider URL and a key. If every laptop holds the org's OpenAI, Anthropic, or Gemini key, you cannot tell who spent what, you cannot limit which models a contractor may call, and rotating a leaked key means touching every machine. An AI gateway sits in front of those upstream APIs. Clients call the gateway. The gateway authenticates the caller, picks a provider and model, records usage, and forwards the request with the org's real key. LiteLLM and Bifrost are dedicated products for that job. Pangolin does it as an identity-aware resource on the same platform as the rest of your application and infrastructure access. ## A Protocol-Aware Resource [#a-protocol-aware-resource] An [AI Gateway resource](/manage/ai/overview) is not a separate stack. It is a Pangolin resource type, the same way HTTP/HTTPS is a type for web apps and SSH is a type for terminals. Pangolin already inspects HTTPS to apply identity, access rules, [request logs](/manage/analytics/request), and analytics. An AI Gateway resource does the equivalent for model APIs. Because it understands the LLM protocol, it can attribute each call to a user, record [cost and token usage](/manage/ai/usage-analytics), keep [chat session history](/manage/ai/session-logs), and enforce who can use which [models](/manage/ai/providers/model-routing) and [budgets](/manage/ai/budgets). That is the justification in Pangolin terms: an identity-aware gateway, not only a model router. ## What Each Solution Provides [#what-each-solution-provides] **[LiteLLM](https://docs.litellm.ai)** is a dedicated LLM proxy. It presents a unified API in front of many model providers, with virtual keys, spend tracking, fallbacks, and load balancing. Teams often run it as a self-hosted proxy or use its SDK inside applications. **[Bifrost](https://www.getmaxim.ai/bifrost)** is a dedicated LLM gateway focused on complex routing rules, failover, and performance. For example, you can use it to route requests to different models based on the user's location or the request's content. You can also run it downstream of Pangolin as a [Custom provider](/manage/ai/providers/custom/bifrost) when Pangolin should authenticate callers and Bifrost should pick models. **Pangolin** provides the same gateway job as a [protocol-aware resource](/about/how-pangolin-works#ai-gateway). Providers, model lists, [virtual API keys](/manage/ai/virtual-api-keys) on public resources, budgets, and session logs are all there. Identity comes from Pangolin users, roles, and (for private AI gateway resources) the [desktop client](/manage/clients/platforms). Site and client tunnels are how you reach self-hosted models and how you keep provider keys off laptops. ## Identity-Aware Gateway [#identity-aware-gateway] Access follows the resource. You grant [users and roles](/manage/access-control/create-user) the same way you do for other Pangolin resources. Pangolin then decides which models that identity may call and whether a [budget](/manage/ai/budgets) still allows the request. On a [public AI Gateway](/manage/resources/public/ai-gateway), coding agents send a [virtual API key](/manage/ai/virtual-api-keys). Pangolin identity keys identify the user; you grant the user or role, not the key. On a [private AI Gateway](/manage/resources/private/ai-gateway), the [Pangolin client](/manage/clients/platforms) running on the end user's device already authenticated that user. Coding agents on that device call over the tunnel fully privately. Pangolin attributes the request to the connected identity. That eliminates provider API keys on the laptop: the upstream key never leaves the provider. ## Virtual API Keys [#virtual-api-keys] On other gateways you mint a key for each person, then keep track of who holds which key and which models that key may call. Pangolin [virtual API keys](/manage/ai/virtual-api-keys) work the other way around. Every user already has an **identity key**. You do not grant the key access to the gateway. You grant the [user or role](/manage/access-control/create-user) access to the AI Gateway resource and its models. The key only identifies the caller as that user. When you change who is on the resource, the key follows. You do not manage a key inventory as the source of access. Users retrieve their own key: * Log in at the public gateway URL in a browser, using Pangolin or their [identity provider](/manage/identity-providers/add-an-idp). After login, Pangolin presents the key. * Open the Resource Launcher, pick the resource, and copy the key. The launcher also shows which models they can use and how to configure a coding agent to use the key. * Admins can [email keys](/manage/ai/virtual-api-keys#email-keys-to-users) to users. You can still mint **manual keys**, the same way other gateways issue arbitrary virtual keys. Use those for services, machines, CI, or a person if you want a key that is not tied to resource grants. Creating the key grants access to the public gateways you scope it to. See [Identity Keys vs Manual Keys](/manage/ai/virtual-api-keys#identity-keys-vs-manual-keys). ## Tunneling [#tunneling] Tunneling is a Pangolin strength, on both sides of the network. ### Sites: Self-Hosted Models on Remote Networks [#sites-self-hosted-models-on-remote-networks] A [site connector](/manage/sites/understanding-sites) sits on the network where [Ollama](/manage/ai/providers/custom/ollama), [vLLM](/manage/ai/providers/custom/vllm), or another model server already runs. [Custom providers](/manage/ai/providers/custom) use **Site Targets** so the gateway reaches that API over the tunnel. The model host does not need a public IP or an inbound port. Pangolin can also call cloud providers from the node, so one resource can mix hosted APIs and self-hosted models. You can put LiteLLM or Bifrost on that same site network and attach them as a Custom provider. Pangolin authenticates; the downstream gateway routes. ### Clients: Remote Access Without Provider Keys [#clients-remote-access-without-provider-keys] Users connect with the Pangolin client, then reach a private AI Gateway the same way they reach a private host or SSH resource. That is how someone on a laptop uses a self-hosted model on a remote network, or a cloud provider, without holding the upstream key. The client connection is the credential so you don't need to distribute virtual API keys to users. ## When to Use Each Solution [#when-to-use-each-solution] Use **LiteLLM** if you want a dedicated Python proxy or SDK in front of many providers, and you already have a network path and your own identity layer. Use **Bifrost** if you want a dedicated high-performance LLM gateway for routing and failover. You can run it on its own, or behind Pangolin as a [Custom provider](/manage/ai/providers/custom/bifrost). Use **Pangolin** if you want LLM access as an identity-aware resource: the same users and roles as the rest of Pangolin, site tunnels to self-hosted models, and client tunnels that authenticate and govern usage at the edge so provider keys stay off devices. Set up providers, resources, and identity-based access for coding agents and AI clients. --- # Cloud vs. Self-Hosted (/about/pangolin-cloud-vs-self-hosted) > Choose managed Pangolin Cloud or run the same software on your own infrastructure You can run Pangolin as [Pangolin Cloud](https://app.pangolin.net/auth/signup) or [self-host](/self-host/quick-install) the same open-source software. Sites, resources, and clients work the same way in both. The difference is who operates the server: the control plane, DNS, certificates, and the nodes that terminate tunnels. ## Pangolin Cloud [#pangolin-cloud] Cloud is the managed control plane. You create an account, install [sites](/manage/sites/install-site) and [clients](/manage/clients/platforms), and define resources. Pangolin runs the dashboard, database, certificates, and globally distributed nodes. Use Cloud when you want high availability, automatic updates, and less operational work. You can still keep traffic on infrastructure you control with [remote nodes](/manage/remote-node/understanding-nodes). ## Self-Hosted [#self-hosted] Self-hosted Pangolin runs on a server you operate. You supply a public IP, a domain, and the listener ports. See [Quick Install](/self-host/quick-install) and [DNS and Networking](/self-host/dns-and-networking). You are responsible for updates, backups, certificates, and uptime. Two editions: * **Community Edition** is the core platform. * **[Enterprise Edition](/self-host/enterprise-edition)** uses the `ee` image and a license key. It unlocks extra identity, logging, and management features. Compare tiers on the [Self-Hosted pricing page](https://pangolin.net/pricing#Self-Hosted-identity-and-access-management). Enterprise Edition is **free** for personal use and organizations with less than $100,000 USD gross annual revenue. You still need a license key. Larger organizations need a paid license. ## Remote Nodes (Hybrid) [#remote-nodes-hybrid] [Remote nodes](/manage/remote-node/understanding-nodes) are a middle path but are part of Pangolin Cloud. You run the node (tunnels, TLS, relay) on your server and bandwidth. Cloud still handles the dashboard, DNS, certificates, and failover. If your node goes down, traffic can fail over to other nodes or cloud points of presence. Public TCP/UDP listeners that bind a port on the Pangolin host are a self-hosted-server feature. On Cloud, deploy a remote node for that. See [TCP / UDP](/manage/resources/public/raw-resources). ## Quick Comparison [#quick-comparison] | | Pangolin Cloud | Self-hosted | | ------------------------ | ---------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | | **Control plane** | Managed | You run Pangolin, Traefik, and Gerbil | | **Traffic path** | Cloud nodes, or your [remote nodes](/manage/remote-node/understanding-nodes) | Your server | | **DNS and certificates** | Managed | You configure DNS and Let's Encrypt | | **Updates and backups** | Included | You operate them | | **High availability** | Included | You design it ([clustering](/self-host/clustering/understanding-clustering) on Enterprise) | | **Identity and logs** | Cloud plans | Community core; extra IdPs, streaming, and AI session logs on [Enterprise Edition](/self-host/enterprise-edition) | | **Data residency** | Cloud regions, or traffic on your remote nodes | Entirely on your infrastructure | | **Best for** | Fast start and less ops | Air-gapped networks, full control, compliance that requires self-hosting | Cloud and self-hosted plans are listed separately on [pricing](https://pangolin.net/pricing). Click "Self-Hosted" at the top to toggle between deployment options. ## When to Use Each [#when-to-use-each] Use **Pangolin Cloud** if you want to start quickly, get HA and certificate management included, and optionally attach remote nodes so tunnel traffic stays on your network. Use **self-hosted** if the control plane and data must stay on infrastructure you operate, you need an air-gapped or fully isolated deployment, or you already run Pangolin as Community or Enterprise Edition. Managed control plane. Install sites and clients, then define resources. Install Community Edition or Enterprise Edition with the quick installer. --- # Understanding Sites (/manage/sites/understanding-sites) > 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 [#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 [#site-types] Pangolin supports three different types of sites, each designed for different use cases and deployment scenarios. ### Pangolin Site (Recommended) [#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 [#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 [#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 --- # Install Sites (/manage/sites/install-site) > 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. 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). ## Binary Installation (Linux, macOS, Windows) [#binary-installation-linux-macos-windows] ### Install [#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 [#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 [#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 [#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 ``` Make sure the binary exists at `/usr/local/bin/pangolin` before daemon reload and starting the service: `~$ which pangolin` 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 [#docker-installation] ### Pull the Image [#pull-the-image] Pull the latest image from Docker Hub: ```bash docker pull fosrl/pangolin-cli:latest ``` ### Run with Docker [#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 [#docker-compose] #### Environment Variables (Recommended) [#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 [#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 [#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 [#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 [#platform-specific-installation] ### Unraid [#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. Pangolin Site in the Unraid Community Applications store from Fossorial's Repository Follow the installation prompts, then enter the site ID, secret, and endpoint from Pangolin in the template fields. Unraid Pangolin Site template showing PANGOLIN_ENDPOINT, SITE_ID, and SITE_SECRET ### Kubernetes [#kubernetes] The Kubernetes install still uses the legacy Newt for the Helm chart, namespace, and secret keys shown below. 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: Quick-start guide for installing a site with Helm. Install a site with rendered manifests and Kustomize overlays. Full configuration reference for Helm and Kustomize workflows. Debug site deployment and connection issues in Kubernetes. ### Newt [#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. --- # Configure Sites (/manage/sites/configure-site) > Configure Pangolin site connector setting ## Flags [#flags] 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. Site ID generated by Pangolin to identify the client. **Example**: `31frd0uzbjvp721` **Env var**: `SITE_ID` **Config file key**: `id` **Legacy env var**: `NEWT_SECRET` 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` Keep this secret private and secure. It's used for authentication. The endpoint where the Pangolin server resides for websocket connections. **Example**: `https://pangolin.example.com` **Env var**: `PANGOLIN_ENDPOINT` **Config file key**: `endpoint` 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` 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` 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` 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` DNS server to use for resolving the endpoint. **Default**: `9.9.9.9` **Env var**: `DNS` **Config file key**: `dns` 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` Interval for pinging the server. **Default**: `15s` **Env var**: `PING_INTERVAL` **Config file key**: `pingInterval` Timeout for each ping. **Default**: `7s` **Env var**: `PING_TIMEOUT` **Config file key**: `pingTimeout` 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` Set the Docker socket path for container discovery integration. **Example**: `/var/run/docker.sock` **Env var**: `DOCKER_SOCKET` **Config file key**: `dockerSocket` 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` 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` Script to be called when targets are added or removed. **Example**: `/path/to/updown.sh` **Env var**: `UPDOWN_SCRIPT` **Config file key**: `updownScript` 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` Don't fail over to the cloud when using managed nodes in Pangolin Cloud. **Default**: `false` **Env var**: `NO_CLOUD` **Config file key**: `noCloud` Disable clients on the WireGuard interface. **Default**: `false` (clients enabled) **Env var**: `DISABLE_CLIENTS` **Config file key**: `disableClients` Disable SSH auth daemon and native SSH mode (remote auth daemon integrations still work). **Default**: `false` **Env var**: `DISABLE_SSH` **Config file key**: `disableSsh` Enable Prometheus /metrics exporter. **Default**: `false` **Env var**: `SITE_METRICS_PROMETHEUS_ENABLED` **Config file key**: `metrics` **Legacy env var**: `NEWT_METRICS_PROMETHEUS_ENABLED` 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` 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` 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` 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` 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` Enforce certificate validation for health checks. **Default**: `false` (accepts any cert) **Env var**: `ENFORCE_HC_CERT` **Config file key**: `enforceHcCert` Path to client certificate file (PEM/DER format) for mTLS. **Example**: `/path/to/client.crt` **Env var**: `TLS_CLIENT_CERT` **Config file key**: `tlsClientCertFile` 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` 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) 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` Pre-shared key for auth daemon authentication. **Env var**: `AD_KEY` **Config file key**: `adPreSharedKey` Path to the principals file for auth daemon. **Default**: `/var/run/auth-daemon/principals` **Env var**: `AD_PRINCIPALS_FILE` **Config file key**: `adPrincipalsFile` Path to the CA certificate file for auth daemon. **Default**: `/etc/ssh/ca.pem` **Env var**: `AD_CA_CERT_PATH` **Config file key**: `adCaCertPath` Generate a random password for authenticated users. **Default**: `false` **Env var**: `AD_GENERATE_RANDOM_PASSWORD` **Config file key**: `adGenerateRandomPassword` Print the Newt (package) version and exit. 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` Prefer this endpoint for the connection (if set, will override the endpoint from the server). **Example**: `https://preferred.endpoint.com` **Config file key**: `preferEndpoint` Name of the WireGuard interface. **Default**: `pangolin` **Env var**: `INTERFACE` **Config file key**: `interface` 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` Use a native WireGuard interface for client tunnels instead of the userspace netstack implementation. Most users should not use this feature. **Default**: `false` **Env var**: `USE_NATIVE_INTERFACE` **Config file key**: `native` Use a native WireGuard interface for the main tunnel (instead of netstack). Most users should not use this feature. **Default**: `false` **Env var**: `USE_NATIVE_MAIN_INTERFACE` **Config file key**: `nativeMain` Name of the native main tunnel WireGuard interface (used with `--native-main`). Most users should not use this feature. **Default**: `pangolin` **Env var**: `INTERFACE_MAIN` **Config file key**: `interfaceMain` MTU for the internal WireGuard interface. Most users should not use this feature. **Default**: `1280` **Env var**: `MTU` **Config file key**: `mtu` ## Config File [#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 [#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\\.config\pangolin\site.json` | | Linux / others | `~/.config/pangolin/site.json` | ### Example [#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": [] } ``` 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. Fields like `secret`, `tlsClientKey`, and `adPreSharedKey` are sensitive. Restrict file permissions on the config file accordingly. ## Docker Socket Integration [#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 ``` If the Docker socket is not available or accessible, the site will gracefully disable Docker integration and continue normal operation. **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. 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. ## Updown Scripts [#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 ``` 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. You can look at `updown.py` as a reference script to get started! ## mTLS Authentication [#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) [#option-1-pkcs12-legacy] This is the original method and still supported. **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) [#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 ``` --- # Update Sites (/manage/sites/update-site) > 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). If you installed Newt instead of the Pangolin CLI, see [Install Newt](/manage/sites/install-newt) for Newt-specific commands. ## Docker Installation [#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 [#binary-installation] ### Auto Installer (Recommended) [#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 [#manual-installation] Download the latest binary for your system from [GitHub releases](https://github.com/fosrl/cli/releases) and replace your existing binary. --- # Automatic Site Updates (/manage/sites/auto-update) > Let sites check for and install updates on their own Only available in [Pangolin Cloud](https://app.pangolin.net/auth/signup) and [Enterprise Edition](/self-host/enterprise-edition). 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 [#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 [#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 [#enable-automatic-updates] Automatic updates are disabled by default. You must opt in at the organization or site level. 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 [#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 [#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. --- # Site Credentials (/manage/sites/credentials) > Understanding how site credentials work and how they can be rotated & regenerated ## Understanding Credentials [#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 [#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 [#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. This is a *secret*! Only share it with trusted people and be sure to store it safely and securely. 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 [#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 [#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 [#rotating-and-regenerating-credentials] 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-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. --- # Site Provisioning Keys (/manage/sites/site-provisioning) > Use long-lived provisioning tokens to bootstrap Pangolin sites at scale without pre-creating ID-secret pairs for every host ## Why provisioning keys exist [#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 [#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. Flow: provision with pre-shared key, exchange for ID and secret, apply YAML, pending approval, admin approves [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. ### `--provisioning-blueprint-file` vs `--blueprint-file` [#--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 [#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`) [#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 [#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. Provisioning keys are not API keys. They cannot authorize arbitrary Pangolin API calls; they exist only to bootstrap sites through the provisioning exchange. ## Creating a key and pending approval [#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. Create a provisioning key in the Pangolin dashboard 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. Pending sites listed for admin review --- # Helm (/manage/sites/kubernetes/helm) > 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 [#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 [#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 [#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 [#quick-install-with-existing-secret] This is the recommended simple production pattern. ### Step 1: Create the namespace [#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 ``` 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. ### Step 2: Create the Newt Secret [#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= \ --from-literal=NEWT_SECRET= ``` Get the Newt credentials from the Pangolin dashboard for the site you want this Newt instance to connect to. ### Step 3: Create a values file [#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 [#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 [#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 [#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: "" 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 ``` `emptyDir` is enough for testing, but it is ephemeral. For durable provisioning state, use `type: persistentVolumeClaim` with an existing PVC. Example with an existing PVC: ```yaml newtInstances: - name: main-tunnel enabled: true pangolinEndpoint: https://pangolin.example.com provisioningKey: "" 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 [#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 [#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 --namespace pangolin ``` ## Multiple Newt instances [#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= \ --from-literal=NEWT_SECRET= kubectl create secret generic newt-auth-site-b \ --namespace pangolin \ --from-literal=PANGOLIN_ENDPOINT=https://pangolin.example.com \ --from-literal=NEWT_ID= \ --from-literal=NEWT_SECRET= ``` ## Architecture notes [#architecture-notes] ### Instance-based deployment [#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 [#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-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 [#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 [#references] ## Next steps [#next-steps] Review all Newt chart options. Debug Newt deployment and connection issues. Install Newt with rendered manifests and Kustomize overlays. Install the Pangolin control plane. --- # Kustomize (/manage/sites/kubernetes/kustomize) > 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 [#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 [#supported-approach] The Newt chart does not provide native Kustomize bases. Render the Helm chart first, then use Kustomize on the rendered manifests. Do not manage the same Newt resources with both a live Helm release and Kustomize. Pick one ownership model per environment. 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 [#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 [#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 [#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= \ --from-literal=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= \ --from-literal=NEWT_SECRET= ``` Use existing Kubernetes Secrets for production. Do not commit Newt credentials into Helm values, rendered manifests, or Kustomize patches. ## Step 3: Create base values [#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 [#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 [#step-5-create-the-base-kustomization] ```yaml # base/kustomization.yaml apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - newt.yaml ``` 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. ## Step 6: Inspect the rendered resource names [#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. Do not assume the rendered Deployment name without checking the generated manifests. Helm naming can change with release name, chart name, `nameOverride`, or `fullnameOverride`. ## Step 7: Create site-specific overlays [#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 ``` Replace `newt-main-tunnel` with the actual Deployment name from your rendered manifests. 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 ``` 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. ## Common Kustomize patches for Newt [#common-kustomize-patches-for-newt] ### Patch resource requests and limits [#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 [#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 ``` Only use index-based JSON patches after checking the rendered manifest. Container order and environment variable layout can change between chart versions. ### Add node affinity [#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 [#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 [#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 [#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 [#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 [#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 [#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 [#troubleshooting] ### The patch does not apply [#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 [#the-pod-does-not-start] Check pod status and events: ```bash kubectl get pods --namespace pangolin kubectl describe pod --namespace pangolin kubectl get events --namespace pangolin --sort-by=.lastTimestamp ``` ### Newt does not connect [#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 [#next-steps] Install Newt with Helm. Review Newt chart options. Debug Newt deployment and connection issues. Deploy Kustomize overlays with Argo CD or Flux. --- # Configuration (/manage/sites/kubernetes/configuration) > 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: ## Version context [#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 [#configuration-sections] 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. 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: "" ``` Creating the namespace manually is recommended when your cluster uses Pod Security Admission, policy labels, admission webhooks, or namespace annotations. 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= \ --from-literal=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: "" secret: "" ``` Inline credentials can appear in rendered manifests and Helm release history. Use `auth.existingSecretName` for production. 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. 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. 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: "" 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: "" 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: "" newtName: "my-site" configPersistence: enabled: true type: emptyDir provisioningBlueprintFile: /etc/newt/provisioning-blueprint.yaml provisioningBlueprintData: | version: 1 routes: [] ``` 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 | Newt 1.11 changed upstream ping defaults. Set `pingInterval` and `pingTimeout` explicitly if you need older timing behavior. 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 | 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`. `emptyDir` is recreated when a pod is replaced. Newt can require a reconnect and handshake after restart, which may briefly interrupt active traffic. For production, prefer an existing PersistentVolumeClaim to keep writable Newt configuration across restarts and rescheduling. 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 ``` Use Secrets for certificates and sensitive script inputs. Avoid inline private keys or credentials in values files. 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 ``` 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. 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. 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. 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. 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 ``` 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. ## Configuration by install method [#configuration-by-install-method] ### Helm [#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 [#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 [#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 [#next-steps] Install Newt with Helm. Install Newt with rendered manifests and Kustomize overlays. Debug Newt deployment and connection issues. Deploy Newt with Argo CD or Flux. --- # Troubleshooting (/manage/sites/kubernetes/troubleshooting) > 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 [#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" ``` Do not assume the pod or Deployment name. Chart-generated names can change with the Helm release name, instance name, `nameOverride`, or `fullnameOverride`. ## Get the generated resource names [#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 [#pod-fails-to-start] ### Symptoms [#symptoms] ```text STATUS RESTARTS CrashLoopBackOff 5 Error 3 CreateContainerConfigError ImagePullBackOff ``` ### Check pod details [#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 [#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 -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 [#secret-issues] ### Verify the Secret exists [#verify-the-secret-exists] ```bash kubectl get secret newt-auth --namespace "$NEWT_NAMESPACE" ``` ### Check Secret keys [#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 ``` Do not paste decoded secrets into issue reports, logs, screenshots, or public repositories. ### Check which Secret the pod uses [#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 [#newt-cannot-reach-pangolin] ### Test DNS from the Newt pod [#test-dns-from-the-newt-pod] ```bash kubectl exec "$NEWT_POD" --namespace "$NEWT_NAMESPACE" -- \ nslookup pangolin.example.com ``` ### Test HTTPS from the Newt pod [#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 [#common-causes-1] | 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 [#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-issues] Provisioning requires writable config persistence. ### Symptoms [#symptoms-1] * 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 [#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: "" 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: "" newtName: "my-site" configPersistence: enabled: true type: persistentVolumeClaim existingClaim: my-newt-config mountPath: /var/lib/newt fileName: config.json ``` `emptyDir` is recreated when the pod is recreated. Use a PVC if the generated configuration must survive pod replacement. ## Service not created or not reachable [#service-not-created-or-not-reachable] ### Important behavior [#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 [#check-services] ```bash kubectl get svc --namespace "$NEWT_NAMESPACE" \ -l app.kubernetes.io/name=newt ``` Describe the Service: ```bash kubectl describe svc --namespace "$NEWT_NAMESPACE" ``` ### LoadBalancer stuck in pending [#loadbalancer-stuck-in-pending] ```text EXTERNAL-IP ``` 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-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 [#metrics-service] Enable the metrics Service: ```yaml global: metrics: enabled: true service: enabled: true port: 2112 ``` ### ServiceMonitor [#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 ``` The chart has separate metrics values for container port, admin address, and metrics Service port. Check the rendered manifest when changing these values. ## NetworkPolicy blocks traffic [#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 [#multiple-newt-instances-conflict] ### Symptoms [#symptoms-2] * 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 [#check-values-1] ```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= \ --from-literal=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= \ --from-literal=NEWT_SECRET= ``` ## RBAC or service account issues [#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 [#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 [#mtu-issues] ### Symptoms [#symptoms-3] * 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 [#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" --namespace "$NEWT_NAMESPACE" ``` ## Kustomize debugging [#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-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 [#next-steps] Review Newt chart options. Install Newt with Helm. Install Newt with rendered manifests and Kustomize overlays. Deploy Newt with Argo CD or Flux. --- # Understanding Resources (/manage/resources/understanding-resources) > Resources are any network address you want to make available to users Resources represent the applications, hosts, or ranges you make available for remote access to users. Resources exist on the remote networks of your sites. Users only ever think about connecting to resources and not specific sites. They find and open what they can access from the [Resource Launcher](/manage/resource-launcher). By default, no resources are made available on sites. Admins must define resources with backend targets, and assign specific access policies before any users can gain access. ## Resource Types [#resource-types] There are two categories of resources: **public resources** and **private resources**. Each category supports different protocol types suited to how users connect. * Protocol-aware reverse proxies on the public internet * Browser-based access for most types (no client required) * Authentication and access rules on protocol-aware types * Zero-trust VPN access over the Pangolin client * Every resource requires authentication * Not browser-rendered; requires a connected client ### Public Resource Types [#public-resource-types] Public resources create a public proxy on the Pangolin server. The protocol changes per type, but the overall model is the same: traffic enters through Pangolin and is forwarded to your backend on a remote site. HTTP/HTTPS, SSH, RDP, and VNC are all **browser-based**. You assign a fully qualified domain name (FQDN) to each resource and users open it in a web browser—no client-side software is required. Pangolin authentication and access rules protect all of these types the same way. You can configure those rules inline on each resource or share them through a [resource policy](/manage/resources/public/resource-policies). [AI Gateway](/manage/resources/public/ai-gateway) also gets a public FQDN, but coding agents call it as an API. Visiting the URL in a browser is how users retrieve a virtual API key, not how they run the workload. SSH, RDP, and VNC require a **Pangolin Site**. HTTP/HTTPS, AI Gateway, and TCP/UDP resources can also run on local and basic WireGuard sites. TCP and UDP are the exception. They do not receive a FQDN. Instead, they bind to a port on the Pangolin server host and act as simple protocol-agnostic pipes to the downstream resource. Because they are not protocol-aware, they do not enforce Pangolin authentication or access rules. Websites, APIs, and dashboards behind an authenticated reverse proxy. Public FQDN for coding agents. Authenticate with virtual API keys. Full terminal in the browser with password, key, or Pangolin identity (PAM). Full remote desktop in the browser, including file transfer and clipboard. Remote display session rendered entirely in the browser. Raw TCP proxy on a Pangolin server port. No authentication. Raw UDP proxy on a Pangolin server port. No authentication. #### Site Compatibility [#site-compatibility] All public resource types supported. Required for SSH, RDP, and VNC. HTTP/HTTPS, AI Gateway, and TCP/UDP only. SSH, RDP, and VNC are not supported. HTTP/HTTPS, AI Gateway, and TCP/UDP only. SSH, RDP, and VNC are not supported. ### Private Resource Types [#private-resource-types] Private resources require users to connect with the Pangolin client before any traffic can flow. Nothing is exposed on the public internet. Users gain access to all resources their account is permitted to use once connected. Route traffic to a single IP address or FQDN on the remote network. Route traffic to an entire IP range, such as a subnet. Private reverse proxy with optional TLS termination at the site edge. AI API over the client tunnel. Identity from the connected client. Traditional terminal SSH over the tunnel via `pangolin ssh`. Route all client internet traffic out through the site. Private resources can only be created on Pangolin Sites. **Private resources function like a zero-trust virtual private network (VPN).** Explicit access to resources must be granted for users and roles to be able to access them. For raw TCP/UDP traffic that does not need a public proxy, prefer a private host or CIDR resource over public TCP/UDP resources. Private resources support [aliases](/manage/resources/private/alias) for human-readable internal hostnames. When multiple sites can reach the same destination, Pangolin [intelligently routes](/manage/resources/private/multi-site-routing) traffic through the healthiest path. #### Site Compatibility [#site-compatibility-1] Supported. Private resources require a Pangolin Site. Not supported. Local sites can only host public resources. Not supported. Basic WireGuard sites can only host public resources. --- # Resource Launcher (/manage/resource-launcher) > Find and open the resources you can access from one organization home page The Resource Launcher is the organization home page. It lists every [public](/manage/resources/understanding-resources#public-resource-types) and [private](/manage/resources/understanding-resources#private-resource-types) resource the signed-in user can access through an explicit grant on their user or [role](/manage/access-control/create-user#roles). Non-administrators land here when they sign in. Administrators can use it the same way, then jump into settings when they need to change a resource. Resource Launcher in the Pangolin dashboard ## Find Resources [#find-resources] Switch between **Grid** and **List** layouts. Search updates as you type across names, domains, sites, labels, destinations, aliases, and related fields. Filter by one or more [sites](/manage/sites/understanding-sites) and [labels](/manage/labels). Sort by name, and group by **Site**, **Label**, or **None** for a flat list. Site groups show an online or offline indicator. You can expand or collapse groups. The launcher remembers that state for the current organization and view. In very large organizations, the launcher may ask you to search or filter before browsing, and it may simplify grouping until the list is smaller. ## Open a Resource [#open-a-resource] Selecting a resource always expands a details panel. The panel shows what matters for that type: access URL or alias, health and auth for public resources, destination and certificate status for private resources, and port restrictions when they apply. Resource Launcher detail panel showing a private SSH resource as an example. From a card, list row, or the panel you can open HTTP-style resources in a new tab (HTTP/HTTPS, SSH, RDP, VNC, AI Gateway) or copy the access string. Private resources remind you to connect with a [Pangolin client](/manage/clients/platforms). For [AI Gateway](/manage/ai/overview) resources, the panel also lists available models, [virtual API keys](/manage/ai/virtual-api-keys), and copy-paste setup for coding agents. Public gateways use your API key. Private gateways use the connected client as the credential. The launcher URL stores the current search, filters, grouping, layout, and sort, so you can bookmark or share a view. Opening a resource from admin settings can deep-link into the launcher with that resource's panel already open. ## Saved Views [#saved-views] A saved view captures search, filters, grouping, layout, and sort. Start from **Default**, then save the current setup as a personal view. Switch views with the tabs at the top. The last view you used in that organization is restored when you come back. * **Save for Me** stores your personal default. It overrides any organization-wide default for you only. * **Save as New View** creates a named personal view. * **Save to Current View** updates a personal view you own. * **Reset View** discards unsaved changes. Organization-wide views are read-only unless you are an administrator. ## For Administrators [#for-administrators] Use **View as Admin** in the header to open organization settings, or from a resource panel to open that resource's settings. From a resource settings page you can jump back to the launcher with that resource already open. Administrators can share views with the organization: * **Save for Everyone** on the default view sets the organization-wide default for members who have not saved a personal default. * **Save for Everyone** on a named view shares it with every member. * **Make Personal** turns an organization-wide view back into a personal view. Set a useful organization-wide default (for example grouped by site, list layout) so people do not have to configure the launcher themselves. Users only see resources granted to their user or a role they belong to. Refresh reloads the latest resources so recent access or config changes show up. --- # HTTP / HTTPS (/manage/resources/public/http-https) > Publish websites, APIs, and dashboards as authenticated public reverse proxies HTTP and HTTPS public resources are the most common public resource type. They expose a web application or API on a fully qualified domain name with a valid TLS certificate, fronted by Pangolin's authenticated reverse proxy. Users open the resource URL in any web browser. No Pangolin client is required. ## Benefits [#benefits] * Publish apps from behind NAT or a firewall without opening inbound ports or needing a public IP on the backend. * Every resource gets a valid TLS certificate that Pangolin issues and renews. * Unauthenticated visitors are stopped at Pangolin before any traffic reaches your app. * SSO, identity providers, and context rules like geo-blocking protect apps that have no login of their own. * Multiple targets across sites give load balancing, health checks, and automatic failover. * Users only need a browser, so contractors, customers, and personal devices can get access without installing anything. ## Use Cases [#use-cases] * Put self-hosted apps such as Grafana, Home Assistant, or an internal wiki behind SSO. * Publish a public website or API running on a home server or private network. * Share a staging site with a client using a PIN, passcode, or expiring link. * Restrict an admin panel to certain countries, IP ranges, or URL paths with access rules. * Load balance a web app across servers in different locations. ## How It Works [#how-it-works] 1. You assign a FQDN on a domain managed in Pangolin. 2. Pangolin terminates TLS and applies [authentication and access rules](/manage/resources/public/authentication). 3. Authenticated requests are proxied through a site connector to your backend target. Pangolin acts as a front-door barrier: unauthenticated visitors are redirected to a Pangolin login page before traffic reaches your application. ## Target Configuration [#target-configuration] HTTP/HTTPS resources use **[targets](/manage/resources/public/targets)** to define where traffic is sent on your remote network. * Add one or more targets, each with an upstream address and port. * Assign each target to a site. Targets on different sites enable [load balancing](/manage/resources/public/targets#load-balancing-strategy) and [automatic failover](/manage/resources/public/healthchecks-failover). * Optionally configure path-based routing, path rewriting, a load balancing strategy, sticky sessions, custom host headers, and other proxy settings. This multi-target model differs from SSH, RDP, and VNC public resources, which use site selection and a single host/port instead of discrete targets. ## Authentication and Access Rules [#authentication-and-access-rules] HTTP/HTTPS resources are protocol-aware and fully support Pangolin's identity and context policies: * Platform SSO and external identity providers * User, role, and machine access assignments * PIN, passcode, shareable links, and email OTP * Ranked allow/deny rules for IP, geolocation, URL paths, and more See [Authentication](/manage/resources/public/authentication) for the full list of options. To share the same settings across multiple resources, use a [resource policy](/manage/resources/public/resource-policies). --- # AI Gateway (/manage/resources/public/ai-gateway) > Publish an AI API on a public FQDN and authenticate coding agents with virtual API keys An AI Gateway public resource is a protocol-aware reverse proxy on a fully qualified domain name, like [HTTP / HTTPS](/manage/resources/public/http-https). Clients call that URL instead of OpenAI, Anthropic, Gemini, or another model API. Pangolin authenticates the caller, then forwards the request to an attached [provider](/manage/ai/providers/overview). This page covers how the **resource** works: reachability, authentication, and what you attach. Providers, keys, model routing, the catalog, and client setup live in [AI Gateway](/manage/ai/overview). ## Benefits [#benefits] * Coding agents work from anywhere, with no Pangolin client on the device. * Provider API keys stay in Pangolin. Users get virtual API keys that you can revoke without rotating the provider key. * Identity keys follow user and role grants, so removing someone's access disables their key. * One URL can front several providers, with model routing, budgets, and usage analytics in one place. ## Use Cases [#use-cases] * Give a team Claude Code, Codex, or Gemini CLI on company provider accounts. * Issue manual keys to CI pipelines, scripts, or internal services that call a model API. * Track AI usage per user and key, and cap spend by role, key, or model with [budgets](/manage/ai/budgets). * Limit which models each team can use with [model routing](/manage/ai/providers/model-routing). ## How It Works [#how-it-works] 1. You assign a FQDN on a domain managed in Pangolin and set the resource type to **AI Gateway**. 2. You attach one or more org-level providers. The resource speaks the API formats those providers advertise. 3. A user retrieves a [virtual API key](/manage/ai/virtual-api-keys) by visiting the URL in a browser and logging in, or from the Resource Launcher or `https://app.pangolin.net//keys`. 4. Coding agents send that key to the same FQDN. Pangolin checks the key and proxies to the selected provider. Visiting the URL in a browser is how you retrieve a key. Model calls still need the key in the request. A dashboard session cookie cannot proxy through the gateway. ## Providers, Not Targets [#providers-not-targets] AI Gateway public resources do **not** use HTTP [targets](/manage/resources/public/targets). Traffic goes to providers configured under **AI Gateway → Providers**, then attached on the resource. Cloud APIs (OpenAI, Anthropic, and similar) need no site. [Custom](/manage/ai/providers/custom) providers can use **Site Targets** when the model server sits on a site network. That routing is on the provider, not on the resource. ## Authentication and Access Rules [#authentication-and-access-rules] Authentication is always on. You cannot turn Platform SSO off the way you can on an HTTPS resource. Assign [users and roles](/manage/access-control/create-user) the same way as a public HTTPS resource. Those grants control who can use an **identity key**. [Manual keys](/manage/ai/virtual-api-keys#manual-keys) grant access as soon as you create them, regardless of users and roles on the resource. When the call uses an identity key, or a manual key attributed to a user, Pangolin forwards that identity upstream as [`Remote-*` headers](/manage/ai/providers/configuration#identity-headers). An unattributed manual key authenticates without sending them. HTTPS resources can add PIN, passcode, header auth, shareable links, or email OTP. AI clients authenticate programmatically, so this type uses virtual API keys instead of those methods. See [Virtual API Keys](/manage/ai/virtual-api-keys) and [public authentication](/manage/resources/public/authentication). You can still attach a [resource policy](/manage/resources/public/resource-policies) for users, roles, and access rules. ## More Than One Resource [#more-than-one-resource] Give different users and roles their own providers with more than one AI Gateway resource. Distinct hostnames are the usual approach. Unlike HTTP / HTTPS, they can also share a FQDN because they all route to the gateway inside Pangolin. See [Multiple Gateway Resources](/manage/ai/multiple-gateway-resources). ## Compared to Private AI Gateway [#compared-to-private-ai-gateway] | | Public AI Gateway | [Private AI Gateway](/manage/resources/private/ai-gateway) | | ----------------- | -------------------------------- | ---------------------------------------------------------- | | **Reachability** | Public FQDN | Pangolin client tunnel | | **Auth** | Virtual API key on every call | Client identity; the gateway does not check a key | | **Browser visit** | Shows the user's key after login | Not used to retrieve a key | For providers, keys, model routing, and connecting Claude Code, Codex, and other clients, see [AI Gateway](/manage/ai/overview). --- # SSH (/manage/resources/public/ssh) > Access a remote shell in the browser with password, key, or Pangolin identity authentication SSH public resources render a full interactive terminal in the browser. Users visit a FQDN—no SSH client or Pangolin desktop client is required. Interactive SSH terminal rendered in the Pangolin web dashboard ## Benefits [#benefits] * Users get a terminal from any browser, with no SSH client, keys, or Pangolin client on their device. * The host's SSH port is never exposed to the internet. Pangolin authentication happens before a session can start. * SSO, identity providers, and access rules like geo-blocking apply to shell access. * With automated provisioning, users are created on the host from their Pangolin identity, so there are no shared accounts or keys to manage. ## Use Cases [#use-cases] * Get emergency access to a server from a borrowed or locked-down computer. * Give contractors shell access to a single server without setting up a VPN or SSH keys for them. * Manage home lab or edge devices from a phone or tablet. * Let support staff reach customer appliances behind NAT through a site. ## Video Walkthrough [#video-walkthrough]