# Amp Source: https://docs.controlplane.com/ai/amp Connect Amp, Sourcegraph's agentic coding tool, to Control Plane to operate your infrastructure from inside your editor or the Amp CLI. ## Prerequisites Install [Amp](https://ampcode.com) for your editor or CLI Sign up at [console.cpln.io](https://console.cpln.io) if you don't have an account yet ## Add the MCP server ```bash theme={null} amp mcp add cpln https://mcp.cpln.io/mcp ``` Connecting to the bare URL uses the default **`core`** toolset profile — a focused set that already covers the whole build-and-operate workflow (it can read and delete any resource kind generically, and reach anything else through the raw-API tool), which is the right fit for this client. Append `?toolsets=mk8s` to add BYOK Kubernetes tools, or `?toolsets=full` for everything. See [toolset profiles](/ai/mcp#toolset-profiles). Launch the Amp CLI. Amp opens your browser automatically: 1. Sign in to Control Plane (Google, GitHub, Microsoft, or SAML). 2. Select the orgs you want Amp to access. 3. Click **Authorize**. Your browser shows a success page — close the tab and return to Amp. In Amp, ask "list GVCs in org ``" (using one of the orgs you authorized) to confirm. ## Example conversation ``` Use org "my-org" and gvc "my-gvc" for context. ``` ``` Create a publicly accessible workload called "my-api" using nginx:latest on port 80. ``` ``` Monitor the readiness of my-api and give me the public endpoint once it's ready. ``` ## Next steps Practical prompts for common Control Plane workflows 30+ production-ready templates your AI can deploy on request # Antigravity Source: https://docs.controlplane.com/ai/antigravity Connect Google Antigravity (CLI or IDE) to Control Plane to manage your infrastructure with AI-powered development workflows. ## Prerequisites Download [Antigravity](https://antigravity.google) Sign up at [console.cpln.io](https://console.cpln.io) if you don't have an account yet For the latest MCP configuration options in Antigravity, see the official [Google Antigravity documentation](https://antigravity.google/docs). ## Add the MCP server Open `mcp_config.json` in your editor: | OS | Path | | ------------- | ---------------------------------------------- | | macOS / Linux | `~/.gemini/config/mcp_config.json` | | Windows | `%USERPROFILE%\.gemini\config\mcp_config.json` | Create the `.gemini/config` directory and `mcp_config.json` file if they don't exist. ```json theme={null} { "mcpServers": { "cpln": { "serverUrl": "https://mcp.cpln.io/mcp" } } } ``` Connecting to the bare URL uses the default **`core`** toolset profile — a focused set that already covers the whole build-and-operate workflow (it can read and delete any resource kind generically, and reach anything else through the raw-API tool), which is the right fit for this client. Append `?toolsets=mk8s` to add BYOK Kubernetes tools, or `?toolsets=full` for everything. See [toolset profiles](/ai/mcp#toolset-profiles). Save the file. Trigger the OAuth flow: * **Antigravity CLI** — run `/mcp`, select `cpln`, and choose **Authenticate**. * **Antigravity IDE** — the agent reports an **MCP Error** for the new server; click it, then **View MCP Settings → Authenticate** (or **Settings → Customizations → Authenticate** next to the `cpln` MCP). Your browser opens: 1. Sign in to Control Plane (Google, GitHub, Microsoft, or SAML). 2. Select the orgs you want Antigravity to access. 3. Click **Authorize**. The browser redirects to an Antigravity page with a code — copy it. Back in Antigravity, paste the code into the prompt. The `cpln` server is now connected. In the agent, ask "list GVCs in org ``" (using one of the orgs you authorized) to confirm. ## Example conversation ``` Use org "my-org" and gvc "my-gvc" for context. ``` ``` Create a publicly accessible workload called "my-api" using nginx:latest on port 80. ``` ``` Monitor the readiness of my-api and give me the public endpoint once it's ready. ``` ## Troubleshooting Open **Settings → Customizations**, click **Sign Out** next to the `cpln` MCP, then click **Authenticate** to start the OAuth flow again. Sign in again and adjust your org selections on the consent screen. ## Next steps Practical prompts for common Control Plane workflows 30+ production-ready templates your AI can deploy on request # Claude Code Source: https://docs.controlplane.com/ai/claude Connect Claude Code to Control Plane to deploy and operate your infrastructure through natural-language conversations. We recommend the [Control Plane AI Plugin](/ai/plugin) for Claude Code. The plugin bundles the MCP server. ## Prerequisites Download [Claude Code](https://claude.com/product/claude-code) Sign up at [console.cpln.io](https://console.cpln.io) if you don't have an account yet ## Add the MCP server Use the `claude mcp add` command: ```bash theme={null} claude mcp add --transport http cpln "https://mcp.cpln.io/mcp?toolsets=full" --scope user ``` The **user** scope makes the server available across all your projects. Use `--scope project` to add it for the current directory only. `?toolsets=full` serves the complete tool catalog — what the plugin configures for every client. Claude Code loads tool schemas on demand, so the full catalog costs nothing up front (see [toolset profiles](/ai/mcp#toolset-profiles)). See the [Claude Code MCP installation scopes](https://code.claude.com/docs/en/mcp#mcp-installation-scopes) for the full set of scope options. Launch Claude Code. It detects the new server and reports that authentication is required. Run `/mcp`, navigate to **cpln**, and choose **Authenticate** — your browser opens: 1. Sign in to Control Plane (Google, GitHub, Microsoft, or SAML). 2. Select the orgs you want this Claude Code install to access. 3. Click **Authorize**. Your browser shows a success page — close the tab and return to Claude Code. In Claude Code, ask: "list GVCs in org ``" (using one of the orgs you authorized). You should see the GVCs in that org. ## Example conversation ``` Use org "my-org" and gvc "my-gvc" for context. ``` ``` Create a publicly accessible workload called "my-api" using nginx:latest on port 80. ``` ``` Monitor the readiness of my-api and give me the public endpoint once it's ready. ``` ## Troubleshooting Some terminal setups can't launch a browser. Claude Code prints a fallback URL — copy it into a browser on the same machine and complete sign-in there. In Claude Code, run `/mcp`, select **cpln**, and choose **Re-authenticate**. Sign in again and adjust your org selections on the consent screen. ## Next steps Practical prompts for common Control Plane workflows Layer skills, agents, slash commands, and guardrails on top of the MCP server # Claude Desktop Source: https://docs.controlplane.com/ai/claude-desktop Connect the Claude desktop app on macOS or Windows to Control Plane via a Custom Connector. Connectors sync across Claude. Setting this up in the desktop app also enables it in [Claude Web](/ai/claude-web), and vice versa — you only need to do it once. ## Prerequisites Download [Claude for Mac or Windows](https://claude.ai/download) Sign up at [console.cpln.io](https://console.cpln.io) if you don't have an account yet ## Add the MCP server Open [claude://claude.ai/customize/connectors](claude://claude.ai/customize/connectors) to jump directly to the Connectors page in the desktop app. If the link doesn't open the app, navigate manually: **Settings** → **Connectors** → **Customize**. Click the **+** button, then **Add custom connector**, and fill in: | Field | Value | | ------------------------- | ------------------------- | | **Name** | `Control Plane` | | **Remote MCP server URL** | `https://mcp.cpln.io/mcp` | Connecting to the bare URL uses the default **`core`** toolset profile — a focused set that already covers the whole build-and-operate workflow (it can read and delete any resource kind generically, and reach anything else through the raw-API tool), which is the right fit for this client. Append `?toolsets=mk8s` to add BYOK Kubernetes tools, or `?toolsets=full` for everything. See [toolset profiles](/ai/mcp#toolset-profiles). Click **Add**. On the new Control Plane connector card, click **Connect**. Claude opens your default browser for sign-in. 1. Sign in to Control Plane (Google, GitHub, Microsoft, or SAML). 2. Select the orgs you want Claude to access. 3. Click **Authorize**. Return to the Claude app — the connector shows as **Connected**. Start a new chat. The Control Plane tools are available — ask Claude to "list GVCs in org ``" (using one of the orgs you authorized) to confirm. For the latest connector setup details, see Anthropic's [Custom Connectors documentation](https://support.anthropic.com/en/articles/11176164-pre-built-connectors-using-remote-mcp). ## Example conversation ``` Use org "my-org" and gvc "my-gvc" for context. ``` ``` Create a publicly accessible workload called "my-api" using nginx:latest on port 80. ``` ``` Monitor the readiness of my-api and give me the public endpoint once it's ready. ``` ## Troubleshooting Make sure a default browser is set in your OS. Re-trigger sign-in by removing the connector and adding it again. Open [claude://claude.ai/customize/connectors](claude://claude.ai/customize/connectors) (or **Settings → Connectors → Customize**), disconnect the **Control Plane** connector, then click **Connect** again. Sign in and adjust your org selections on the consent screen. ## Next steps Practical prompts for common Control Plane workflows 30+ production-ready templates your AI can deploy on request # Claude Web Source: https://docs.controlplane.com/ai/claude-web Connect claude.ai in your browser to Control Plane via a Custom Connector. Connectors sync across Claude. Setting this up on claude.ai also enables it in the [Claude desktop app](/ai/claude-desktop), and vice versa — you only need to do it once. ## Prerequisites Sign in at [claude.ai](https://claude.ai) Sign up at [console.cpln.io](https://console.cpln.io) if you don't have an account yet ## Add the MCP server Go to [claude.ai/customize/connectors](https://claude.ai/customize/connectors). Click the **+** button, then **Add custom connector**, and fill in: | Field | Value | | ------------------------- | ------------------------- | | **Name** | `Control Plane` | | **Remote MCP server URL** | `https://mcp.cpln.io/mcp` | Connecting to the bare URL uses the default **`core`** toolset profile — a focused set that already covers the whole build-and-operate workflow (it can read and delete any resource kind generically, and reach anything else through the raw-API tool), which is the right fit for this client. Append `?toolsets=mk8s` to add BYOK Kubernetes tools, or `?toolsets=full` for everything. See [toolset profiles](/ai/mcp#toolset-profiles). Click **Add**. On the new Control Plane connector card, click **Connect**. Claude opens a new tab for sign-in. 1. Sign in to Control Plane (Google, GitHub, Microsoft, or SAML). 2. Select the orgs you want claude.ai to access. 3. Click **Authorize**. The tab closes automatically and the connector shows as **Connected**. Open a new chat. The Control Plane tools are now available — try asking Claude to "list GVCs in org ``" (using one of the orgs you authorized) to confirm. For the latest connector setup details, see Anthropic's [Custom Connectors documentation](https://support.anthropic.com/en/articles/11176164-pre-built-connectors-using-remote-mcp). ## Example conversation ``` Use org "my-org" and gvc "my-gvc" for context. ``` ``` Create a publicly accessible workload called "my-api" using nginx:latest on port 80. ``` ``` Monitor the readiness of my-api and give me the public endpoint once it's ready. ``` ## Troubleshooting Go to [claude.ai/customize/connectors](https://claude.ai/customize/connectors), disconnect the **Control Plane** connector, then click **Connect** again. Sign in and adjust your org selections on the consent screen. ## Next steps Practical prompts for common Control Plane workflows 30+ production-ready templates your AI can deploy on request # OpenAI Codex Source: https://docs.controlplane.com/ai/codex Connect OpenAI Codex to Control Plane to manage your infrastructure through AI-assisted development. We recommend the [Control Plane AI Plugin](/ai/plugin) for Codex. The plugin bundles the MCP server. ## Prerequisites Install [Codex CLI](https://openai.com/codex) Sign up at [console.cpln.io](https://console.cpln.io) if you don't have an account yet For the latest MCP configuration options, refer to the official [OpenAI Codex documentation](https://developers.openai.com/codex). ## Add the MCP server Use the `codex mcp add` command: ```bash theme={null} codex mcp add cpln --url https://mcp.cpln.io/mcp ``` Connecting to the bare URL uses the default **`core`** toolset profile — a focused set that already covers the whole build-and-operate workflow (it can read and delete any resource kind generically, and reach anything else through the raw-API tool), which is the right fit for this client. Append `?toolsets=mk8s` to add BYOK Kubernetes tools, or `?toolsets=full` for everything. See [toolset profiles](/ai/mcp#toolset-profiles). Run the login command: ```bash theme={null} codex mcp login cpln ``` Your browser opens: 1. Sign in to Control Plane (Google, GitHub, Microsoft, or SAML). 2. Select the orgs you want this Codex install to access. 3. Click **Authorize**. Your browser shows a success page — close the tab and return to Codex. ```bash theme={null} codex mcp list ``` You should see `cpln` with **enabled** status and **OAuth** auth: ```text theme={null} Name Url Bearer Token Env Var Status Auth cpln https://mcp.cpln.io/mcp - enabled OAuth ``` Launch Codex and ask: "list GVCs in org ``" (using one of the orgs you authorized). You should see the GVCs in that org. ## Example conversation ``` Use org "my-org" and gvc "my-gvc" for context. ``` ``` Create a publicly accessible workload called "my-api" using nginx:latest on port 80. ``` ``` Monitor the readiness of my-api and give me the public endpoint once it's ready. ``` ## Troubleshooting Some terminal setups can't launch a browser. `codex mcp login cpln` prints a fallback URL — copy it into a browser on the same machine and complete sign-in there. Re-run `codex mcp login cpln`, sign in again, and adjust your org selections on the consent screen. ## Next steps Practical prompts for common Control Plane workflows Layer skills, agents, slash commands, and guardrails on top of the MCP server # Cursor IDE Source: https://docs.controlplane.com/ai/cursor Connect the Cursor desktop app to Control Plane to manage your infrastructure directly from your editor. ## Prerequisites Download [Cursor](https://cursor.com) Sign up at [console.cpln.io](https://console.cpln.io) if you don't have an account yet ## Add the MCP server In Cursor, go to **Settings → Tools & MCPs**, then click **New MCP Server** under **Home MCP Servers**. Cursor opens your global `mcp.json`: | OS | Path | | ------------- | -------------------------------- | | macOS / Linux | `~/.cursor/mcp.json` | | Windows | `%USERPROFILE%\.cursor\mcp.json` | ```json theme={null} { "mcpServers": { "cpln": { "type": "http", "url": "https://mcp.cpln.io/mcp" } } } ``` Connecting to the bare URL uses the default **`core`** toolset profile — a focused set that already covers the whole build-and-operate workflow (it can read and delete any resource kind generically, and reach anything else through the raw-API tool), which is the right fit for this client. Append `?toolsets=mk8s` to add BYOK Kubernetes tools, or `?toolsets=full` for everything. See [toolset profiles](/ai/mcp#toolset-profiles). Save and close the file. Back in Cursor's MCP settings, the `cpln` server now shows a **Connect** button. Click it. 1. Sign in to Control Plane (Google, GitHub, Microsoft, or SAML). 2. Select the orgs you want Cursor to access. 3. Click **Authorize**. Your browser shows a success page — close the tab and return to Cursor. Open Cursor's AI chat and ask "list GVCs in org ``" (using one of the orgs you authorized) to confirm. For the latest UI changes, see Cursor's [MCP documentation](https://cursor.com/docs/context/mcp). ## Example conversation ``` Use org "my-org" and gvc "my-gvc" for context. ``` ``` Create a publicly accessible workload called "my-api" using nginx:latest on port 80. ``` ``` Monitor the readiness of my-api and give me the public endpoint once it's ready. ``` ## Troubleshooting In Cursor's MCP settings, expand the `cpln` server, click **Logout**, then **Connect** again. Sign in again and adjust your org selections on the consent screen. Verify the JSON syntax in `mcp.json` and reload Cursor. ## Next steps Practical prompts for common Control Plane workflows 30+ production-ready templates your AI can deploy on request # Cursor Web Source: https://docs.controlplane.com/ai/cursor-web Connect Cursor on the web to Control Plane to manage your infrastructure from your browser. ## Prerequisites Open [Cursor on the web](https://cursor.com/agents) Sign up at [console.cpln.io](https://console.cpln.io) if you don't have an account yet ## Add the MCP server In Cursor on the web, click the **MCPs** dropdown. Click **+**, select **Custom MCP**, and fill in: | Field | Value | | -------------- | ------------------------- | | **Name** | `cpln` | | **Server URL** | `https://mcp.cpln.io/mcp` | Connecting to the bare URL uses the default **`core`** toolset profile — a focused set that already covers the whole build-and-operate workflow (it can read and delete any resource kind generically, and reach anything else through the raw-API tool), which is the right fit for this client. Append `?toolsets=mk8s` to add BYOK Kubernetes tools, or `?toolsets=full` for everything. See [toolset profiles](/ai/mcp#toolset-profiles). Click **Add MCP**. On the new `cpln` item in the MCPs dropdown, click **Login**. 1. Sign in to Control Plane (Google, GitHub, Microsoft, or SAML). 2. Select the orgs you want Cursor to access. 3. Click **Authorize**. You're returned to the same Cursor page. Ask "list GVCs in org ``" (using one of the orgs you authorized) to confirm. For the latest UI changes, see Cursor's [MCP documentation](https://cursor.com/docs/context/mcp). ## Example conversation ``` Use org "my-org" and gvc "my-gvc" for context. ``` ``` Create a publicly accessible workload called "my-api" using nginx:latest on port 80. ``` ``` Monitor the readiness of my-api and give me the public endpoint once it's ready. ``` ## Troubleshooting In the MCPs dropdown, click **Edit** on the `cpln` MCP, click **Logout** in the popup, then **Login** again. Sign in again and adjust your org selections on the consent screen. ## Next steps Practical prompts for common Control Plane workflows 30+ production-ready templates your AI can deploy on request # Usage Examples Source: https://docs.controlplane.com/ai/examples Practical examples of using the Control Plane MCP Server with AI assistants This page provides practical examples of how to interact with Control Plane through AI assistants using the MCP Server. These examples work across all [compatible tools](/ai/mcp#compatible-tools). ## Setting Context Before performing operations, always set your organization and GVC context: ```text Natural Language theme={null} Use org "my-org" and gvc "my-gvc" for the following operations. ``` ```text Alternative theme={null} Set my Control Plane context to organization "my-org" and GVC "my-gvc". ``` Setting context once at the start of a conversation helps the AI assistant understand which resources to target. *** ## GVC Operations ### Create a GVC Create a new [Global Virtual Cloud](/concepts/gvc) with specific locations. You can use friendly location names or technical location IDs: ```text Basic GVC (Friendly Names) theme={null} Create a new GVC called "production" with locations in Frankfurt, Virginia, and Dublin. ``` ```text Basic GVC (Technical IDs) theme={null} Create a new GVC called "production" with locations in aws-us-east-1 and aws-eu-west-1. ``` ```text Multi-Cloud GVC theme={null} Create a GVC named "multi-cloud-app" with locations: - azure-centralus - gcp-me-west1 - aws-eu-central-1 ``` ```text With Description theme={null} Create a GVC called "staging" with these settings: - Description: "Staging environment for QA testing" - Locations: Frankfurt, Dublin - Tags: environment=staging, team=platform ``` You can use friendly location names (like "Frankfurt", "Virginia", "Dublin") or technical location IDs (like "aws-eu-central-1", "aws-us-east-1"). The MCP server will resolve friendly names to the appropriate location IDs. ### List GVCs ```text theme={null} Show me all GVCs in the org "my-org". ``` ### Get GVC Details ```text theme={null} Get the details of GVC "production" including its locations and configuration. ``` *** ## Workload Operations ### Create a Workload Create a new [workload](/concepts/workload) with various configurations: ```text Basic Workload theme={null} Create a publicly accessible workload called "api-server" with: - Image: nginx:latest - Port: 80 - Memory: 256Mi - CPU: 250m ``` ```text Web Application theme={null} Deploy a publicly accessible workload named "web-frontend" with these specifications: - Container image: httpd:latest - Exposed port: 80 - Memory: 512Mi - CPU: 500m - Min replicas: 2 - Max replicas: 5 - Health check path: / ``` ```text Background Worker theme={null} Create a cron workload called "data-processor" that: - Uses image: python:3.11-slim - Runs every hour (0 * * * *) - Has 1Gi memory - CPU: 500m - Does not need external access ``` ### Update a Workload ```text Scale Up theme={null} Scale the workload "api-server" to a minimum of 3 replicas and maximum of 5 replicas. ``` ```text Update Image theme={null} Update the "web-frontend" workload to use image httpd:2.4-alpine. ``` ```text Add Environment Variables theme={null} Add these environment variables to workload "api-server": - DATABASE_URL: postgres://db.example.com:5432/mydb - CACHE_ENABLED: true - LOG_LEVEL: info ``` ### List Workloads ```text theme={null} List all workloads in GVC "production" with their current replica counts and status. ``` ### Get Workload Status ```text theme={null} Show me the detailed status of workload "api-server" including: - Current replicas - Deployment status per location - Recent events or errors ``` ### Delete a Workload ```text theme={null} Delete the workload "old-service" from GVC "staging". ``` Deletion operations are irreversible. The AI assistant should be configured to confirm before executing destructive operations. *** ## Image Operations ### Build an Image The assistant can build a container image straight from a **GitHub or GitLab repository** and push it to your org's private registry. No Docker daemon and no CLI are involved — the build runs on Control Plane, and the assistant follows it until the image is pushed. ```text theme={null} Build the image "my-app:v1.0.0" in org "my-org" from https://github.com/my-org/my-app on the main branch, then deploy it. ``` The first build from a **private** repository returns a one-time authorization link instead of starting: open it, approve access for your organization, then ask the assistant to build again. Later builds run without prompting. To build a **local folder**, the assistant will hand you a `cpln image build --remote` command to run yourself — it has no access to your filesystem. That still needs no Docker daemon, only the [CLI](/cli-reference/installation) with a [profile](/cli-reference/get-started/profiles) authenticated to the target organization. See [build options](/cli-reference/get-started/images#build-options). ```text theme={null} I want to build the app in ./api and deploy it to org "my-org". ``` ### Check a Build ```text theme={null} What is the status of build "b-1a2b3c" in org "my-org"? Show me the log if it failed. ``` ### List Images ```text theme={null} Show me all images in org "my-org" with their tags and creation dates. ``` ### Get Image Details ```text theme={null} Get the details of image "my-app" including all available tags and their digests. ``` *** ## Secret Management ### Wire an Existing Secret Secret data is managed by you — in the Console, CLI, Terraform, Pulumi, or the API. The assistant verifies a secret exists and wires access to it; it never reads or writes secret values. ```text Check a Secret Exists theme={null} Do we have a secret called "db-credentials"? List the secrets in my org and show me its type and tags. ``` ```text Grant Workload Access to a Secret theme={null} Give the workload "api-server" access to the secret "db-credentials" as environment variables. Set up the identity, policy, and binding automatically. ``` When granting workload access to secrets, describe the desired outcome — the MCP server will automatically create the identity, policy, and workload binding in one step. *** ## Policy & Access Control ### Manage Policies ```text Create a Read-Only Policy theme={null} Create a policy called "viewer-workloads" that grants "view" permission on all workloads to the group "//group/developers". ``` ```text Grant Secret Access theme={null} Create a policy called "api-secrets" that grants "reveal" and "use" permissions on the secret "//secret/db-credentials" to the identity "//gvc/production/identity/api-identity". ``` *** ## Domain Configuration ```text theme={null} Create a domain "api.example.com" with: - DNS mode: cname - Certificate challenge: http01 - Port 443 (http2) routing to workload "//gvc/production/workload/api-server" ``` *** ## Cloud Accounts ```text Setup AWS Cloud Account theme={null} Get the setup guide for creating an AWS cloud account with: - Provider: aws - Cloud account name: my-aws-prod - AWS account ID: 123456789012 - Role ARN: arn:aws:iam::123456789012:role/cpln-my-org ``` ```text Setup GCP Cloud Account theme={null} Get the setup guide for creating a GCP cloud account with: - Provider: gcp - Project ID: my-gcp-project-123 ``` *** ## Suggestion Tools ### Plan Before Executing ```text CLI Suggestion theme={null} I want to deploy a workload with a custom domain. What CLI commands would I need? Use cpln_suggest to plan the workflow. ``` ```text API Suggestion theme={null} I need to update a workload's environment variables via the REST API. Use api_suggest to show me the endpoint, method, and body format. ``` *** ## Workload Logs ### Query Logs ```text Recent Logs theme={null} Show me the last hour of logs for workload "api-server" in GVC "production". ``` ```text Filter for Errors theme={null} Get logs for workload "api-server" that contain "error" or "panic" in the last 2 hours. ``` ```text Specific Container theme={null} Show logs for the "_accesslog" container of workload "web-frontend" in the last 30 minutes. ``` ```text Time Range theme={null} Get logs for workload "data-processor" between 2024-01-15T10:00:00Z and 2024-01-15T11:00:00Z. ``` The `get_workload_logs` tool builds LogQL queries automatically from structured parameters. Use the `query` parameter for advanced LogQL syntax. *** ## Complex Workflows ### Full Application Deployment ```text theme={null} Deploy a complete application stack to Control Plane: 1. Create a GVC called "my-app-prod" in Frankfurt and Virginia 2. Create a publicly accessible workload "frontend" with: - Image: httpd:latest - Port: 80 - Memory: 512Mi - CPU: 500m - 2-5 replicas 3. Create a publicly accessible workload "backend" with: - Image: nginx:latest - Port: 8080 - Memory: 1Gi - CPU: 500m - 2-5 replicas - Environment variable SERVICE_NAME=backend ``` ### Environment Promotion ```text theme={null} Promote the configuration from GVC "staging" to "production": 1. Get the current workload configurations from staging 2. Update the production workloads to match staging 3. Show me the differences before and after ``` ### Debugging Session ```text theme={null} Help me debug the workload "api-server" in production: 1. Show me the current status and replica count 2. Check if there are any error events 3. Get the recent logs and filter for errors 4. Show me the resource utilization ``` *** ## Next Steps Configure MCP for your preferred tool Learn more about workload configuration options Understand Global Virtual Clouds Configure permissions for your authorized orgs # MCP Server Source: https://docs.controlplane.com/ai/mcp Connect any MCP-compatible AI tool to Control Plane. The Control Plane MCP Server lets any AI tool with [Model Context Protocol](https://modelcontextprotocol.io/) support manage Control Plane resources through natural-language conversations. If your AI tool supports plugins — **Claude Code, Codex, or Antigravity CLI** — start with the [Control Plane AI Plugin](/ai/plugin) instead. The plugin auto-configures this MCP server and adds skills, agents, commands, and guardrails on top. ## Endpoint `https://mcp.cpln.io/mcp` ## Authentication OAuth with per-org consent. Your AI tool walks you through it on first use — see your tool's setup page below for the exact trigger. ## Toolset profiles The full tool catalog is larger than most AI tools can hold in context at once — and some clients cap how many tools they accept. So each connection selects a **toolset profile** with the `toolsets` query parameter. Pick **one** — `core`, `mk8s`, and `full` are nested (each is a superset of the one above), and `readonly` stands on its own: | Profile | What it includes | | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `readonly` | Investigation only. Metrics, logs, traces, workload events, replicas and deployments, quotas, the audit trail, configuration inventory, and Terraform export — plus a generic read tool that works on **every** resource kind. It advertises no tool that creates, updates, deletes or executes a command, so an assistant on this profile cannot change a live resource. | | `core` *(default)* | The everyday deploy-and-operate journey: GVCs, workloads, deployments, logs, image builds, custom domains, identities, policies, the template catalog, volume sets, Terraform export, metrics, traces, and the audit trail — plus a generic read/delete tool that works on **every** resource kind, and a raw-API escape hatch for anything else. | | `mk8s` | Everything in `core`, **plus** the typed BYOK managed-Kubernetes (mk8s) cluster tools (create/update across all 11 providers). | | `full` | Everything in `mk8s`, **plus** the full administrative and fine-tuning surface: cloud-account registration (AWS/GCP/Azure/NATS), org IAM (users, groups, service accounts, policy edits), network-bridge agents, identity network-resource bindings, IP sets, audit contexts, advanced workload tuning (load balancer, sidecar, rollout, security, retry, external logging), deep custom-domain editing (routes, ports, CORS, TLS), volume-set snapshots / expand / shrink, and org/GVC settings. | ```text Examples theme={null} https://mcp.cpln.io/mcp # core (default) https://mcp.cpln.io/mcp?toolsets=readonly # reads only, no writes https://mcp.cpln.io/mcp?toolsets=mk8s # core + BYOK Kubernetes https://mcp.cpln.io/mcp?toolsets=full # everything ``` An unknown value returns a `400` listing the accepted names (`readonly`, `core`, `mk8s`, `full`). So does combining `readonly` with another profile — it withholds the write tools the others add, so the two cannot be mixed. **Which profile should I use?** **`full`** is the complete catalog — it's what the [AI Plugin](/ai/plugin) configures for every client. Pick **`core`** (the default) for a lighter, focused catalog if your client caps tool counts or loads every tool definition up front; it still covers the whole build-and-operate workflow (it can read and delete *any* resource kind generically, and reaches anything else through the raw-API tool). Use **`mk8s`** when you manage BYOK Kubernetes clusters. Use **`readonly`** to point an assistant at production for performance work, incident triage, or an audit without the possibility of an unintended change. The profile is fixed for the life of a connection — an AI agent can't expand it mid-session. If your assistant says a Control Plane tool isn't available, reconnect with the profile that includes it. ## Compatible tools Claude Code CLI Claude apps for Mac & Windows claude.ai in the browser OpenAI Codex Cursor IDE Visual Studio Code Google Antigravity (CLI or IDE) Sourcegraph Amp OpenCode terminal assistant Don't see your tool listed? Any client that supports remote MCP servers can connect — point it at `https://mcp.cpln.io/mcp` and let it handle OAuth. ## Next steps Every tool the server exposes, grouped by resource, with toolset profiles Practical prompts for common Control Plane workflows Recommended path for Claude Code, Codex, Antigravity CLI Scope what an AI tool can do in your org with policies and least privilege # OpenCode Source: https://docs.controlplane.com/ai/opencode Connect OpenCode, the terminal-native AI coding assistant, to Control Plane to operate your infrastructure without leaving your shell. ## Prerequisites Install [OpenCode](https://opencode.ai) Sign up at [console.cpln.io](https://console.cpln.io) if you don't have an account yet ## Add the MCP server ```bash theme={null} opencode mcp add ``` Answer the prompts: | Prompt | Value | | ---------------------------------------------- | ------------------------- | | Location | **Global** | | MCP server name | `cpln` | | MCP server type | **Remote** | | MCP server URL | `https://mcp.cpln.io/mcp` | | Does this server require OAuth authentication? | **Yes** | | Do you have a pre-registered client ID? | **No** | Connecting to the bare URL uses the default **`core`** toolset profile — a focused set that already covers the whole build-and-operate workflow (it can read and delete any resource kind generically, and reach anything else through the raw-API tool), which is the right fit for this client. Append `?toolsets=mk8s` to add BYOK Kubernetes tools, or `?toolsets=full` for everything. See [toolset profiles](/ai/mcp#toolset-profiles). OpenCode writes the entry to `~/.config/opencode/opencode.jsonc`. ```bash theme={null} opencode mcp auth cpln ``` Your browser opens: 1. Sign in to Control Plane (Google, GitHub, Microsoft, or SAML). 2. Select the orgs you want OpenCode to access. 3. Click **Authorize**. Your browser shows a success page — close the tab and return to the terminal. OpenCode prints **Authentication successful!**. Launch OpenCode and ask "list GVCs in org ``" (using one of the orgs you authorized) to confirm. ## Example conversation ``` Use org "my-org" and gvc "my-gvc" for context. ``` ``` Create a publicly accessible workload called "my-api" using nginx:latest on port 80. ``` ``` Monitor the readiness of my-api and give me the public endpoint once it's ready. ``` ## Troubleshooting Re-run `opencode mcp auth cpln`. Sign in again and adjust your org selections on the consent screen. ## Next steps Practical prompts for common Control Plane workflows 30+ production-ready templates your AI can deploy on request # Teach your AI coding agent to use Control Plane Source: https://docs.controlplane.com/ai/overview Two paths — the AI Plugin for Claude Code, Codex, and Antigravity CLI, or the MCP Server for any other MCP-compatible client. For **Claude Code, Codex, and Antigravity CLI**. Includes the MCP server plus skills, agents, slash commands, and guardrails. For **any other MCP-compatible AI** — Cursor, Claude Web, Claude Desktop, VS Code, Antigravity IDE, Amp, OpenCode. ## What you can do Create and configure GVCs, workloads, identities, and more Build images and deploy applications across global locations Get real-time information about your Control Plane resources Streamline DevOps tasks through conversational AI ## Which path for which tool? **AI Plugin** · [MCP only](/ai/claude) **AI Plugin** · [MCP only](/ai/codex) **AI Plugin** · [MCP only](/ai/antigravity) MCP Server MCP Server MCP Server MCP Server MCP Server MCP Server MCP Server Any remote-MCP client ## Sign in Each tool triggers OAuth its own way — see your tool's setup page for the specifics. Once your browser lands on Control Plane: Use Google, GitHub, Microsoft, or SAML. Pick the orgs this AI client may access and click **Authorize**. ## Best practices Per-client consent lets you scope each AI tool to the smallest org set that makes sense for its job Always set your org and GVC context at the start of a conversation Provide specific values for resources (memory, CPU, replicas) when possible Add tags to resources for better organization and filtering ### Recommended prompt structure For the best results, structure your prompts with: 1. **Context** — Org and GVC 2. **Action** — What you want to do 3. **Target** — Which resource 4. **Details** — Specific configuration values ```text Good prompt theme={null} Using org "my-org" and GVC "production", create a workload called "api-v2" with image nginx:latest, 512Mi memory, port 8080, and minimum 2 replicas. ``` ```text Less effective theme={null} Make a new workload. ``` ## References Install guide and the full inventory of skills, agents, commands, and guardrails Endpoint and the compatible-tools list Every tool the MCP server exposes, grouped by resource Practical prompts for common Control Plane workflows Source, release notes, and contribution guide # AI Plugin Source: https://docs.controlplane.com/ai/plugin Install the Control Plane AI Plugin in Claude Code, Codex, or Antigravity CLI. ## Install Add the marketplace, install the plugin, then reload: ```text theme={null} /plugin marketplace add https://github.com/controlplane-com/ai-plugin.git /plugin install cpln@controlplane /reload-plugins ``` To enable auto-update, run `/plugin`, use the `<` / `>` arrow keys to navigate to the **Marketplaces** tab, select **controlplane**, and choose **Enable auto-update**. Otherwise update manually with `/plugin marketplace update controlplane` followed by `/reload-plugins`. Alternatively, install from [Anthropic's community plugin marketplace](https://github.com/anthropics/claude-plugins-community), where every listing has passed Anthropic's review: ```text theme={null} /plugin marketplace add anthropics/claude-plugins-community /plugin install cpln@claude-community /reload-plugins ``` Add the plugin marketplace: ```bash theme={null} codex plugin marketplace add https://github.com/controlplane-com/ai-plugin.git ``` Start Codex, open `/plugins`, navigate to **Control Plane**, then install the `cpln` plugin. **Enable plugin hooks for guardrail injection (recommended).** Codex ships with `plugin_hooks` off by default. Enable it in `~/.codex/config.toml` so the plugin's `SessionStart` hook can inject CLI conventions and guardrails: ```toml theme={null} [features] plugins = true plugin_hooks = true ``` After restart, `/plugins` → **Control Plane** → **Hooks** should show `SessionStart`. Codex does not auto-update plugin marketplaces. When a new release is published, run the upgrade command and restart Codex: ```bash theme={null} codex plugin marketplace upgrade controlplane ``` Install the plugin straight from GitHub: ```bash theme={null} agy plugin install https://github.com/controlplane-com/ai-plugin/plugins/cpln ``` Confirm it loaded with `agy plugin list`. ## Sign in The plugin bundles the MCP server and configures every client with the `full` [toolset profile](/ai/mcp#toolset-profiles). Each tool triggers OAuth its own way — see your tool's setup page for the specifics. Once your browser lands on Control Plane: Use Google, GitHub, Microsoft, or SAML. Pick the orgs this AI client may access and click **Authorize**. ## Try it ```text Troubleshoot a workload theme={null} /cpln:troubleshoot my-api --gvc production ``` ```text Set up secret access theme={null} My "my-api" workload needs to read the "db-password" secret. ``` ```text Migrate a Kubernetes manifest theme={null} /cpln:migrate-k8s ./k8s/deployment.yaml ``` ```text Natural language theme={null} Deploy this app on Frankfurt and give me the public URL. ``` ## Next steps Practical prompts for common Control Plane workflows Source, full inventory of skills, agents, commands, and guardrails # Tools Reference Source: https://docs.controlplane.com/ai/tools Every tool the Control Plane MCP Server exposes, grouped by resource, with read-only and destructive markers and the toolset profile each belongs to. This page lists every tool the [MCP Server](/ai/mcp) exposes, grouped by the resource it operates on. Tool names are the exact identifiers the AI calls — you normally describe what you want in natural language and let the assistant pick the tool, but knowing what exists helps you prompt precisely and understand what an assistant is doing on your behalf. You don't call these tools directly. Describe the outcome you want — see [Usage Examples](/ai/examples) for prompt patterns — and the assistant selects and chains the right tools. ## Toolset profiles The server advertises a different set of tools depending on the `toolsets` query parameter on the endpoint URL. A smaller set keeps the assistant focused and fits comfortably in its context window; the larger sets unlock specialized resource families. | Profile | Tools | Endpoint | Includes | | :--------- | :---: | :------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `readonly` | 30 | `https://mcp.cpln.io/mcp?toolsets=readonly` | Investigation only — observability, runtime state, inventory, and generic reads. Nothing that creates, updates, deletes, or executes, so an assistant on this profile cannot change a live resource. | | `core` | 56 | `https://mcp.cpln.io/mcp` | The default. Everyday workflows — workloads, GVCs, image builds, domains, identities, policies, templates, volume sets, observability (logs, metrics, traces, audit), quotas, and Terraform export. | | `mk8s` | 78 | `https://mcp.cpln.io/mcp?toolsets=mk8s` | Everything in `core` plus the [managed Kubernetes](/mk8s/overview) family for provisioning and updating clusters across cloud providers. | | `full` | 132 | `https://mcp.cpln.io/mcp?toolsets=full` | Every tool, including agents, cloud accounts, IP sets, service accounts, audit contexts, external logging, quotas, advanced workload configuration, and volume snapshots. | Connecting to `https://mcp.cpln.io/mcp` with no query parameter gives you the `core` profile. To switch profiles, point your client at the URL with the `?toolsets=` parameter — see your tool's [setup page](/ai/mcp#compatible-tools) for where the endpoint URL is configured. ### Reading the tables Each tool is listed with its name, its label, and what it does. The **Access** column flags how the tool behaves: | Marker | Meaning | | :-------------- | :---------------------------------------------------------------------------------------------------------------------- | | **Read** | Read-only. Fetches data without changing anything. | | **Write** | Creates or modifies a resource. | | **Destructive** | Deletes, removes, or overwrites data. Assistants present the impact and ask for your confirmation before running these. | Tools available only in a larger profile are marked **`mk8s`** or **`full`** next to the access marker; everything else is in `core`. *** ## Docs & guidance Read-only tools the assistant uses to ground itself in accurate Control Plane facts, schemas, and documentation before acting. All are in `core`. | Name | Label | Description | Access | | :------------------------------------ | :---------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------ | :------------ | | `get_cpln_rules` | Get Control Plane Operating Guide | The operating guide — the resource model, production defaults, and how to verify a change landed. | Read | | `get_cpln_skill` | Get a Control Plane Skill | The runbook for one task family: how to use a feature correctly, the constraints that are easy to miss, and when it's the wrong tool. | Read | | `get_resource_schema` | Get Resource Schema & API Endpoints | The exact object schema and REST endpoints for a resource kind, for authoring an accurate manifest or calling the API. | Read | | `get_permissions` | Get Permissions for a Resource Kind | The grantable permissions for a resource kind, for building policies. | Read | | `search_control_plane` | Search Control Plane Docs | Search the documentation knowledge base for guides, examples, and API references. | Read | | `query_docs_filesystem_control_plane` | Query Control Plane Docs Filesystem | Run read-only, shell-like queries against an in-memory filesystem of the docs pages and OpenAPI specs. | Read · `full` | *** ## Generic resource tools The single read and delete tools that work for every resource kind. Use these to list, inspect, or delete resources — pass the `kind` (`workload`, `secret`, `gvc`, and so on); secrets are read-only. All are in `core`. | Name | Label | Description | Access | | :---------------- | :----------------------- | :------------------------------------------------------------------------------------------------------------------------- | :---------- | | `list_resources` | List Resources of a Kind | List resources of one kind as a summary table. | Read | | `get_resource` | Get a Resource | Fetch one resource by kind and name, with its full JSON. Secrets return metadata only — the API never includes their data. | Read | | `delete_resource` | Delete a Resource | Delete one resource by kind and name. The single delete tool for every deletable kind — secrets are not deletable. | Destructive | *** ## GVCs Create and manage [GVCs](/reference/gvc) and the cloud locations they span. | Name | Label | Description | Access | | :--------------------- | :-------------------------- | :----------------------------------------------------------------------- | :------------------- | | `create_gvc` | Create a Control Plane GVC | Create a GVC with one or more cloud locations. | Write | | `update_gvc` | Update a Control Plane GVC | Update a GVC's settings, such as its description, tags, or pull secrets. | Destructive | | `add_gvc_locations` | Add locations to a GVC | Add one or more cloud locations to an existing GVC. | Write · `full` | | `remove_gvc_locations` | Remove locations from a GVC | Remove cloud locations from a GVC. | Destructive · `full` | *** ## Workloads Create, update, run, and operate [workloads](/reference/workload/general). The advanced `configure_workload_*` tools each manage one specialized block of the spec and live in the `full` profile. | Name | Label | Description | Access | | :--------------------------------- | :------------------------------------------- | :------------------------------------------------------------------------------------------------------------------- | :------------------- | | `create_workload` | Create a Workload | Create a serverless, standard, cron, stateful, or VM workload with its containers and exposure. | Write | | `update_workload` | Update a Workload | Update an existing workload — image, resources, scaling, environment, and more. | Destructive | | `grant_workload_secret_access` | Grant Workload Secret Access | Grant an existing workload access to a secret by wiring its identity and access policy. Never returns secret values. | Write | | `workload_exec` | Workload Exec — Run a Command in a Container | Run a single command in a running container and return its output. Audited. | Destructive | | `workload_start_cron` | Workload — Start a Cron Run | Trigger an immediate run of a cron workload. | Write | | `workload_stop_replica` | Workload — Stop a Replica | Terminate one running replica of a workload; the platform reschedules a replacement per the scaling settings. | Write | | `configure_workload_load_balancer` | Configure Workload Load Balancer | Set or clear the direct per-location load balancer, geo headers, and replica-direct routing. | Destructive · `full` | | `configure_workload_security` | Configure Workload Security Options | Set or clear the pod-level security context (filesystem group, run-as user). | Destructive · `full` | | `configure_workload_rollout` | Configure Workload Rollout Options | Set or clear how updates roll out — surge, unavailability, and scaling policy. | Destructive · `full` | | `configure_workload_retry` | Configure Workload Request Retry Policy | Set or clear the retry behavior for failed requests. | Destructive · `full` | | `configure_workload_sidecar` | Configure Workload Envoy Sidecar | Set or clear the Envoy proxy filter chain. Advanced. | Destructive · `full` | | `configure_workload_local_options` | Configure Workload Per-Location Options | Set or clear per-location overrides of the default options. | Destructive · `full` | | `configure_workload_extras` | Configure Workload BYOK Extras | Set or clear BYOK-only Kubernetes customizations (affinity, tolerations, spread constraints). | Destructive · `full` | *** ## Images Build container [images](/reference/image) on Control Plane and follow the build. An image record is created by pushing, so there is no create or update tool — list, inspect, and delete image records with the [generic tools](#generic-resource-tools) using `kind: image`. | Name | Label | Description | Access | | :---------------- | :----------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----- | | `build_image` | Build an Image from a Git Repository | Build an image from a GitHub or GitLab repository and push it to the org's private registry. Returns a build id; the build keeps running after the call returns. | Write | | `get_image_build` | Get an Image Build Status and Log | Read a build's status, progress events, and log by its build id. | Read | No Docker daemon is involved: the build service clones the repository, detects how to build it (using the Dockerfile when one is present), and always produces a `linux/amd64` image. Building an existing `NAME:TAG` replaces that image, so prefer a fresh tag. Only **repository** builds run through the MCP server — it has no access to your filesystem. To build a local folder, run [`cpln image build --remote`](/cli-reference/get-started/images#build-options), which uploads the folder and builds it the same way, also without Docker. A **private** repository needs a one-time browser authorization per organization. The first build that needs it returns an authorization link instead of starting a build; open the link, approve access, then ask the assistant to build again. The link is single-use and expires shortly. Later builds in that organization run without prompting. *** ## Secrets [Secrets](/reference/secret) are read-only through the MCP server: `list_resources` and `get_resource` show that a secret exists and its metadata — never its data. No tool creates, edits, deletes, or reveals a secret; secret data and lifecycle are managed by you in the [Console](https://console.cpln.io), the [CLI](/cli-reference/overview), [Terraform](/iac/terraform), [Pulumi](/iac/pulumi), or the [API](/api-reference/api). To let a workload consume an existing secret, use `grant_workload_secret_access` under [Workloads](#workloads). *** ## Custom domains Configure [custom domains](/reference/domain), their port listeners, routes, TLS, and CORS. Port, route, TLS, and CORS tools operate on an existing domain. | Name | Label | Description | Access | | :-------------------- | :------------------------------------ | :---------------------------------------------------------------------------------------- | :------------------- | | `create_domain` | Create a Domain | Create a custom domain in `cname` or `ns` DNS mode. | Write | | `update_domain` | Update a Domain | Update a domain's metadata, host/subdomain acceptance flags, and GVC or workload binding. | Destructive | | `add_domain_port` | Add a Domain Port Listener | Add a port listener (for example, 443/http2) to a domain. | Write | | `remove_domain_port` | Remove a Domain Port Listener | Remove a port listener from a domain. | Destructive | | `add_domain_route` | Add a Route to a Domain Listener | Add a route mapping a path or prefix to a workload. | Write | | `update_domain_route` | Update a Route on a Domain Listener | Update an existing route on a domain listener. | Destructive | | `remove_domain_route` | Remove a Route from a Domain Listener | Remove a route from a domain listener. | Destructive | | `set_domain_tls` | Set TLS on a Domain Listener | Set the TLS configuration on a domain listener. | Destructive | | `clear_domain_tls` | Clear TLS on a Domain Listener | Clear the TLS configuration from a domain listener. | Destructive | | `set_domain_cors` | Set CORS on a Domain Listener | Set the CORS policy on a domain listener. | Destructive · `full` | | `clear_domain_cors` | Clear CORS on a Domain Listener | Clear the CORS policy from a domain listener. | Destructive · `full` | *** ## Identity & access Manage [identities](/reference/identity), [policies](/reference/policy), [groups](/reference/group), [service accounts](/reference/serviceaccount), and the private-network resources attached to an identity. Groups, service accounts, network resources, and user invitations are in `full`. | Name | Label | Description | Access | | :------------------------------------- | :------------------------------------ | :-------------------------------------------------------------------------------------------------- | :------------------- | | `create_identity` | Create an Identity | Create a GVC-scoped identity for workload access to secrets, cloud resources, and private networks. | Write | | `update_identity` | Update an Identity | Update an identity's settings and bound credentials. | Destructive | | `create_policy` | Create a Policy | Create a policy that grants permissions on resources to principals. | Write | | `update_policy` | Update a Policy | Update a policy's bindings, targets, or permissions. | Destructive | | `create_group` | Create a Group | Create a group of users and service accounts, with optional tag-based membership. | Write · `full` | | `edit_group` | Edit a Group | Update a group's membership or membership query. | Destructive · `full` | | `create_service_account` | Create a Service Account | Create a service account for programmatic access. | Write · `full` | | `update_service_account` | Update a Service Account | Update a service account's description or tags, or revoke its keys by name. | Destructive · `full` | | `add_key_to_service_account` | Add Key to Service Account | Create a service account if needed, add a key, and optionally add it to a group. | Write · `full` | | `invite_user_to_org` | Invite User to Organization | Invite a user by email to join the organization. | Write · `full` | | `add_identity_network_resource` | Add Agent-Based Network Resource | Add an agent-based (wormhole) network resource so a workload can reach a private endpoint. | Write · `full` | | `add_identity_native_network_resource` | Add Cloud-Native Network Resource | Add an AWS PrivateLink or GCP Private Service Connect network resource to an identity. | Write · `full` | | `list_identity_network_resources` | List Network Resources on an Identity | List the agent-based and cloud-native network resources attached to an identity. | Read · `full` | | `remove_identity_network_resource` | Remove Network Resource from Identity | Remove a network resource from an identity. | Destructive · `full` | *** ## Template catalog Browse and operate the [Template Catalog](/template-catalog/overview) — production-ready stacks you install instead of hand-authoring resources. | Name | Label | Description | Access | | :------------------------- | :----------------------------------- | :------------------------------------------------------------------------------------------------ | :------------------- | | `browse_templates` | Browse Template Catalog | List the catalog — name, category, latest version, and whether each template creates its own GVC. | Read | | `get_template` | Get Template Detail & Example Values | Show a template's versions, prerequisites, and example `values.yaml`. | Read | | `preview_template` | Preview Template (Dry-Run) | Render the resources an install or upgrade would create, without applying anything. | Read · `full` | | `install_template` | Install Template | Install a catalog template as a new release. | Write | | `upgrade_template` | Upgrade Template | Upgrade an installed release to a new version or updated values. | Destructive | | `rollback_template` | Rollback a Template Release | Roll a release back to a prior revision. | Destructive · `full` | | `uninstall_template` | Uninstall Template | Uninstall a release and remove the resources it created. | Destructive | | `list_installed_templates` | List Installed Templates | List the template releases installed in an org. | Read | | `get_installed_template` | Get Installed Template Resources | Show a release's status, revision, and the resources it created. | Read | *** ## Volume sets & storage Create [volume sets](/reference/volumeset), mount them to workloads, and manage their volumes and snapshots. Snapshot and per-volume operations are in `full`. | Name | Label | Description | Access | | :---------------------------- | :-------------------------- | :---------------------------------------------------------------------------------------------------------------------------------- | :------------------- | | `create_volumeset` | Create a Volumeset | Create a volume set for persistent storage. | Write | | `update_volumeset` | Update a Volumeset | Update a volume set's settings. | Destructive | | `mount_volumeset_to_workload` | Mount Volumeset to Workload | Mount a volume set to a workload at a path (ext4/xfs require a stateful or VM workload; shared mounts on any type). | Write | | `expand_volumeset` | Expand Volumeset Volume | Increase a volume's capacity with no downtime or data loss. | Write | | `shrink_volumeset` | Shrink Volumeset Volume | Shrink a volume to a smaller capacity. Data is migrated, but if used bytes exceed the new size the excess is lost — snapshot first. | Destructive · `full` | | `create_volumeset_snapshot` | Create Volumeset Snapshot | Create a point-in-time snapshot of a volume. | Write · `full` | | `list_volumeset_snapshots` | List Volumeset Snapshots | List the snapshots of a volume set. | Read · `full` | | `restore_volumeset_snapshot` | Restore Volumeset Snapshot | Restore a snapshot, discarding everything written since it was taken. | Destructive · `full` | | `delete_volumeset_snapshot` | Delete Volumeset Snapshot | Delete a volume set snapshot. | Destructive · `full` | | `delete_volumeset_volume` | Delete Volumeset Volume | Delete a single volume from a volume set. | Destructive · `full` | *** ## Observability Inspect deployments, logs, events, replicas, metrics, traces, the audit trail, and the status of asynchronous operation commands. All are in `core`. | Name | Label | Description | Access | | :----------------------- | :------------------------------ | :---------------------------------------------------------------------------------------------------------------------------------- | :----- | | `list_deployments` | List Workload Deployments | A workload's per-location rollout status — the primary readiness check after a deploy. | Read | | `get_workload_logs` | Get Workload Logs | Query a workload's logs by structured parameters or a raw LogQL query. | Read | | `get_workload_events` | Get Workload Events | Fetch a workload's event log to diagnose readiness, liveness, and deploy failures. | Read | | `list_workload_replicas` | List Workload Replicas | List the running replicas of a workload in a location. | Read | | `list_metrics` | List & Discover Metrics | Discover the metrics and labels you can query, each with a correct PromQL template. | Read | | `query_metrics` | Query Workload Metrics (PromQL) | Run a PromQL query against Control Plane metrics. | Read | | `query_audit_events` | Query Audit Events | Query the audit trail for mutations on resources of a kind. | Read | | `query_traces` | Query Distributed Traces | Search distributed traces (TraceQL) for slow or failing requests. | Read | | `get_trace` | Get Trace | Fetch one trace by ID and summarize its span tree, durations, and errors. | Read | | `list_commands` | List Commands | List the asynchronous operation commands issued against a workload or volume set — cron runs, replica stops, and volume operations. | Read | | `get_command` | Get a Command | Fetch one asynchronous command by ID to check a long-running operation's status. | Read | *** ## Terraform & IaC Convert manifests to Terraform and export existing resources as HCL for [infrastructure as code](/iac/overview). Batch export and kind discovery are in `full`. | Name | Label | Description | Access | | :----------------------- | :--------------------------------------- | :------------------------------------------------------------------------------------- | :------------ | | `convert_to_terraform` | Convert a manifest to Terraform | Convert a resource manifest to Terraform HCL, dry-run validated against the API first. | Read | | `export_terraform` | Export an existing resource to Terraform | Generate Terraform HCL for existing resources from a self link, single or in bulk. | Read | | `export_terraform_batch` | Export many resources to Terraform | Generate merged, de-duplicated Terraform for several existing resources in one call. | Read · `full` | | `list_terraform_kinds` | List Terraform-convertible kinds | List the resource kinds the exporter can convert. | Read · `full` | *** ## Cloud accounts, agents & networking Onboard [cloud accounts](/reference/cloudaccount), create [agents](/reference/agent) for private connectivity, and reserve [IP sets](/reference/ipset). All are in `full`. | Name | Label | Description | Access | | :---------------------------------- | :----------------------------------- | :----------------------------------------------------------------------------------------- | :------------------- | | `how_to_create_aws_cloud_account` | How to Create an AWS Cloud Account | The canonical IAM steps to onboard an AWS cloud account, filled in with your values. | Read · `full` | | `how_to_create_gcp_cloud_account` | How to Create a GCP Cloud Account | The steps to onboard a GCP cloud account. | Read · `full` | | `how_to_create_azure_cloud_account` | How to Create an Azure Cloud Account | The steps to onboard an Azure cloud account. | Read · `full` | | `how_to_create_ngs_cloud_account` | How to Create an NGS Cloud Account | The steps to onboard a NATS NGS cloud account. | Read · `full` | | `create_cloud_account` | Create a Cloud Account | Create a cloud account bridging Control Plane to AWS, GCP, Azure, or NGS. | Write · `full` | | `update_cloud_account` | Update a Cloud Account | Update a cloud account's settings. | Destructive · `full` | | `create_agent` | Create an Agent | Create an agent for secure connectivity to a private network. | Write · `full` | | `update_agent` | Update an Agent | Update an agent's settings. | Destructive · `full` | | `get_agent_info` | Get Agent Info | Fetch an agent's real-time status — active state, last heartbeat, peer and service counts. | Read · `full` | | `get_agent_eventlog` | Get Agent Event Log | Fetch an agent's event log. | Read · `full` | | `create_ipset` | Create an IP Set | Reserve static public IPs per location. | Write · `full` | | `update_ipset` | Update an IP Set | Update an IP set's settings. | Destructive · `full` | | `add_ipset_location` | Add or update IP set locations | Add locations to an IP set or update their retention policy. | Write · `full` | | `remove_ipset_location` | Remove IP set locations | Remove locations from an IP set. | Destructive · `full` | *** ## Organization & operations Manage [org](/reference/org) settings, [quotas](/reference/quota), [audit contexts](/reference/auditctx), and [external logging](/external-logging/overview). All are in `full` except `list_quotas`. | Name | Label | Description | Access | | :--------------------------- | :--------------------------------- | :------------------------------------------------------------------------------------ | :------------------- | | `update_org` | Update Organization Settings | Update organization-wide settings. | Destructive · `full` | | `list_quotas` | List Organization Quotas | List the org's resource quotas with current usage — filter to those near their limit. | Read | | `get_quota` | Get Quota Details | Fetch the details and current usage of one quota. | Read · `full` | | `create_audit_context` | Create Audit Context | Create a custom audit context for tamper-proof external events. | Write · `full` | | `edit_audit_context` | Edit an Audit Context | Update an audit context's description and tags. | Write · `full` | | `get_external_logging` | Get External Logging Configuration | View the org's primary and extra external logging providers. | Read · `full` | | `configure_external_logging` | Configure External Logging | Configure log shipping to an external provider (S3, Datadog, Coralogix, and more). | Destructive · `full` | | `remove_external_logging` | Remove External Logging Provider | Remove an external logging provider from the org. | Destructive · `full` | *** ## Managed Kubernetes Provision and update [managed Kubernetes](/mk8s/overview) clusters across cloud and bare-metal providers. This family is available in the `mk8s` and `full` profiles — connect to `https://mcp.cpln.io/mcp?toolsets=mk8s` to use it. | Name | Label | Description | Access | | :------------------------- | :--------------------------------- | :-------------------------------------------------------------------------------- | :------------------- | | `create_mk8s_aws` | Create an AWS mk8s cluster | Provision a managed Kubernetes cluster on AWS. | Write · `mk8s` | | `update_mk8s_aws` | Update an AWS mk8s cluster | Update an AWS managed Kubernetes cluster. | Destructive · `mk8s` | | `create_mk8s_gcp` | Create a GCP mk8s cluster | Provision a managed Kubernetes cluster on GCP. | Write · `mk8s` | | `update_mk8s_gcp` | Update a GCP mk8s cluster | Update a GCP managed Kubernetes cluster. | Destructive · `mk8s` | | `create_mk8s_azure` | Create an Azure mk8s cluster | Provision a managed Kubernetes cluster on Azure. | Write · `mk8s` | | `update_mk8s_azure` | Update an Azure mk8s cluster | Update an Azure managed Kubernetes cluster. | Destructive · `mk8s` | | `create_mk8s_hetzner` | Create a Hetzner mk8s cluster | Provision a managed Kubernetes cluster on Hetzner. | Write · `mk8s` | | `update_mk8s_hetzner` | Update a Hetzner mk8s cluster | Update a Hetzner managed Kubernetes cluster. | Destructive · `mk8s` | | `create_mk8s_digitalocean` | Create a DigitalOcean mk8s cluster | Provision a managed Kubernetes cluster on DigitalOcean. | Write · `mk8s` | | `update_mk8s_digitalocean` | Update a DigitalOcean mk8s cluster | Update a DigitalOcean managed Kubernetes cluster. | Destructive · `mk8s` | | `create_mk8s_linode` | Create a Linode mk8s cluster | Provision a managed Kubernetes cluster on Linode. | Write · `mk8s` | | `update_mk8s_linode` | Update a Linode mk8s cluster | Update a Linode managed Kubernetes cluster. | Destructive · `mk8s` | | `create_mk8s_oblivus` | Create an Oblivus mk8s cluster | Provision a managed Kubernetes cluster on Oblivus. | Write · `mk8s` | | `update_mk8s_oblivus` | Update an Oblivus mk8s cluster | Update an Oblivus managed Kubernetes cluster. | Destructive · `mk8s` | | `create_mk8s_lambdalabs` | Create a Lambda Labs mk8s cluster | Provision a managed Kubernetes cluster on Lambda Labs. | Write · `mk8s` | | `update_mk8s_lambdalabs` | Update a Lambda Labs mk8s cluster | Update a Lambda Labs managed Kubernetes cluster. | Destructive · `mk8s` | | `create_mk8s_paperspace` | Create a Paperspace mk8s cluster | Provision a managed Kubernetes cluster on Paperspace. | Write · `mk8s` | | `update_mk8s_paperspace` | Update a Paperspace mk8s cluster | Update a Paperspace managed Kubernetes cluster. | Destructive · `mk8s` | | `create_mk8s_triton` | Create a Triton mk8s cluster | Provision a managed Kubernetes cluster on Triton. | Write · `mk8s` | | `update_mk8s_triton` | Update a Triton mk8s cluster | Update a Triton managed Kubernetes cluster. | Destructive · `mk8s` | | `create_mk8s_generic` | Create a generic mk8s cluster | Provision a managed Kubernetes cluster on generic or self-managed infrastructure. | Write · `mk8s` | | `update_mk8s_generic` | Update a generic mk8s cluster | Update a generic managed Kubernetes cluster. | Destructive · `mk8s` | *** ## Next steps Endpoint, authentication, and the compatible-tools list Practical prompts that put these tools to work Skills, agents, commands, and guardrails on top of the MCP server The full configuration and behavior of every Control Plane resource # Visual Studio Code Source: https://docs.controlplane.com/ai/vscode Connect Visual Studio Code to Control Plane using GitHub Copilot's MCP support to manage your infrastructure from your editor. For background on MCP servers in VS Code, see the official [VS Code MCP documentation](https://code.visualstudio.com/docs/copilot/customization/mcp-servers). ## Prerequisites Download [VS Code](https://code.visualstudio.com) and install the [GitHub Copilot](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot) extension Sign up at [console.cpln.io](https://console.cpln.io) if you don't have an account yet ## Add the MCP server Open the Command Palette (`Cmd/Ctrl + Shift + P`) and run **MCP: Add Server**. Follow the prompts: | Prompt | Value | | ----------- | ------------------------- | | Server type | **HTTP** | | URL | `https://mcp.cpln.io/mcp` | | Name | `cpln` | | Scope | **Global** | Connecting to the bare URL uses the default **`core`** toolset profile — a focused set that already covers the whole build-and-operate workflow (it can read and delete any resource kind generically, and reach anything else through the raw-API tool), which is the right fit for this client. Append `?toolsets=mk8s` to add BYOK Kubernetes tools, or `?toolsets=full` for everything. See [toolset profiles](/ai/mcp#toolset-profiles). VS Code shows a permission prompt to authenticate the new server. Click **Allow** — your browser opens. 1. Sign in to Control Plane (Google, GitHub, Microsoft, or SAML). 2. Select the orgs you want VS Code to access. 3. Click **Authorize**. Your browser shows a success page — close the tab and return to VS Code. Open GitHub Copilot Chat and ask "list GVCs in org ``" (using one of the orgs you authorized) to confirm. ## Example conversation ``` Use org "my-org" and gvc "my-gvc" for context. ``` ``` Create a publicly accessible workload called "my-api" using nginx:latest on port 80. ``` ``` Monitor the readiness of my-api and give me the public endpoint once it's ready. ``` ## Troubleshooting Open the Command Palette → **MCP: List Servers** → select **cpln** → **Sign Out**. Then open **MCP: List Servers** again, select **cpln**, and choose **Start Server** — VS Code prompts you to authenticate. Sign in again and adjust your org selections on the consent screen. ## Next steps Practical prompts for common Control Plane workflows 30+ production-ready templates your AI can deploy on request # Create an agent Source: https://docs.controlplane.com/api-reference/agent/create-an-agent post /org/{org}/agent # Delete an agent by name Source: https://docs.controlplane.com/api-reference/agent/delete-an-agent-by-name delete /org/{org}/agent/{name} # Get all agents Source: https://docs.controlplane.com/api-reference/agent/get-all-agents get /org/{org}/agent Response will contain all the agents the caller is authorized to view # Get all agents based on a query Source: https://docs.controlplane.com/api-reference/agent/get-all-agents-based-on-a-query post /org/{org}/agent/-query Response will contain all the agents the caller is authorized to view # Get an agent by name Source: https://docs.controlplane.com/api-reference/agent/get-an-agent-by-name get /org/{org}/agent/{name} Response will contain the requested agent if the caller is authorized to view it # Get available permissions for an agent Source: https://docs.controlplane.com/api-reference/agent/get-available-permissions-for-an-agent get /org/{org}/agent/-schema/permissions Response contains the grantable permissions that are available for an agent when adding a binding in a policy. # Get the access report for an agent by name Source: https://docs.controlplane.com/api-reference/agent/get-the-access-report-for-an-agent-by-name get /org/{org}/agent/{name}/-accessreport Response will contain the permissions and associated bindings (principal and granted permission) for an agent if the caller is authorized to view it # Perform a Patch on an agent by name Source: https://docs.controlplane.com/api-reference/agent/perform-a-patch-on-an-agent-by-name patch /org/{org}/agent/{name} # Query agent health Source: https://docs.controlplane.com/api-reference/agent/query-agent-health get /org/{org}/agent/{name}/-info # API Reference Source: https://docs.controlplane.com/api-reference/api REST API reference for Control Plane covering Core and Audit APIs, Bearer token authentication, service account keys, and user access tokens. ## Overview Two APIs are available: * **Core API** - Main API for managing Control Plane resources * **Audit API** - API for querying and creating audit events ## API Endpoints | API | Endpoint URL | Fallback Swagger API Documentation | | :---- | :--------------------------------------------------------- | :----------------------------------------------------------------------------- | | Core | [https://api.cpln.io/](https://api.cpln.io/) | [https://console.cpln.io/openapi/core](https://console.cpln.io/openapi/core) | | Audit | [https://audit.cpln.io/audit](https://audit.cpln.io/audit) | [https://console.cpln.io/openapi/audit](https://console.cpln.io/openapi/audit) | ## Authentication The Control Plane APIs use Bearer token authentication. All API requests must include an authorization header with a valid token. ### Obtaining Authorization Tokens You can authenticate using one of the following methods: #### Service Account Key [Create a service account](/guides/create-service-account) in your organization with the necessary permissions, then generate a service account key. This key can be used as an API token. #### User Access Token If you're using the [Control Plane CLI](/cli-reference/overview), first log in by running [cpln login](/cli-reference/commands/login). The CLI prints a login URL and a confirmation code; open the URL in a browser on any device and authenticate using your identity provider (Google, Microsoft, etc.). After logging in, your CLI profile receives an access token that is used for all subsequent requests. To obtain this access token, run: ```bash theme={null} cpln profile token ``` Replace `` with the name of your logged-in [CLI profile](/cli-reference/commands/profile#profile-get). Use the returned token as your authorization token. ### Using Authentication Include your token in the `Authorization` header of all API requests: ```bash theme={null} Authorization: Bearer YOUR_TOKEN_HERE ``` Example with cURL: ```bash theme={null} curl --request GET \ --url https://api.cpln.io/org/{org}/gvc/{name} \ --header 'Authorization: YOUR_TOKEN_HERE' ``` ## Using the Interactive API Playground Each endpoint in this documentation includes a **"Try it"** button that opens an interactive API playground. You can use this to: 1. **Test endpoints directly** - Make live API calls without leaving the documentation 2. **See request/response examples** - View formatted JSON responses 3. **Copy code samples** - Get ready-to-use cURL, Python, or JavaScript examples ### Example: Getting a GVC To get a GVC (Global Virtual Cloud) using the interactive playground: 1. Navigate to the **GVC** section in the Core API 2. Click on **"Get a GVC by name"** 3. Click the green **"Try it"** button 4. Enter your authorization token in the authentication field 5. Fill in the required parameters: * `org`: Your organization name (e.g., `my-org`) * `name`: The name of your GVC (e.g., `my-gvc`) 6. Click **"Send"** to execute the request The response will display the GVC details in JSON format, which you can copy or use as a reference for your own API calls. ### Example Request Here's a complete example of getting a GVC using cURL: ```bash theme={null} curl --request GET \ --url https://api.cpln.io/org/my-org/gvc/my-gvc \ --header 'Authorization: YOUR_TOKEN_HERE' ``` Successful response: ```json theme={null} { "kind": "gvc", "id": "aac813e3-0fef-423f-9dcd-630f5a59e56a", "name": "my-gvc", "alias": "a1gm4cx40er00", "description": "my-gvc", "version": 12, "tags": {}, "created": "2024-01-10T21:50:46.156Z", "lastModified": "2025-11-20T13:09:31.150Z", "spec": { "env": [ { "name": "KEY", "value": "VALUE" } ], "loadBalancer": { "trustedProxies": 0 }, "staticPlacement": { "locationLinks": ["/org/my-org/location/aws-eu-central-1"] } }, "links": [ { "rel": "self", "href": "/org/my-org/gvc/my-gvc" }, { "rel": "org", "href": "/org/my-org" }, { "rel": "workload", "href": "/org/my-org/gvc/my-gvc/workload" }, { "rel": "identity", "href": "/org/my-org/gvc/my-gvc/identity" } ], "status": {} } ``` # Create a new audit event Source: https://docs.controlplane.com/api-reference/audit/producer/create-a-new-audit-event post /org/{org}/auditctx/{contextName} # Get all audit events by query parameters Source: https://docs.controlplane.com/api-reference/audit/query/get-all-audit-events-by-query-parameters get /org/{org} # Get all audit events by request ID Source: https://docs.controlplane.com/api-reference/audit/query/get-all-audit-events-by-request-id get /org/{org}/requestId/{requestId} # Get all audit events by resource ID Source: https://docs.controlplane.com/api-reference/audit/query/get-all-audit-events-by-resource-id get /org/{org}/resource/id/{resourceId} # Get all audit events by resource name Source: https://docs.controlplane.com/api-reference/audit/query/get-all-audit-events-by-resource-name get /org/{org}/resource/name/{resourceName} # Get all audit events by subject email Source: https://docs.controlplane.com/api-reference/audit/query/get-all-audit-events-by-subject-email get /org/{org}/subject/email/{subjectEmail} # Get an audit event by ID Source: https://docs.controlplane.com/api-reference/audit/query/get-an-audit-event-by-id get /org/{org}/id/{id} # Create an audit context Source: https://docs.controlplane.com/api-reference/auditctx/create-an-audit-context post /org/{org}/auditctx # Get all audit contexts Source: https://docs.controlplane.com/api-reference/auditctx/get-all-audit-contexts get /org/{org}/auditctx Response will contain all the audit contexts the caller is authorized to view # Get all audit contexts based on a query Source: https://docs.controlplane.com/api-reference/auditctx/get-all-audit-contexts-based-on-a-query post /org/{org}/auditctx/-query Response will contain all the audit contexts the caller is authorized to view # Get an audit context by name Source: https://docs.controlplane.com/api-reference/auditctx/get-an-audit-context-by-name get /org/{org}/auditctx/{name} Response will contain the requested audit context if the caller is authorized to view it # Get available permissions for an audit context Source: https://docs.controlplane.com/api-reference/auditctx/get-available-permissions-for-an-audit-context get /org/{org}/auditctx/-schema/permissions Response contains the grantable permissions that are available for an audit context when adding a binding in a policy. # Get the access report for an audit context by name Source: https://docs.controlplane.com/api-reference/auditctx/get-the-access-report-for-an-audit-context-by-name get /org/{org}/auditctx/{name}/-accessreport Response will contain the permissions and associated bindings (principal and granted permission) for an audit context if the caller is authorized to view it # Perform a Patch on an audit context by name Source: https://docs.controlplane.com/api-reference/auditctx/perform-a-patch-on-an-audit-context-by-name patch /org/{org}/auditctx/{name} # Create a cloud account Source: https://docs.controlplane.com/api-reference/cloudaccount/create-a-cloud-account post /org/{org}/cloudaccount # Delete an cloud account by name Source: https://docs.controlplane.com/api-reference/cloudaccount/delete-an-cloud-account-by-name delete /org/{org}/cloudaccount/{name} # Get a cloud account by name Source: https://docs.controlplane.com/api-reference/cloudaccount/get-a-cloud-account-by-name get /org/{org}/cloudaccount/{name} Response will contain the requested cloud account if the caller is authorized to view it # Get all cloud accounts Source: https://docs.controlplane.com/api-reference/cloudaccount/get-all-cloud-accounts get /org/{org}/cloudaccount Response will contain all the cloud accounts the caller is authorized to view # Get all cloud accounts based on a query Source: https://docs.controlplane.com/api-reference/cloudaccount/get-all-cloud-accounts-based-on-a-query post /org/{org}/cloudaccount/-query Response will contain all the cloud accounts the caller is authorized to view # Get available permissions for a cloud account resource type Source: https://docs.controlplane.com/api-reference/cloudaccount/get-available-permissions-for-a-cloud-account-resource-type get /org/{org}/cloudaccount/-schema/permissions Response contains the grantable permissions that are available for a cloud account when adding a binding in a policy. # Get the access report for a cloud account by name Source: https://docs.controlplane.com/api-reference/cloudaccount/get-the-access-report-for-a-cloud-account-by-name get /org/{org}/cloudaccount/{name}/-accessreport Response will contain the permissions and associated bindings (principal and granted permission) for a cloud account if the caller is authorized to view it # Perform a Patch on a cloud account by name Source: https://docs.controlplane.com/api-reference/cloudaccount/perform-a-patch-on-a-cloud-account-by-name patch /org/{org}/cloudaccount/{name} # Create a domain Source: https://docs.controlplane.com/api-reference/domain/create-a-domain post /org/{org}/domain # Delete a domain by name Source: https://docs.controlplane.com/api-reference/domain/delete-a-domain-by-name delete /org/{org}/domain/{name} # Get a domain by name Source: https://docs.controlplane.com/api-reference/domain/get-a-domain-by-name get /org/{org}/domain/{name} Response will contain the requested domain if the caller is authorized to view it # Get all domains Source: https://docs.controlplane.com/api-reference/domain/get-all-domains get /org/{org}/domain Response will contain all the domains the caller is authorized to view # Get all domains based on a query Source: https://docs.controlplane.com/api-reference/domain/get-all-domains-based-on-a-query post /org/{org}/domain/-query Response will contain all the domains the caller is authorized to view # Get available permissions for a domain resource type Source: https://docs.controlplane.com/api-reference/domain/get-available-permissions-for-a-domain-resource-type get /org/{org}/domain/-schema/permissions Response contains the grantable permissions that are available for a domain when adding a binding in a policy. # Get the access report for a domain by name Source: https://docs.controlplane.com/api-reference/domain/get-the-access-report-for-a-domain-by-name get /org/{org}/domain/{name}/-accessreport Response will contain the permissions and associated bindings (principal and granted permission) for a domain if the caller is authorized to view it # Perform a Patch on a domain by name Source: https://docs.controlplane.com/api-reference/domain/perform-a-patch-on-a-domain-by-name patch /org/{org}/domain/{name} # Create a group Source: https://docs.controlplane.com/api-reference/group/create-a-group post /org/{org}/group # Delete a group by name Source: https://docs.controlplane.com/api-reference/group/delete-a-group-by-name delete /org/{org}/group/{name} # Get a group by name Source: https://docs.controlplane.com/api-reference/group/get-a-group-by-name get /org/{org}/group/{name} Response will contain the requested group if the caller is authorized to view it # Get all groups Source: https://docs.controlplane.com/api-reference/group/get-all-groups get /org/{org}/group Response will contain all the groups the caller is authorized to view # Get all groups based on a query Source: https://docs.controlplane.com/api-reference/group/get-all-groups-based-on-a-query post /org/{org}/group/-query Response will contain all the groups the caller is authorized to view # Get available permissions for a group resource type Source: https://docs.controlplane.com/api-reference/group/get-available-permissions-for-a-group-resource-type get /org/{org}/group/-schema/permissions Response contains the grantable permissions that are available for a group when adding a binding in a policy. # Get the access report for a group by name Source: https://docs.controlplane.com/api-reference/group/get-the-access-report-for-a-group-by-name get /org/{org}/group/{name}/-accessreport Response will contain the permissions and associated bindings (principal and granted permission) for a group if the caller is authorized to view it # Perform a Patch on a group by name Source: https://docs.controlplane.com/api-reference/group/perform-a-patch-on-a-group-by-name patch /org/{org}/group/{name} # Create a GVC Source: https://docs.controlplane.com/api-reference/gvc/create-a-gvc post /org/{org}/gvc # Delete a GVC by name Source: https://docs.controlplane.com/api-reference/gvc/delete-a-gvc-by-name delete /org/{org}/gvc/{name} # Get a GVC by name Source: https://docs.controlplane.com/api-reference/gvc/get-a-gvc-by-name get /org/{org}/gvc/{name} Response will contain the requested GVC if the caller is authorized to view it # Get all GVCs Source: https://docs.controlplane.com/api-reference/gvc/get-all-gvcs get /org/{org}/gvc Response will contain all the GVCs the caller is authorized to view # Get all GVCs based on a query Source: https://docs.controlplane.com/api-reference/gvc/get-all-gvcs-based-on-a-query post /org/{org}/gvc/-query Response will contain all the GVCs the caller is authorized to view # Get available permissions for a GVC resource type Source: https://docs.controlplane.com/api-reference/gvc/get-available-permissions-for-a-gvc-resource-type get /org/{org}/gvc/-schema/permissions Response contains the grantable permissions that are available for a GVC when adding a binding in a policy. # Get the access report for a GVC by name Source: https://docs.controlplane.com/api-reference/gvc/get-the-access-report-for-a-gvc-by-name get /org/{org}/gvc/{name}/-accessreport Response will contain the permissions and associated bindings (principal and granted permission) for a GVC if the caller is authorized to view it # Perform a Patch on a GVC by name Source: https://docs.controlplane.com/api-reference/gvc/perform-a-patch-on-a-gvc-by-name patch /org/{org}/gvc/{name} # Create an identity Source: https://docs.controlplane.com/api-reference/identity/create-an-identity post /org/{org}/gvc/{gvc}/identity # Delete an identity by GVC and name Source: https://docs.controlplane.com/api-reference/identity/delete-an-identity-by-gvc-and-name delete /org/{org}/gvc/{gvc}/identity/{name} # Get all GVC identities based on a query Source: https://docs.controlplane.com/api-reference/identity/get-all-gvc-identities-based-on-a-query post /org/{org}/identity/-query Response will contain all the GVC identities the caller is authorized to view # Get all identities from all GVCs Source: https://docs.controlplane.com/api-reference/identity/get-all-identities-from-all-gvcs get /org/{org}/identity Response will contain all the identities from all the GVCs the caller is authorized to view # Get all identities in a GVC Source: https://docs.controlplane.com/api-reference/identity/get-all-identities-in-a-gvc get /org/{org}/gvc/{gvc}/identity # Get an identity by GVC and name Source: https://docs.controlplane.com/api-reference/identity/get-an-identity-by-gvc-and-name get /org/{org}/gvc/{gvc}/identity/{name} Response will contain the requested identity if the caller is authorized to view it # Get available permissions for an identity Source: https://docs.controlplane.com/api-reference/identity/get-available-permissions-for-an-identity get /org/{org}/identity/-schema/permissions Response contains the grantable permissions that are available for an identity when adding a binding in a policy. # Get the access report for an identity by GVC and name Source: https://docs.controlplane.com/api-reference/identity/get-the-access-report-for-an-identity-by-gvc-and-name get /org/{org}/gvc/{gvc}/identity/{name}/-accessreport Response will contain the permissions and associated bindings (principal and granted permission) for an identity if the caller is authorized to view it # Perform a Patch on an identity by name Source: https://docs.controlplane.com/api-reference/identity/perform-a-patch-on-an-identity-by-name patch /org/{org}/gvc/{gvc}/identity/{name} # Delete an image by name Source: https://docs.controlplane.com/api-reference/image/delete-an-image-by-name delete /org/{org}/image/{name} # Get all images Source: https://docs.controlplane.com/api-reference/image/get-all-images get /org/{org}/image Response will contain all the images the caller is authorized to view # Get all images based on a query Source: https://docs.controlplane.com/api-reference/image/get-all-images-based-on-a-query post /org/{org}/image/-query Response will contain all the images the caller is authorized to view # Get a image by name Source: https://docs.controlplane.com/api-reference/image/get-an-image-by-name get /org/{org}/image/{name} Response will contain the requested image if the caller is authorized to view it # Get available permissions for an image resource type Source: https://docs.controlplane.com/api-reference/image/get-available-permissions-for-an-image-resource-type get /org/{org}/image/-schema/permissions Response contains the grantable permissions that are available for an image when adding a binding in a policy. # Get the access report for an image by name Source: https://docs.controlplane.com/api-reference/image/get-the-access-report-for-an-image-by-name get /org/{org}/image/{name}/-accessreport Response will contain the permissions and associated bindings (principal and granted permission) for an image if the caller is authorized to view it # Perform a Patch on an image by name Source: https://docs.controlplane.com/api-reference/image/perform-a-patch-on-an-image-by-name patch /org/{org}/image/{name} # Create an IP Set Source: https://docs.controlplane.com/api-reference/ipset/create-an-ip-set post /org/{org}/ipset # Delete an IP Set by name Source: https://docs.controlplane.com/api-reference/ipset/delete-an-ip-set-by-name delete /org/{org}/ipset/{name} # Get all IP Sets Source: https://docs.controlplane.com/api-reference/ipset/get-all-ip-sets get /org/{org}/ipset Response will contain all the IP Sets the caller is authorized to view # Get an IP Set by name Source: https://docs.controlplane.com/api-reference/ipset/get-an-ip-set-by-name get /org/{org}/ipset/{name} Response will contain the requested IP Set if the caller is authorized to view it # Get available permissions for an IP Set resource type Source: https://docs.controlplane.com/api-reference/ipset/get-available-permissions-for-an-ip-set-resource-type get /org/{org}/ipset/-schema/permissions Response contains the grantable permissions that are available for a IP Set when adding a binding in a policy. # Get the access report for an IP Set by name Source: https://docs.controlplane.com/api-reference/ipset/get-the-access-report-for-an-ip-set-by-name get /org/{org}/ipset/{name}/-accessreport Response will contain the permissions and associated bindings (principal and granted permission) for an IP Set if the caller is authorized to view it # Perform a Patch on a secret by name Source: https://docs.controlplane.com/api-reference/ipset/perform-a-patch-on-an-ip-set-by-name patch /org/{org}/secret/{name} # Get a location by name Source: https://docs.controlplane.com/api-reference/location/get-a-location-by-name get /org/{org}/location/{name} Response will contain the requested location if the caller is authorized to view it # Get all locations Source: https://docs.controlplane.com/api-reference/location/get-all-locations get /org/{org}/location Response will contain all the locations the caller is authorized to view # Get all locations based on a query Source: https://docs.controlplane.com/api-reference/location/get-all-locations-based-on-a-query post /org/{org}/location/-query Response will contain all the locations the caller is authorized to view # Get available permissions for a location resource type Source: https://docs.controlplane.com/api-reference/location/get-available-permissions-for-a-location-resource-type get /org/{org}/location/-schema/permissions Response contains the grantable permissions that are available for a location when adding a binding in a policy. # Get the access report for a location by name Source: https://docs.controlplane.com/api-reference/location/get-the-access-report-for-a-location-by-name get /org/{org}/location/{name}/-accessreport Response will contain the permissions and associated bindings (principal and granted permission) for a location if the caller is authorized to view it # Perform a Patch on a location by name Source: https://docs.controlplane.com/api-reference/location/perform-a-patch-on-a-location-by-name patch /org/{org}/location/{name} # CA root for this cluster Source: https://docs.controlplane.com/api-reference/mk8s/ca-root-for-this-cluster get /org/{org}/mk8s/{name}/-cacerts PEM-encoded CA certificate # Check if cluster is deployed Source: https://docs.controlplane.com/api-reference/mk8s/check-if-cluster-is-deployed get /org/{org}/mk8s/{name}/-ready # Create an Mk8s Cluster Source: https://docs.controlplane.com/api-reference/mk8s/create-an-mk8s-cluster post /org/{org}/mk8s # Delete an mk8s cluster by name Source: https://docs.controlplane.com/api-reference/mk8s/delete-an-mk8s-cluster-by-name delete /org/{org}/mk8s/{name} # Get all mk8s clusters Source: https://docs.controlplane.com/api-reference/mk8s/get-all-mk8s-clusters get /org/{org}/mk8s Response will contain all the mk8s clusters the caller is authorized to view # Get all mk8s clusters based on a query Source: https://docs.controlplane.com/api-reference/mk8s/get-all-mk8s-clusters-based-on-a-query post /org/{org}/mk8s/-query Response will contain all the mk8s clusters the caller is authorized to view # Get an mk8s cluster by name Source: https://docs.controlplane.com/api-reference/mk8s/get-an-mk8s-cluster-by-name get /org/{org}/mk8s/{name} Response will contain the requested mk8s cluster if the caller is authorized to view it # Get available permissions for an mk8s cluster resource type Source: https://docs.controlplane.com/api-reference/mk8s/get-available-permissions-for-an-mk8s-cluster-resource-type get /org/{org}/mk8s/-schema/permissions Response contains the grantable permissions that are available for an mk8s cluster when adding a binding in a policy. # Get the access report for an mk8s cluster name Source: https://docs.controlplane.com/api-reference/mk8s/get-the-access-report-for-an-mk8s-cluster-name get /org/{org}/mk8s/{name}/-accessreport Response will contain the permissions and associated bindings (principal and granted permission) for a mk8s cluster if the caller is authorized to view it # Perform a Patch on an mk8s cluster by name Source: https://docs.controlplane.com/api-reference/mk8s/perform-a-patch-on-an-mk8s-cluster-by-name patch /org/{org}/mk8s/{name} # Retrieve a cloud-init script Source: https://docs.controlplane.com/api-reference/mk8s/retrieve-a-cloud-init-script post /org/{org}/mk8s/{name}/-cloudinit Note: the result contains sensitive information (secrets). Not all providers support this. In this case 400 will be returned # Retrieve a join shell script. Source: https://docs.controlplane.com/api-reference/mk8s/retrieve-a-join-shell-script post /org/{org}/mk8s/{name}/-join Note: the result contains sensitive information (secrets). Not all providers support this. In this case 400 will be returned # Retrieve a kubeconfig Source: https://docs.controlplane.com/api-reference/mk8s/retrieve-a-kubeconfig post /org/{org}/mk8s/{name}/-kubeconfig The kubeconfig will use the cpln CLI as an auth provider. The response contains not secrets as the cpln CLI is used for authorization # Create an org Source: https://docs.controlplane.com/api-reference/org/create-an-org post /org # Get all orgs Source: https://docs.controlplane.com/api-reference/org/get-all-orgs get /org Response will contain all the orgs the caller is authorized to view # Get all orgs based on a query Source: https://docs.controlplane.com/api-reference/org/get-all-orgs-based-on-a-query post /org/-query Response will contain all the orgs the caller is authorized to view # Get an org by name Source: https://docs.controlplane.com/api-reference/org/get-an-org-by-name get /org/{name} Response will contain the requested org if the caller is authorized to view it # Get available permissions for an org resource type Source: https://docs.controlplane.com/api-reference/org/get-available-permissions-for-an-org-resource-type get /org/{org}/org/-schema/permissions Response contains the grantable permissions that are available for an org when adding a binding in a policy. # Get the access report for an org by name Source: https://docs.controlplane.com/api-reference/org/get-the-access-report-for-an-org-by-name get /org/{name}/-accessreport Response will contain the permissions and associated bindings (principal and granted permission) for an org if the caller is authorized to view it # Perform a Patch on an org by name Source: https://docs.controlplane.com/api-reference/org/perform-a-patch-on-an-org-by-name patch /org/{name} # Create a policy Source: https://docs.controlplane.com/api-reference/policy/create-a-policy post /org/{org}/policy # Delete a policy by name Source: https://docs.controlplane.com/api-reference/policy/delete-a-policy-by-name delete /org/{org}/policy/{name} # Get a policy by name Source: https://docs.controlplane.com/api-reference/policy/get-a-policy-by-name get /org/{org}/policy/{name} Response will contain the requested policy if the caller is authorized to view it # Get all policies Source: https://docs.controlplane.com/api-reference/policy/get-all-policies get /org/{org}/policy Response will contain all the policies the caller is authorized to view # Get all policies based on a query Source: https://docs.controlplane.com/api-reference/policy/get-all-policies-based-on-a-query post /org/{org}/policy/-query Response will contain all the policies the caller is authorized to view # Get available permissions for a policy resource type Source: https://docs.controlplane.com/api-reference/policy/get-available-permissions-for-a-policy-resource-type get /org/{org}/policy/-schema/permissions Response contains the grantable permissions that are available for a policy when adding a binding in a policy. # Get the access report for a policy by name Source: https://docs.controlplane.com/api-reference/policy/get-the-access-report-for-a-policy-by-name get /org/{org}/policy/{name}/-accessreport Response will contain the permissions and associated bindings (principal and granted permission) for a policy if the caller is authorized to view it # Perform a Patch on a policy by name Source: https://docs.controlplane.com/api-reference/policy/perform-a-patch-on-a-policy-by-name patch /org/{org}/policy/{name} # Get a quota by ID Source: https://docs.controlplane.com/api-reference/quota/get-a-quota-by-id get /org/{org}/quota/{id} Response will contain the requested quota if the caller is authorized to view it # Get all quotas Source: https://docs.controlplane.com/api-reference/quota/get-all-quotas get /org/{org}/quota Response will contain all the quotas the caller is authorized to view # Get all quotas based on a query Source: https://docs.controlplane.com/api-reference/quota/get-all-quotas-based-on-a-query post /org/{org}/quota/-query Response will contain all the quotas the caller is authorized to view # Get available permissions for a quota resource type Source: https://docs.controlplane.com/api-reference/quota/get-available-permissions-for-a-quota-resource-type get /org/{org}/quota/-schema/permissions Response contains the grantable permissions that are available for a quota when adding a binding in a policy. # Create a secret Source: https://docs.controlplane.com/api-reference/secret/create-a-secret post /org/{org}/secret # Delete a secret by name Source: https://docs.controlplane.com/api-reference/secret/delete-a-secret-by-name delete /org/{org}/secret/{name} # Get a secret by name Source: https://docs.controlplane.com/api-reference/secret/get-a-secret-by-name get /org/{org}/secret/{name} Response will contain the requested secret (except for any sensitive content) if the caller is authorized to view it. Use "reveal" to get the sensitive content. # Get all secrets Source: https://docs.controlplane.com/api-reference/secret/get-all-secrets get /org/{org}/secret Response will contain all the secrets the caller is authorized to view # Get all secrets based on a query Source: https://docs.controlplane.com/api-reference/secret/get-all-secrets-based-on-a-query post /org/{org}/secret/-query Response will contain all the secrets the caller is authorized to view # Get available permissions for a secret resource type Source: https://docs.controlplane.com/api-reference/secret/get-available-permissions-for-a-secret-resource-type get /org/{org}/secret/-schema/permissions Response contains the grantable permissions that are available for a secret when adding a binding in a policy. # Get the access report for a secret by name Source: https://docs.controlplane.com/api-reference/secret/get-the-access-report-for-a-secret-by-name get /org/{org}/secret/{name}/-accessreport Response will contain the permissions and associated bindings (principal and granted permission) for a secret if the caller is authorized to view it # Get the sensitive content of a secret by name Source: https://docs.controlplane.com/api-reference/secret/get-the-sensitive-content-of-a-secret-by-name get /org/{org}/secret/{name}/-reveal # Perform a Patch on a secret by name Source: https://docs.controlplane.com/api-reference/secret/perform-a-patch-on-a-secret-by-name patch /org/{org}/secret/{name} # Add a key to a service account Source: https://docs.controlplane.com/api-reference/serviceaccount/add-a-key-to-a-service-account post /org/{org}/serviceaccount/{name}/-addKey You may want to capture the result of this as the key will never be shown again # Create a service account Source: https://docs.controlplane.com/api-reference/serviceaccount/create-a-service-account post /org/{org}/serviceaccount # Delete a service account by name Source: https://docs.controlplane.com/api-reference/serviceaccount/delete-a-service-account-by-name delete /org/{org}/serviceaccount/{name} # Get a service account by name Source: https://docs.controlplane.com/api-reference/serviceaccount/get-a-service-account-by-name get /org/{org}/serviceaccount/{name} Response will contain the requested service account if the caller is authorized to view it # Get all service accounts Source: https://docs.controlplane.com/api-reference/serviceaccount/get-all-service-accounts get /org/{org}/serviceaccount Response will contain all the service accounts the caller is authorized to view # Get all service accounts based on a query Source: https://docs.controlplane.com/api-reference/serviceaccount/get-all-service-accounts-based-on-a-query post /org/{org}/serviceaccount/-query Response will contain all the service accounts the caller is authorized to view # Get available permissions for a service account resource type Source: https://docs.controlplane.com/api-reference/serviceaccount/get-available-permissions-for-a-service-account-resource-type get /org/{org}/serviceaccount/-schema/permissions Response contains the grantable permissions that are available for a service account when adding a binding in a policy. # Get the access report for a service account by name Source: https://docs.controlplane.com/api-reference/serviceaccount/get-the-access-report-for-a-service-account-by-name get /org/{org}/serviceaccount/{name}/-accessreport Response will contain the permissions and associated bindings (principal and granted permission) for a service account if the caller is authorized to view it # Perform a Patch on a service account by name Source: https://docs.controlplane.com/api-reference/serviceaccount/perform-a-patch-on-a-service-account-by-name patch /org/{org}/serviceaccount/{name} # Delete/Cancel a task by ID Source: https://docs.controlplane.com/api-reference/task/deletecancel-a-task-by-id delete /org/{org}/task/{id} # Get a task by ID Source: https://docs.controlplane.com/api-reference/task/get-a-task-by-id get /org/{org}/task/{id} Response will contain the requested task if the caller is authorized to view it # Get all tasks Source: https://docs.controlplane.com/api-reference/task/get-all-tasks get /org/{org}/task Response will contain all the tasks the caller is authorized to view # Get all tasks based on a query Source: https://docs.controlplane.com/api-reference/task/get-all-tasks-based-on-a-query post /org/{org}/task/-query Response will contain all the tasks the caller is authorized to view # Get available permissions for a task resource type Source: https://docs.controlplane.com/api-reference/task/get-available-permissions-for-a-task-resource-type get /org/{org}/task/-schema/permissions Response contains the grantable permissions that are available for a task when adding a binding in a policy. # List tasks that you can accept to refuse Source: https://docs.controlplane.com/api-reference/task/list-tasks-that-you-can-accept-to-refuse get /task/-forme # List tasks that you can accept to refuse in a specific org Source: https://docs.controlplane.com/api-reference/task/list-tasks-that-you-can-accept-to-refuse-in-a-specific-org get /org/{org}/task/-forme # Post Source: https://docs.controlplane.com/api-reference/task/post post /org/{org}/task/{id} Accept or refuse a task # Delete a user by name Source: https://docs.controlplane.com/api-reference/user/delete-a-user-by-name delete /org/{org}/user/{name} # Get a user by name Source: https://docs.controlplane.com/api-reference/user/get-a-user-by-name get /org/{org}/user/{name} Response will contain the requested user if the caller is authorized to view it # Get all users Source: https://docs.controlplane.com/api-reference/user/get-all-users get /org/{org}/user Response will contain all the users the caller is authorized to view # Get all users based on a query Source: https://docs.controlplane.com/api-reference/user/get-all-users-based-on-a-query post /org/{org}/user/-query Response will contain all the users the caller is authorized to view # Get available permissions for a user resource type Source: https://docs.controlplane.com/api-reference/user/get-available-permissions-for-a-user-resource-type get /org/{org}/user/-schema/permissions Response contains the grantable permissions that are available for a user when adding a binding in a policy. # Get the access report for a user by name Source: https://docs.controlplane.com/api-reference/user/get-the-access-report-for-a-user-by-name get /org/{org}/user/{name}/-accessreport Response will contain the permissions and associated bindings (principal and granted permission) for a user if the caller is authorized to view it # Invite users to this org Source: https://docs.controlplane.com/api-reference/user/invite-users-to-this-org post /org/{org}/user/-invite # Perform a Patch on a user by name Source: https://docs.controlplane.com/api-reference/user/perform-a-patch-on-a-user-by-name patch /org/{org}/user/{name} # Create a volumeset Source: https://docs.controlplane.com/api-reference/volumeset/create-a-volumeset post /org/{org}/gvc/{gvc}/volumeset # Delete a volumeset by name Source: https://docs.controlplane.com/api-reference/volumeset/delete-a-volumeset-by-name delete /org/{org}/gvc/{gvc}/volumeset/{name} # Get a volumeset by GVC and name Source: https://docs.controlplane.com/api-reference/volumeset/get-a-volumeset-by-gvc-and-name get /org/{org}/gvc/{gvc}/volumeset/{name} Response will contain the requested volumeset if the caller is authorized to view it # Get all volumesets Source: https://docs.controlplane.com/api-reference/volumeset/get-all-volumesets get /org/{org}/volumeset Response will contain all the volumesets the caller is authorized to view # Get all volumesets based on a query Source: https://docs.controlplane.com/api-reference/volumeset/get-all-volumesets-based-on-a-query post /org/{org}/volumeset/-query Response will contain all the volumesets the caller is authorized to view # Get available permissions for a volumeset resource type Source: https://docs.controlplane.com/api-reference/volumeset/get-available-permissions-for-a-volumeset-resource-type get /org/{org}/volumeset/-schema/permissions Response contains the grantable permissions that are available for a volumeset when adding a binding in a policy. # Get the access report for a volumeset by GVC and name Source: https://docs.controlplane.com/api-reference/volumeset/get-the-access-report-for-a-volumeset-by-gvc-and-name get /org/{org}/gvc/{gvc}/volumeset/{name}/-accessreport Response will contain the permissions and associated bindings (principal and granted permission) for a volumeset if the caller is authorized to view it # List volumesets in a GVC you are authorized to view Source: https://docs.controlplane.com/api-reference/volumeset/list-volumesets-in-a-gvc-you-are-authorized-to-view get /org/{org}/gvc/{gvc}/volumeset # Perform a Patch on a volumeset by name Source: https://docs.controlplane.com/api-reference/volumeset/perform-a-patch-on-a-volumeset-by-name patch /org/{org}/gvc/{gvc}/volumeset/{name} # Create a workload Source: https://docs.controlplane.com/api-reference/workload/create-a-workload post /org/{org}/gvc/{gvc}/workload # Delete a workload by name Source: https://docs.controlplane.com/api-reference/workload/delete-a-workload-by-name delete /org/{org}/gvc/{gvc}/workload/{name} # Get a deployment by GVC, workload and name Source: https://docs.controlplane.com/api-reference/workload/get-a-deployment-by-gvc-workload-and-name get /org/{org}/gvc/{gvc}/workload/{workload}/deployment/{name} Fetch a specific deployment of a workload # Get a workload by GVC and name Source: https://docs.controlplane.com/api-reference/workload/get-a-workload-by-gvc-and-name get /org/{org}/gvc/{gvc}/workload/{name} Response will contain the requested workload if the caller is authorized to view it # Get all workloads Source: https://docs.controlplane.com/api-reference/workload/get-all-workloads get /org/{org}/workload Response will contain all the workloads the caller is authorized to view # Get all workloads based on a query Source: https://docs.controlplane.com/api-reference/workload/get-all-workloads-based-on-a-query post /org/{org}/workload/-query Response will contain all the workloads the caller is authorized to view # Get available permissions for a workload resource type Source: https://docs.controlplane.com/api-reference/workload/get-available-permissions-for-a-workload-resource-type get /org/{org}/workload/-schema/permissions Response contains the grantable permissions that are available for a workload when adding a binding in a policy. # Get the access report for a workload by GVC and name Source: https://docs.controlplane.com/api-reference/workload/get-the-access-report-for-a-workload-by-gvc-and-name get /org/{org}/gvc/{gvc}/workload/{name}/-accessreport Response will contain the permissions and associated bindings (principal and granted permission) for a workload if the caller is authorized to view it # List deployments in a workload Source: https://docs.controlplane.com/api-reference/workload/list-deployments-in-a-workload get /org/{org}/gvc/{gvc}/workload/{workload}/deployment # List workloads in a GVC you are authorized to view Source: https://docs.controlplane.com/api-reference/workload/list-workloads-in-a-gvc-you-are-authorized-to-view get /org/{org}/gvc/{gvc}/workload # Perform a Patch on a workload by name Source: https://docs.controlplane.com/api-reference/workload/perform-a-patch-on-a-workload-by-name patch /org/{org}/gvc/{gvc}/workload/{name} # CPLN BYOK Source: https://docs.controlplane.com/byok/overview Integrate your own Kubernetes cluster into Control Plane as a new location, enabling managed workload deployment on your own infrastructure. The CPLN BYOK (Bring Your Own Kubernetes) allows you to integrate a Kubernetes cluster into the Control Plane Cloud Platform. It works by installing a few additional components into the cluster and then it registers as a new Location in the API for your Org(s). Any Workloads that are configured for that Location will be managed by Control Plane and roll out onto your cluster. You get all the benefits of Control Plane with the added flexibility and security of running in your own cloud account or data center. ## Requirements * Label at least one nodegroup in the cluster with `cpln.io/nodeType=core`. This label deploys critical system pods and workloads managed by the Control Plane Platform. * Minimum 2 CPUs per node (4 CPUs or more recommended) * Minimum 8 GB of RAM per node (16 GB or more recommended) * Minimum 2 nodes per cluster (3 or more recommended) * Node processor architecture: amd64, arm64 * Supports the three most recent minor releases of Kubernetes for installation. For information on Kubernetes releases, see the [Kubernetes releases page](https://kubernetes.io/releases/). * Full network connectivity between all nodes in the cluster (either public or private network) * Enable egress access for all nodes (contact customer support for alternatives if this is not feasible) * Exclude service-mesh from your cluster. Control Plane provides an out-of-the-box Istio-based service-mesh * A working load balancer controller so that at least one service of type LoadBalancer is able to obtain a public or private ip address ## Setup Procedure ### Step 1 - Create BYOK Location Using the UI Console 1. Create a new location by either: * Clicking `Locations` in the left menu and then clicking `New`, or * Clicking the `Create` dropdown in the upper right corner and selecting `Location`. 2. Enter a unique name. Click `Next (Tags)`. 3. Enter any optional [tags](/core/misc#tags). Click `Create`. You have now created a new location. To use it, install this location on at least one Kubernetes cluster as described in the next step. ### Step 2 - Install Location on a Kubernetes Cluster 1. Generate the install command: * Click `Actions` in the upper right corner of the created location page and choose `Install`. Control Plane console showing the Actions dropdown with Install option for a BYOK location 2. Copy the generated kubectl command. You will need to apply the command within about 5 and a half minutes, as the manifests contain sensitive tokens. 3. Connect to your Kubernetes cluster that you wish to integrate as a location in Control Plane and apply the kubectl command generated in the previous step. 4. Follow the `cpln-byok-agent` deployment in the `kube-system` namespace. Use the following command to fetch its status: ```shell theme={null} kubectl get pod -l app=cpln-byok-agent -n kube-system ``` The `cpln-byok-agent` will create and maintain the necessary deployments for your location to function. Allow a few minutes for all necessary components to finish deploying. Ensure at least one of the node groups is labeled with `cpln.io/nodeType=core`, as it is required to deploy essential components. You can now add the BYOK location to a GVC in your Org. Repeat [Step 2 - Install Location on a Kubernetes Cluster](#step-2-install-location-on-a-kubernetes-cluster) if you need to add more clusters to the same location, or start from [Step 1 - Create BYOK Location Using the UI Console](#step-1-create-byok-location-using-the-ui-console) to add another location. ## Uninstall Procedure 1. Generate the uninstall command: * Click `Actions` in the upper right corner of the location you want to remove and choose `Uninstall`. Control Plane console showing the Actions dropdown with Uninstall option for a BYOK location 2. Copy the generated `kubectl` command. You will need to apply the command within about 5 and a half minutes, as the manifests contain sensitive tokens. 3. Connect to your Kubernetes cluster that you wish to remove from the location in Control Plane and apply the `kubectl` command generated in the previous step. 4. The job `cpln-agent-uninstall` will be created in the `kube-system` namespace. It will clean up all BYOK components from the cluster. ## Cloud Provider Recommendations ### GKE * Private Cluster: Enabled * Default SNAT: Enabled * Stack type: IPv4 * VPC-native traffic routing: Enabled * Intranode visibility: Disabled * HTTP Load Balancing: Enabled * Calico Network Policy: Enabled * DNS provider: Kube-DNS * Shielded GKE Nodes: Enabled * Application -layer secrets encryption: Enabled * Compute Engine persistent disk CSI Driver: Enabled * Service Mesh: Disabled After the GKE cluster is created, you must provide the IP address of the kube-dns service in the kube-system namespace to support. ```bash theme={null} kubectl get svc -n kube-system kube-dns ``` Once the Control Plane configuration has been applied to the cluster, scale down the kube-dns deployments. ```bash theme={null} kubectl scale --replicas=0 deployment/kube-dns-autoscaler --namespace=kube-system kubectl scale --replicas=0 deployment/kube-dns --namespace=kube-system ``` ### EKS * Enabled Add-ons: * Amazon VPC CNI * kube-proxy * CoreDNS * Amazon EBS CSI Driver ## Settings When using a BYOK Location with Control Plane there are additional settings available to control the behavior of that location. * [Agent Configuration:](/byok/settings/agent) Agent settings are used during the installation and upgrade of all BYOK components. * [Actuator Settings:](/byok/settings/actuator) Actuator settings are used to control ingress and other changes to the way workloads are created in Kubernetes. * [Workload Settings:](/byok/settings/workload) Additional workload settings when using a BYOK Location. * [Volume Set Settings:](/byok/settings/volumeset) Additional volume set settings when using a BYOK Location. ## Guides * [Configure a Content Delivery Network (CDN) Domain.](/guides/configure-cdn#amazon-cloudfront-configuration-steps) # Actuator Settings Source: https://docs.controlplane.com/byok/settings/actuator Configure the BYOK actuator component that processes Control Plane events to manage Kubernetes deployments, routing, and storage on your cluster. The Actuator component receives events from Control Plane and uses the metadata provided in the events to instantiate the desired configuration on the kubernetes cluster. This can be Kubernetes deployments, domain routing configuration, storage devices, etc. ## Settings These environment variables can be added into configuration for the actuator to adjust the behavior of the CPLN Platform. To adjustment these settings, make changes to the [CPLN Platform Add-on](/mk8s/add-ons/byok) configuration or manually adjust the cpln-byok-current configmap if you are running your own Kubernetes. ### INGRESS\_REQ\_CPU * The initial cpu request size used for shared and dedicated load balancer ingress deployments. ### INGRESS\_REQ\_MEM * The initial memory request size used for shared and dedicated load balancer ingress deployments. ### INGRESS\_TARGET\_CPU\_PERCENT * The target cpu percent used by the horizontal pod autoscalers for shared and dedicated load balancer ingress deployments. This should be calculated with awareness that the cpu value used is 4000 millicores but may be less if small nodes are used. For example, if the request size is set to 1000m and you desire a target to be 3000m, then set this value to 300. ### INGRESS\_FIREWALL\_CIDR\_LIST * A comma delimited list of CIDRs used to restrict inbound access for shared and dedicated load balancers. ### INGRESS\_LOAD\_BALANCER\_SCHEME * One of `internet-facing` or `internal`. When internal is used then you must be on a private network to access the workloads running in this location. Automated detection of geo-routing is limited in this configuration since the external monitoring will not be able to reach the endpoints. ### INGRESS\_ANNOTATIONS * Annotations to be used instead of standard generated ones for the load balancer service for shared and dedicated load balancer ingress deployments. This can be useful if your cloud provider load balancer controller is not already supported by Control Plane and requires a specific annotation. ### INGRESS\_EXTRA\_ANNOTATIONS * Extra annotations to be added to the load balancer service for shared and dedicated load balancers. This is useful if a specific annotation should be used in your environment for all load balancers. This is applied after the standard load balancer annotations and can be used to replace existing values. Pass through annotations are applied after this can be used to make additional adjustments to specific dedicated load balancers. ### INGRESS\_PORTS * An array of ports used to limit the custom ports that are added to dedicated load balancers. ### BYOK\_K8S\_API\_WORKLOADS * An array of workload links which are allowed to access the kubernetes api. * The ClusterRole the workloads are given should be specified with the tag cpln/k8sClusterRole on the workload. Ensure the ClusterRole already exists ### THIN\_PROVISION * Percent of CPU that is guaranteed for workloads. #### How to Configure Thin Provisioning To configure thin provisioning, you need to modify the cpln-byok-current ConfigMap in the kube-system namespace. This ConfigMap contains the actuator settings where you can specify the THIN\_PROVISION environment variable. #### Steps to Configure 1. Modify the cpln-byok-current configmap in the kube-system namespace. 2. In the actuator settings, add an environment variable named THIN\_PROVISION. This variable should have a value representing the percentage of resources that will be guaranteed. For example, if you want to reserve 800m when 1000m is requested, set THIN\_PROVISION to .8. Example Configuration: Here is an example of how the configuration should look: ```json theme={null} { "actuator": { "env": { "CACHE_PERIOD_DATA_SERVICE": "600", "LABEL_NODES": "false", "THIN_PROVISION": ".8" } } } ``` ### MESH\_DISABLE\_IP\_AUTOALLOCATE Stamps the mTLS [ServiceEntries](/reference/workload/security) the actuator generates with `networking.istio.io/enable-autoallocate-ip=false`. Defaults to `false` (off); enable it **per location**, only where it is needed. #### When to use it Enable this when a workload — most commonly a [VM workload](/reference/workload/vm) such as a Windows guest — connects to **another workload over the internal mesh endpoint** and the connection is unreliable over **raw TCP** (databases, RDP, or any non-HTTP protocol). * **Symptom:** HTTP requests to the internal endpoint succeed, but plain TCP connections reset intermittently (roughly half the time). * **Cause:** Istio's DNS proxy auto-allocates a virtual IP for each ServiceEntry. In the per-port mesh clusters Control Plane generates, that auto-allocated IP can resolve to an endpoint that is not actually serving, so the TCP connection is reset. HTTP survives because it is re-routed by its `Host` header; raw TCP has no such fallback. * **Fix:** Setting this to `true` removes the auto-allocated IP, leaving only the real workload endpoint, so raw TCP lands on a live target. #### How to configure Add the environment variable to the actuator settings — either through the [CPLN Platform Add-on](/mk8s/add-ons/byok) configuration or by editing the `cpln-byok-current` ConfigMap directly if you run your own Kubernetes: ```json theme={null} { "actuator": { "env": { "MESH_DISABLE_IP_AUTOALLOCATE": "true" } } } ``` After the actuator picks up the change, redeploy or reconcile the affected workload so its ServiceEntry is re-stamped, then re-establish any existing TCP connections. ### LABEL\_NODES Controls whether the actuator automatically manages node labels and workload node selectors to enable isolation between core platform components and user workloads across multiple nodepools. * Core Nodes - infrastructure components: ingress gateways, service mesh, monitoring, cloud integration, etc * Workload Nodes - used for workloads and minimal supporting infrastructure for workloads #### How LABEL\_NODES Works When `LABEL_NODES` is set to `true`, the actuator implements the following behavior: 1. **Node Labeling**: The actuator automatically labels all nodes with `cpln.io/nodeType`: * If a node has a taint `cpln.io/nodeType` with a specific value, the node receives that value as its label * If a node does not have the `cpln.io/nodeType` taint, it receives the label `cpln.io/nodeType=core` 2. **Workload Node Selection**: The actuator adds a node selector `cpln.io/nodeType=workload` to all user workloads 3. **Taint Toleration**: All user workloads automatically tolerate the taint `cpln.io/nodeType=workload` #### Configuring Multi-Nodepool Isolation To run the CPLN Platform with separate nodepools for core components and user workloads: 1. **Enable LABEL\_NODES**: Set the `LABEL_NODES` environment variable to `true` in the actuator settings 2. **Configure Workload Nodepools**: Add a taint to all nodes in your workload nodepools: ```bash theme={null} cpln.io/nodeType=workload:NoSchedule ``` 3. **Configure Core Nodepools**: Leave core component nodepools without any taints since the workloads running there will not tolerate any taints. It's ok to provide the label `cpln.io/nodeType=core` #### How Isolation Works With this configuration: * **User workloads** will only run on workload nodes because: * They have a node selector requiring `cpln.io/nodeType=workload` * They tolerate the `cpln.io/nodeType=workload:NoSchedule` taint * **Core platform components** will only run on core nodes because: * They do not tolerate the `cpln.io/nodeType=workload:NoSchedule` taint on workload nodes #### Example Isolated Node Pool Configuration Enable LABEL\_NODES in actuator: ```json theme={null} { "actuator": { "env": { "LABEL_NODES": "true" } } } ``` In your Kubernetes or Managed Kubernetes (MK8s) nodepool configuration, add the appropriate taints: **Workload Nodepool(s):** ```yaml theme={null} taints: - key: cpln.io/nodeType value: workload effect: NoSchedule ``` **Core Nodepool(s):** ```yaml theme={null} # Must not have any taints labels: cpln.io/nodeType: core ``` ### WORKLOAD\_DNS\_CONFIG\_OPTIONS Overrides the pod `dnsConfig.options` the actuator applies to standard, stateful, cron, and serverless workload pods in this location. Value is a JSON array of `{name, value?}` objects, matching Kubernetes' [PodDNSConfigOption](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-config) shape. Defaults to: ```json theme={null} [ { "name": "ndots", "value": "2" }, { "name": "single-request-reopen" }, { "name": "timeout", "value": "2" }, { "name": "attempts", "value": "3" } ] ``` These defaults reduce transient `getaddrinfo` resolution failures. Set the value to `[]` to disable pod DNS options entirely. Changing this setting does not force a fleet-wide pod restart. The new options take effect the next time each workload is otherwise updated (its next deploy). #### How to configure Add the environment variable to the actuator settings — either through the [CPLN Platform Add-on](/mk8s/add-ons/byok) configuration or by editing the `cpln-byok-current` ConfigMap directly if you run your own Kubernetes: ```json theme={null} { "actuator": { "env": { "WORKLOAD_DNS_CONFIG_OPTIONS": "[{\"name\":\"ndots\",\"value\":\"2\"}]" } } } ``` # Agent Settings Source: https://docs.controlplane.com/byok/settings/agent Configure BYOK agent settings via the Control Plane API or the cpln-byok-current configmap for self-managed Kubernetes clusters. If this CPLN Platform Location is created from the Managed kubernetes CPLN PLatform Add-on then all these settings can be adjusted in the Control Plane api. See [CPLN Platform Add-on](/mk8s/add-ons/byok) for information about where to adjust the settings. If this Kubernetes cluster is not managed by Control Plane, then the configmap `cpln-byok-current` in the `kube-system` namespace can be used to adjust these options. You can read the default values in the configmap by running this kubectl command against the cluster: `kubectl get configmap -n kube-system cpln-byok-default -o yaml` ## Usage To modify the configuration, update the configmap `cpln-byok-current` in the `kube-system` namespace. Its recommended to store settings in a git repo or CI system so that they can be applied automatically for each new cluster. ## Example cpln-byok-default configmap ```json theme={null} { "actuator": { "minCpu": "50m", "maxCpu": "1001m", "minMemory": "200Mi", "maxMemory": "500Mi", "logLevel": "info", "env": { "CACHE_PERIOD_DATA_SERVICE": "600", "LABEL_NODES": "false" } }, "common": { "pdb": { "minAvailable": 0 } }, "internalDns": { "minCpu": "1m", "maxCpu": "1001m", "minMemory": "100Mi", "maxMemory": "512Mi" }, "istio": { "istiod": { "replicas": 1, "maxCpu": "1001m", "minMemory": "100Mi", "maxMemory": "2500Mi", "pdb": 0 } }, "logSplitter": { "minCpu": "1m", "maxCpu": "200m", "minMemory": "10Mi", "maxMemory": "256Mi", "memBufferSize": "128M", "perPodRate": 10000 }, "monitoring": { "minMemory": "100Mi" }, "redis": { "minCpu": "10m", "maxCpu": "2001m", "minMemory": "100Mi", "maxMemory": "1000Mi", "storage": "8Gi" }, "middlebox": { "enabled": true } } ``` ## General Configurations Common configuration settings that may be available for each component, check the `cpln-byok-default` configmap for availability. | Property | Type | Description | | :--------------- | :------------- | :------------------------------------------------------------- | | minCpu | string | Minimum CPU allocation | | maxCpu | string | Maximum CPU allocation | | minMemory | string | Minimum memory allocation | | maxMemory | string | Maximum memory allocation | | logLevel | string | Logging level for the actuator | | env | key: string\[] | Optional environment variables for the component | | targetPercent | number | Target percentage for Horizontal CPU autoscaling | | pdb.minAvailable | number | Minimum number of available pods for the pod disruption budget | | replicas | number | Number of replicas | ## Additional Options ### Actuator Actuator is responsible for syncing the configuration between the Control Plane API and the cluster. It has several environment variables that will control its behavior. [Actuator Settings](/byok/settings/actuator) ### Log Splitter Configuration for component that forwards logs for workloads to Control Plane. | Property | Type | Description | | :------------ | :----- | :---------------------------------------------------- | | memBufferSize | string | Memory buffer size | | perPodRate | number | Max log records per second before rate limit; per pod | ### Redis Configuration for a Redis instance that is used by Control Plane. | Property | Type | Description | | :------- | :----- | :------------------------------------------------------------------------------------------- | | storage | string | Storage allocation for Redis in kubernetes storage capacity format. examples: `1Gi`, `500Mi` | ### Middlebox Configuration for Middlebox, required for the [Cloud Wormhole agent](/reference/agent) to work with BYOK location. | Property | Type | Description | | :-------------- | :------ | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | enabled | boolean | Required. `true` or `false` | | ip | string | Optional. For cloud platform that do not support UDP type Load Balancers | | port | number | Optional. To change the default (51820) port. | | ingressReplicas | number | Optional. Required for the agent [reverse HTTP proxy](/reference/agent#bi-directional-functionality). Set greater than `0` to enable inbound access from allowed agents. Defaults to `0`. | # GVC Settings Source: https://docs.controlplane.com/byok/settings/gvc When using a dedicated load balancer on a GVC for a BYOK location, you can add additional GVC tags to provide passthrough annotations on the service that is created. ### Passthrough Annotations You can add tags to the GVC to pass extra annotations through to the Kubernetes service created for the dedicated load balancer: | Tag Key | Tag Value | | ------------------------------ | --------- | | `cpln/passthruAnnotation:$key` | `$value` | The annotation applied to the Kubernetes service will be: ``` $key = $value ``` These annotations are merged after the actuator configured `INGRESS_ANNOTATIONS` and `INGRESS_EXTRA_ANNOTATIONS`. # Volume Set Settings Source: https://docs.controlplane.com/byok/settings/volumeset Configure volume set storage class suffixes to use custom CSI-compatible storage solutions in BYOK environments. When using a volume set in a BYOK environment, there are some additional configuration settings available. ## Storage Class Suffix You can specify a `spec.storageClassSuffix` in a volume set. When this property is set, Control Plane will look for a storage class including this suffix. Storage classes are constructed by combining `spec.performanceClass`, `spec.fileSystemType`, and (optionally) the `spec.storageClassSuffix`. If a storage class including the suffix cannot be found, Control Plane falls back to the storage class excluding the suffix. This allows BYOK users to utilize any CSI-compatible storage solution they wish. # Workload Settings Source: https://docs.controlplane.com/byok/settings/workload BYOK-specific workload settings including internal firewall configuration for private address ranges and extra Kubernetes options. When running workloads on a BYOK Location, there are some additional configuration settings that are available. ## Internal Firewall Settings When a workload is running on the Control Plane cloud platform IP Addresses and CIDR blocks configured for the external firewall of a workload are ignored if they are in any private address ranges. When running in a BYOK location these internal address ranges are allowed and can be used to directly access internal resources in your data center. ## extra k8s options Some specific k8s options can be configured on workloads. ```yaml YAML theme={null} spec: extras: affinity: ... tolerations: ... topologySpreadConstraints: ... ``` * Right now, only `affinity`, `tolerations`, and `topologySpreadConstraints` are supported. * These options will be **merged** with the default ones. ## Custom Tags An extra list of Control Plane tags can be used to change the behavior of workloads when they are run in BYOK Locations. ### Disable Service Mesh `cpln/disableServiceMesh`=`true` * When this tag is added to a workload the inbound and outbound sidecar is completely disabled. this can be useful in situations where proxying can confuse clients that are expecting direct IP communication without any NAT. ### Disable Service Mesh Inbound Port `cpln/disableServiceMeshInboundPort` * A comma delimited list of ports to exclude from being intercepted by the sidecar proxy inbound. ### Disable Service Mesh Outbound Port `cpln/disableServiceMeshOutboundPort` * A comma delimited list of ports to exclude from being intercepted by the sidecar proxy outbound. ### Disable Service Mesh Outbound CIDR `cpln/disableServiceMeshOutboundCIDR` * A comma delimited list of CIDR IP ranges to exclude from being intercepted by the sidecar proxy outbound. ### ClusterRole `cpln/k8sClusterRole` * The ClusterRole that should be bound to the workload The actuator must be configured to allow this. See [BYOK Actuator settings](/byok/settings/actuator#byok-k8s-api-workloads) # CI/CD Usage Source: https://docs.controlplane.com/cli-reference/ci-cd-development/ci-cd Automate deployments and resource management with the Control Plane CLI in CI/CD pipelines. Use the Control Plane CLI in CI/CD pipelines to automate resource provisioning, image builds, and deployments. ## Overview The CLI enables CI/CD workflows for: * **Resource provisioning** - Apply GVC, workload, and infrastructure configurations * **Image delivery** - Build and push container images to Control Plane * **GitOps** - Maintain infrastructure as code in version control ## Quick start Create a service account with appropriate permissions for your pipeline. See [Create a Service Account](#service-account-setup) for details. Generate a key and store these environment variables in your CI/CD platform: * `CPLN_TOKEN` - The service account key (required) * `CPLN_ORG` - Your organization name (recommended) * `CPLN_GVC` - Your target GVC name (recommended) Setting `CPLN_ORG` and `CPLN_GVC` as environment variables means you don't need to pass `--org` and `--gvc` flags with every command. Never print `CPLN_TOKEN` in logs or commit it to version control. Install a pinned version of the CLI in your pipeline: ```bash theme={null} npm install -g @controlplane/cli@ cpln --version ``` Download and extract the binary for your platform. See [Installation](/cli-reference/installation). Pin the CLI version for reproducible builds. Create a default profile in your pipeline: ```bash theme={null} cpln profile create ci --default ``` When `CPLN_TOKEN` is set as an environment variable, the CLI uses it automatically. Execute CLI commands to build images and apply resources: ```bash theme={null} # Build and push image (optional) cpln image build --name : --push # Apply resources cpln apply --file resources.yaml ``` ## Service account setup ### Create the service account 1. Navigate to **Org** → **Service Accounts** 2. Click **Create** 3. Enter a name (e.g., `ci-pipeline`) 4. Assign to a group with appropriate permissions (e.g., `superusers` or a custom group) ```bash theme={null} cpln serviceaccount create --name ci-pipeline --description "CI/CD automation" ``` Add to a group: ```bash theme={null} cpln group add-member --serviceaccount ci-pipeline ``` ### Generate a key Create an authentication key for the service account: 1. Open the service account you created 2. Click the **Keys** link 3. Enter a key description (e.g., `ci-pipeline-key`) and click **Add** 4. Copy and download the generated key securely ```bash theme={null} cpln serviceaccount add-key ci-pipeline --description ci-pipeline-key ``` Save the returned key securely. Store the key in your CI/CD platform's secret manager as `CPLN_TOKEN`. ## Environment variables Set these variables in your CI/CD platform: ### Required * `CPLN_TOKEN` - Service account key (keep secret) ### Optional but recommended * `CPLN_ORG` - Default organization * `CPLN_GVC` - Default GVC ### Optional * `CPLN_SKIP_UPDATE_CHECK` - Any non-empty value opts out of the CLI's daily new-version check and the notice it prints ```bash GitHub Actions theme={null} env: CPLN_TOKEN: ${{ secrets.CPLN_TOKEN }} CPLN_ORG: my-org CPLN_GVC: production ``` ```bash GitLab CI theme={null} variables: CPLN_ORG: my-org CPLN_GVC: production ``` ```bash CircleCI theme={null} environment: CPLN_ORG: my-org CPLN_GVC: production ``` ## Common pipeline workflows ### Build and push images ```bash theme={null} # Build and push cpln image build --name : --push ``` `cpln image build --push` runs the build locally through Docker — a Dockerfile build shells out to `docker buildx build`, and a buildpacks build runs the `pack` CLI — so the runner needs a working Docker daemon, and `--push` requires `docker-credential-cpln` on PATH (installed alongside the CLI). On runners without a daemon (for example, GitLab runners without privileged Docker-in-Docker), build with a daemonless tool such as kaniko or buildah and push directly to the org registry: the registry is `.registry.cpln.io`, the username is the literal string ``, and the password is the service account key. Building remotely is also available: `cpln image build --name : --remote` uploads the build folder, and the image is built and pushed to the org registry with only `CPLN_TOKEN` and `CPLN_ORG` (or `--org`) — no daemon and no `docker-credential-cpln`, though the local build options (`--dockerfile`, `--builder`, `--buildpack`, `--env`, `--platform`) do not apply. With `--repo `, the remote build clones a GitHub or GitLab repository instead of uploading the folder. For a private repository, run that command once from your own terminal: the CLI opens the provider's authorization page and continues the build once you grant the org access to the repository. The connection is bound to the org, so from then on pipelines build that repository without any further prompting. A pipeline cannot do this first run itself — with no terminal attached, the CLI prints the connect link and stops. ### Apply resources (GitOps) ```bash theme={null} # Apply a single file cpln apply --file gvc.yaml # Apply multiple files cpln apply --file workload.yaml --file secret.yaml # Apply from stdin cat resource.yaml | cpln apply --file - ``` A single `cpln apply` invocation orders resources automatically — agent, secret, cloudaccount, gvc, identity, volumeset, policy, workload, then all remaining kinds — so a workload and the GVC it references can live in the same file (or directory) in any order. Ordering only matters across separate `cpln apply` invocations: if you split resources into multiple pipeline steps, apply the referenced resource (e.g., the GVC) before the resource that references it. Separate multiple resources with `---` in a single file: ```yaml theme={null} kind: gvc name: my-gvc --- kind: workload name: my-app ``` Apply with: ```bash theme={null} cpln apply --file resources.yaml ``` Renaming a resource in YAML creates a new resource. The old resource must be deleted manually: ```bash theme={null} cpln delete ``` ### Export existing resources Export "known good" configurations as templates for GitOps: Use the **Export** action to download JSON or YAML. ```bash bash theme={null} cpln gvc get --output yaml-slim > gvc.yaml cpln workload get --gvc --output yaml-slim > workload.yaml cpln secret get --output yaml-slim > secret.yaml ``` Use `json-slim` or `yaml-slim` to remove non-essential properties like IDs, versions, and timestamps. This gives you only what's necessary for applying the resource with `cpln apply`. ## Platform-specific examples Complete working examples for popular CI/CD platforms: Full workflow with build, push, and apply Pipeline configuration for GitLab Bitbucket pipeline with Docker build CircleCI config with multi-stage workflow Cloud Build with Secret Manager ## Best practices Specify an exact CLI version for reproducible builds: ```bash theme={null} npm install -g @controlplane/cli@x.x.x ``` Update the version explicitly when you want to adopt new features. Create separate service accounts for different environments: * `ci-dev` for development * `ci-staging` for staging * `ci-production` for production Assign each to groups with scoped permissions. * Use your CI/CD platform's secret manager * Never commit tokens to version control * Rotate service account keys regularly * Avoid printing `CPLN_TOKEN` in logs Apply changes to development or staging environments before production: ```bash theme={null} # Staging cpln apply --file resources.yaml --org staging # Production (after staging succeeds) cpln apply --file resources.yaml --org production ``` Export resources with `yaml-slim` or `json-slim` to remove metadata: ```bash theme={null} cpln gvc get my-gvc --output yaml-slim > gvc.yaml ``` This creates clean templates suitable for version control and `cpln apply`. ## Troubleshooting 1. Verify `CPLN_TOKEN` is set correctly in your CI/CD secrets 2. Check the token hasn't expired or been revoked 3. Ensure the service account has the required permissions Verify that the `CPLN_ORG` and `CPLN_GVC` environment variables are set correctly. Or explicitly set org and GVC in commands: ```bash theme={null} cpln apply --file workload.yaml --org my-org --gvc my-gvc ``` Or set in the profile: ```bash theme={null} cpln profile update ci --org my-org --gvc my-gvc ``` For more troubleshooting help, see the [Troubleshooting](/cli-reference/using-cli/troubleshooting) page. ## Next steps Use the CLI inside container images Learn about profile management GitOps resource management guide Create and manage service accounts # Using the CLI in Containers Source: https://docs.controlplane.com/cli-reference/ci-cd-development/container-image Install and run the Control Plane CLI inside Docker images for CI/CD and automation. Install the Control Plane CLI inside container images to use it in CI jobs, Cron Workloads, or custom automation containers. ## When to use this Install `cpln` in a container image for: * **CI/CD jobs** - Run `cpln` commands in containerized pipelines * **Cron Workloads** - Execute automation tasks in Control Plane [cron workloads](/reference/workload/types#cron) * **Custom automation images** - Bundle `cpln` with your tools and scripts Never bake credentials into container images. Pass tokens at runtime via environment variables or secret managers. ## Installation methods Use this method if your base image already includes Node.js (16 or later). ```dockerfile Dockerfile theme={null} FROM node:20-slim # Pin the CLI version for reproducible builds ARG CPLN_CLI_VERSION=3.7.5 # Install the Control Plane CLI RUN npm install -g @controlplane/cli@${CPLN_CLI_VERSION} && \ cpln --version WORKDIR /app # Keep container running for testing (remove in production) CMD ["tail", "-f", "/dev/null"] ``` **Build and run:** ```bash bash theme={null} # Build the image docker build -t cpln-test . # Run in a container docker run -d --name cpln-test \ -e CPLN_TOKEN="$CPLN_TOKEN" \ -e CPLN_ORG="my-org" \ -e CPLN_GVC="my-gvc" \ cpln-test ``` **Exec into the container:** ```bash bash theme={null} docker exec -it cpln-test bash ``` **Test the CLI:** ```bash bash theme={null} # List all workloads cpln workload get ``` **Cleanup:** ```bash bash theme={null} docker stop cpln-test && docker rm cpln-test ``` Use this method for minimal images without Node.js. ```dockerfile Dockerfile theme={null} # Stage 1: Download and extract the CLI FROM debian:bookworm-slim AS cpln-downloader # Pin the CLI version for reproducible builds ARG CPLN_BINARY_URL=https://storage.googleapis.com/artifacts.cpln-build.appspot.com/binaries/cpln/2754171261-e11832d5/cpln-linux.tgz # Download and extract cpln binary RUN apt-get update && apt-get install -y --no-install-recommends curl ca-certificates && \ curl -fsSL "${CPLN_BINARY_URL}" -o /tmp/cpln.tgz && \ tar -xzf /tmp/cpln.tgz -C /usr/local/bin && \ rm -rf /var/lib/apt/lists/* # Stage 2: Runtime image FROM debian:bookworm-slim # Copy binaries from downloader stage COPY --from=cpln-downloader /usr/local/bin/cpln /usr/local/bin/cpln COPY --from=cpln-downloader /usr/local/bin/docker-credential-cpln /usr/local/bin/docker-credential-cpln # Verify installation RUN cpln --version WORKDIR /work # Keep container running for testing (remove in production) CMD ["tail", "-f", "/dev/null"] ``` **Build and run:** ```bash bash theme={null} # Build the image (use --platform for Apple Silicon Macs) docker build --platform linux/amd64 -t cpln-test . # Run in a container docker run -d --name cpln-test \ -e CPLN_TOKEN="$CPLN_TOKEN" \ -e CPLN_ORG="my-org" \ -e CPLN_GVC="my-gvc" \ cpln-test ``` The `--platform linux/amd64` flag is required on Apple Silicon Macs because the CLI binary is compiled for x86\_64. On Intel Macs and Linux, you can omit this flag. **Exec into the container:** ```bash bash theme={null} docker exec -it cpln-test bash ``` **Test the CLI:** ```bash bash theme={null} # List all workloads cpln workload get ``` **Cleanup:** ```bash bash theme={null} docker stop cpln-test && docker rm cpln-test ``` Multi-stage builds keep the final image small by excluding download tools. ## Pin the CLI version Always pin the CLI version for reproducible builds: ```dockerfile npm theme={null} ARG CPLN_CLI_VERSION=3.7.5 RUN npm install -g @controlplane/cli@${CPLN_CLI_VERSION} ``` ```dockerfile Binary theme={null} ARG CPLN_BINARY_URL=https://storage.googleapis.com/artifacts.cpln-build.appspot.com/binaries/cpln/2754171261-e11832d5/cpln-linux.tgz ``` Check available versions: * npm: [https://www.npmjs.com/package/@controlplane/cli](https://www.npmjs.com/package/@controlplane/cli) * Binary: See [Installation](/cli-reference/installation) ## Runtime authentication ### Pass secrets via environment variables Set at minimum `CPLN_TOKEN` when running the container: ```bash theme={null} docker run --rm \ -e CPLN_TOKEN="$CPLN_TOKEN" \ -e CPLN_ORG="my-org" \ -e CPLN_GVC="my-gvc" \ your-image:tag \ cpln workload get ``` Never print `CPLN_TOKEN` in logs or commit it to version control. ## Using cpln in automation Once installed, execute CLI commands from your application code: ```javascript theme={null} import { spawn } from "node:child_process"; const proc = spawn("cpln", ["workload", "get", "--gvc", "my-gvc"], { stdio: "inherit", env: process.env, }); proc.on("exit", (code) => { process.exit(code ?? 1); }); ``` ```python theme={null} import os import subprocess import sys result = subprocess.run( ["cpln", "workload", "get", "--gvc", "my-gvc"], env=os.environ, check=False, ) sys.exit(result.returncode) ``` ```go theme={null} package main import ( "os" "os/exec" ) func main() { cmd := exec.Command("cpln", "workload", "get", "--gvc", "my-gvc") cmd.Env = os.Environ() cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { os.Exit(1) } } ``` ```bash theme={null} #!/usr/bin/env bash set -euo pipefail # Ensure required environment variables are set for var in CPLN_TOKEN CPLN_ORG CPLN_GVC; do if [ -z "${!var:-}" ]; then echo "Error: $var is not set" exit 1 fi done # Create profile cpln profile create ci --default # Run commands cpln workload get ``` ## Common workflows ### Build and push images ```bash theme={null} cpln image build --name : --push ``` This runs the build through a Docker daemon inside the container. For containers without one, see [CI/CD Usage](/cli-reference/ci-cd-development/ci-cd#build-and-push-images). ### Apply resources ```bash theme={null} # Apply from a file cpln apply --file resources.yaml # Apply from stdin cat resources.yaml | cpln apply --file - ``` ### Execute workload commands ```bash theme={null} cpln workload get --gvc my-gvc cpln workload exec my-app --gvc my-gvc -- echo hello world ``` ## Troubleshooting Verify the binary is in PATH and executable: ```dockerfile theme={null} RUN cpln --version ``` Ensure the binary is copied to a directory in PATH (e.g., `/usr/local/bin`). 1. Verify `CPLN_TOKEN` is set at runtime: ```bash theme={null} docker run --rm -e CPLN_TOKEN="$CPLN_TOKEN" your-image cpln profile get ``` 2. Check the token isn't truncated Re-run Docker login in your container: ```bash theme={null} cpln image docker-login ``` Ensure the service account has access to the image registry. For more troubleshooting help, see the [Troubleshooting](/cli-reference/using-cli/troubleshooting) page. ## Best practices Keep final images small by using multi-stage builds to separate download/build tools from the runtime image. Always specify an exact CLI version for reproducible builds: ```dockerfile npm theme={null} ARG CPLN_CLI_VERSION=3.7.5 RUN npm install -g @controlplane/cli@${CPLN_CLI_VERSION} ``` ```dockerfile Binary theme={null} ARG CPLN_BINARY_URL=https://storage.googleapis.com/artifacts.cpln-build.appspot.com/binaries/cpln/2754171261-e11832d5/cpln-linux.tgz RUN curl -fsSL "${CPLN_BINARY_URL}" -o /tmp/cpln.tgz && \ tar -xzf /tmp/cpln.tgz -C /usr/local/bin ``` Pass tokens at runtime via environment variables or secret managers. Never include them in the image. Set `CPLN_ORG` and `CPLN_GVC` as environment variables so you don't need to pass `--org` and `--gvc` flags with every command: ```bash theme={null} docker run --rm \ -e CPLN_TOKEN="$CPLN_TOKEN" \ -e CPLN_ORG="my-org" \ -e CPLN_GVC="my-gvc" \ your-image:tag ``` Add a verification step: ```dockerfile theme={null} RUN cpln --version ``` This catches installation issues during the build. ## Next steps Complete CI/CD automation guide CLI installation methods Authentication strategies Profile management # account Source: https://docs.controlplane.com/cli-reference/commands/account cpln account — Manage accounts linked to your organization. Manage accounts ```none theme={null} cpln account [SUBCOMMAND] ``` ```text theme={null} Commands: cpln account get [ref...] Get accounts by id or list accounts Options: --help Show help [boolean] ``` *** #### account get Get accounts by id or list accounts **Usage** ```none theme={null} cpln account get [ref...] [OPTIONS] ``` An empty reference will return all references ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [default: []] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** # agent Source: https://docs.controlplane.com/cli-reference/commands/agent cpln agent — Manage agents that connect self-hosted infrastructure to Control Plane. Manage agents For a detailed overview, please visit the [agent's](/reference/agent) reference page. ```none theme={null} cpln agent [SUBCOMMAND] ``` ```text theme={null} Commands: cpln agent access-report Show the access report for the referenced agent cpln agent audit [ref] Retrieve audit trail events for the referenced agent cpln agent create Create a new agent cpln agent delete Delete one or more referenced agents cpln agent edit Edit the referenced agent, as YAML, within an editor cpln agent eventlog Show the event log of the referenced agent [aliases: log] cpln agent get [ref...] Retrieve one or more referenced agents cpln agent info Get info about an agent cpln agent manifest Generate a manifest for running an agent in K8S cpln agent patch Update the referenced agent's metadata using an input file cpln agent permissions Show the grantable permissions for an agent object type cpln agent query Find all the agents based on the given query cpln agent tag Manage the tags belonging to one or more referenced agents cpln agent up Run an agent within a local Docker instance cpln agent update Update properties of the referenced agent Options: --help Show help [boolean] ``` *** #### agent access-report Show the access report for the referenced agent **Usage** ```none theme={null} cpln agent access-report [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln agent access-report demo-agent --org demo-cpln-organization ``` ```text Output theme={null} +-----------+--------------------------------------------------------+--------+ |PERMISSION |PRINCIPAL |GRANTED | |-----------|--------------------------------------------------------|--------| |create |/org/demo-cpln-organization/group/superusers |manage | |create |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |delete |/org/demo-cpln-organization/group/superusers |manage | |delete |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |edit |/org/demo-cpln-organization/group/superusers |manage | |edit |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |manage |/org/demo-cpln-organization/group/superusers |manage | |manage |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |use |/org/demo-cpln-organization/group/superusers |manage | |use |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |view |/org/demo-cpln-organization/group/viewers |view | |view |/org/demo-cpln-organization/group/superusers |manage | |view |/org/demo-cpln-organization/serviceaccount/controlplane |manage | +-----------+--------------------------------------------------------+--------+ ``` *** #### agent audit Retrieve audit trail events for the referenced agent **Usage** ```none theme={null} cpln agent audit [ref] [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. Command options: --subject Filter by subject (serviceaccount name or user email) [string] --context Audit context name (the 'cpln' context displays Control Plane actions) [string] [default: "cpln"] --since Relative lookback window from now (e.g., 1h, 24h, 7d). Mutually exclusive with --from/--to [string] [default: "7d"] --from Start time for audit events (ISO 8601 format or relative duration, e.g., 2025-10-23T07:00:00Z, 7d, 1h). Cannot be used with --since [string] --to End time for audit events (ISO 8601 format or relative duration, e.g., 2025-10-23T07:00:00Z, 1d, 30m) [string] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### agent create Create a new agent **Usage** ```none theme={null} cpln agent create --name AGENT_NAME [OPTIONS] ``` ```text theme={null} Command options: --name Name of the agent [required] --description, --desc Optional description, defaults to the name if not set --tag Attach tags (e.g., --tag drink=water) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln agent create --name demo-agent --desc demo-agent --tag drink=water --org demo-cpln-organization ``` ```text Output theme={null} { "agentId": "83672911-3eba-4572-9942-310ac2701c72", "agentLink": "/org/demo-cpln-organization/agent/demo-agent", "hubEndpoint": "https://hub.test.cpln.io", "registrationToken": "a2HV7eZdEGyhuguGTTXCnIkpuL4lO5b0Rw99ZqGkQc61I6m0PuhquCQA/DDGJYH", "protocolVersion": "v2" } ``` *** #### agent delete Delete one or more referenced agents **Usage** ```none theme={null} cpln agent delete [OPTIONS] ``` ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [required] [default: []] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln agent delete demo-agent --org demo-cpln-organization ``` ```text Output theme={null} If the delete was successful, a new command prompt will be shown. ``` *** #### agent edit Edit the referenced agent, as YAML, within an editor **Usage** ```none theme={null} cpln agent edit [OPTIONS] ``` The default editor can be set by using the EDITOR environment variable. ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --replace, -r Replace instead of patch/merge [boolean] [default: false] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln agent edit demo-agent --org demo-cpln-organization ``` ```text Output theme={null} Your default editor will be launched. Once any modifications are saved, the updates will be sent to the API. ``` *** #### agent eventlog Show the event log of the referenced agent **Usage** ```none theme={null} cpln agent eventlog [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln agent eventlog demo-agent --org demo-cpln-organization ``` ```text Output theme={null} The event log for the referenced agent will be shown. ``` *** #### agent get Retrieve one or more referenced agents **Usage** ```none theme={null} cpln agent get [ref...] [OPTIONS] ``` An empty reference will return all references ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [default: []] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Get all agents ```bash Command theme={null} cpln agent get --org demo-cpln-organization ``` ```text Output theme={null} +-----------------+-----------------+--------+--------------+ |NAME |DESCRIPTION |CREATED |TAGS | |-----------------|-----------------|--------|--------------| |demo-agent |demo-agent |Now |drink="water" | |demo-agent-get-1 |demo-agent-get-1 |Now | | |demo-agent-get-2 |demo-agent-get-2 |Now | | +-----------------+-----------------+--------+--------------+ ``` * Get one agent ```bash Command theme={null} cpln agent get demo-agent-get-1 --org demo-cpln-organization ``` ```text Output theme={null} +-----------------+-----------------+--------+-----+ |NAME |DESCRIPTION |CREATED |TAGS | |-----------------|-----------------|--------|-----| |demo-agent-get-1 |demo-agent-get-1 |Now | | +-----------------+-----------------+--------+-----+ ``` * Get multiple agents ```bash Command theme={null} cpln agent get demo-agent-get-1 demo-agent-get-2 --org demo-cpln-organization ``` ```text Output theme={null} +-----------------+-----------------+--------+-----+ |NAME |DESCRIPTION |CREATED |TAGS | |-----------------|-----------------|--------|-----| |demo-agent-get-1 |demo-agent-get-1 |Now | | |demo-agent-get-2 |demo-agent-get-2 |Now | | +-----------------+-----------------+--------+-----+ ``` *** #### agent info Get info about an agent **Usage** ```none theme={null} cpln agent info [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Return the details of a deployed agent. The example below was for an agent deployed at AWS. ```bash Command theme={null} cpln agent info demo-agent --org demo-cpln-organization ``` ```json Output theme={null} { "env": { "cpuPlatform": "x86_64", "image": "ami-0fff5e2863a768052", "id": "i-01adfe256a82a2a76", "machineType": "t2.micro", "zone": "us-west-2c", "internalIP": "172.31.13.246", "hostname": "ip-172-31-13-246.us-west-2.compute.internal", "vpc": "vpc-29e3c451", "subnet": "subnet-b11e9dec", "accountId": "015716931765", "cloudProvider": "aws" }, "lastActive": "2021-05-01T00:26:55.945Z", "serviceCount": 0, "peerCount": 0 } ``` *** #### agent manifest Generate a manifest for running an agent in K8S **Usage** ```none theme={null} cpln agent manifest --bootstrap-file FILE_NAME --namespace NAME_SPACE [OPTIONS] ``` ```text theme={null} Command options: --bootstrap-file Path to the bootstrap config file [required] --namespace, -n Namespace where the agent deployment(s) will live [required] --image Advanced use: Use a different agent Docker image --cluster Add metadata to agent's environment, useful to remind you which cluster an agent is running in --replicas Number of agent deployments to create [default: 1] --create-namespace Create the namespace [boolean] [default: true] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### agent patch Update the referenced agent's metadata using an input file **Usage** ```none theme={null} cpln agent patch --file FILE_NAME [OPTIONS] ``` Running this command with the option **`--file -`** sets the input to stdin. The input is required to be a valid JSON or YAML string (e.g., `{"description": "My new description"}` or `description: My new description`). To complete the update, press Control-D (or Control-Z if you are using Windows). ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --file, -f File to load the patch from. Use `--file -` to enable input from stdin. [required] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln agent patch demo-agent --file update.json --org demo-cpln-organization ``` ```text Output theme={null} The changes within the JSON file will be performed against the referenced agent. ``` *** #### agent permissions Show the grantable permissions for an agent object type **Usage** ```none theme={null} cpln agent permissions [OPTIONS] ``` ```text theme={null} Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln agent permissions --org demo-cpln-organization ``` ```text Output theme={null} +-------+----------------------------+----------------------------------------+ |NAME |DESCRIPTION |IMPLIES | |-------|----------------------------|----------------------------------------| |create |Create new agents | | |delete |Delete service agents | | |edit |Modify existing agents |view | |manage |Full access |create, delete, edit, manage, use, view | |use |Use an agent in an identity |view | |view |Read-only access | | +-------+----------------------------+----------------------------------------+ ``` *** #### agent query Find all the agents based on the given query **Usage** ```none theme={null} cpln agent query QUERY_OPTIONS [OPTIONS] ``` ```text theme={null} Query options: --match Query filter type [choices: "all", "none", "any"] [default: "all"] --property, --prop Property to query --tag Tag to query --rel Relation to query Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) Use the `--match` option to display **all**, **none**, or **any** of the results based on a given property or tag * Search for an agent based on the name property ```bash Command theme={null} cpln agent query --match any --prop name=demo-agent --org demo-cpln-organization ``` ```text Output theme={null} +-----------+------------+--------+--------------+ |NAME |DESCRIPTION |CREATED |TAGS | |-----------|------------|--------|--------------| |demo-agent |demo-agent |Now |drink="water" | +-----------+------------+--------+--------------+ ``` * Search for an agent based on the description property ```bash Command theme={null} cpln agent query --match any --prop description="demo-agent" --org demo-cpln-organization ``` ```text Output theme={null} +-----------+------------+--------+--------------+ |NAME |DESCRIPTION |CREATED |TAGS | |-----------|------------|--------|--------------| |demo-agent |demo-agent |Now |drink="water" | +-----------+------------+--------+--------------+ ``` * Search for an agent based on a tag ```bash Command theme={null} cpln agent query --match any --tag drink=water --org demo-cpln-organization ``` ```text Output theme={null} +-----------+------------+--------+--------------+ |NAME |DESCRIPTION |CREATED |TAGS | |-----------|------------|--------|--------------| |demo-agent |demo-agent |Now |drink="water" | +-----------+------------+--------+--------------+ ``` * Search for an agent based on a property and tag ```bash Command theme={null} cpln agent query --match any --prop description="demo-agent" --tag drink=water --org demo-cpln-organization ``` ```text Output theme={null} +-----------+------------+--------+--------------+ |NAME |DESCRIPTION |CREATED |TAGS | |-----------|------------|--------|--------------| |demo-agent |demo-agent |Now |drink="water" | +-----------+------------+--------+--------------+ ``` *** #### agent tag Manage the tags belonging to one or more referenced agents **Usage** ```none theme={null} cpln agent tag --tag KEY=VALUE [OPTIONS] ``` Built-in tags, which begin with cpln/\*, can't be modified. ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [required] [default: []] Command options: --tag Attach tags (e.g., --tag drink=water) --remove-tag Remove tags (e.g., --remove-tag tagname) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Add a tag ```bash Command theme={null} cpln agent tag demo-agent --tag drink=water --org demo-cpln-organization ``` ```text Output theme={null} +-----------+------------+--------+--------------+ |NAME |DESCRIPTION |CREATED |TAGS | |-----------|------------|--------|--------------| |demo-agent |demo-agent |Now |drink="water" | +-----------+------------+--------+--------------+ ``` * Update same tag (after running the example above) ```bash Command theme={null} cpln agent tag demo-agent --tag drink=coffee --org demo-cpln-organization ``` ```text Output theme={null} +-----------+------------+--------+---------------+ |NAME |DESCRIPTION |CREATED |TAGS | |-----------|------------|--------|---------------| |demo-agent |demo-agent |Now |drink="coffee" | +-----------+------------+--------+---------------+ ``` * Remove tag (after running the example above) ```bash Command theme={null} cpln agent tag demo-agent --remove drink --org demo-cpln-organization ``` ```text Output theme={null} +-----------+------------+--------+-----+ |NAME |DESCRIPTION |CREATED |TAGS | |-----------|------------|--------|-----| |demo-agent |demo-agent |Now | | +-----------+------------+--------+-----+ ``` *** #### agent up Run an agent within a local Docker instance **Usage** ```none theme={null} cpln agent up --bootstrap-file FILE_NAME [--background] [OPTIONS] ``` ```text theme={null} Command options: --bootstrap-file Path to the bootstrap config file [required] --background, -b If set, run the agent as a background process [boolean] --image Advanced use: Use a different agent Docker image --net Docker network to use [default: "bridge"] --exposeProxy, -p Expose port 3128 if agent is to be used as forward proxy. Has no effect on v1 agents [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln agent up --bootstrap-file bootstrap.config --org demo-cpln-organization ``` ```text Output theme={null} The Control Plane agent will be executed in a local Docker instance using the referenced bootstrap config file ``` *** #### agent update Update properties of the referenced agent **Usage** ```none theme={null} cpln agent update --set UPDATE_PROPERTY [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Update Properties: --set Update the following properties (e.g., --set description="Updated Description"): description string tags. string --unset Unset a property (same paths as --set, e.g., --unset description) Command options: --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * The `--set` option allows the modification of the following agent properties: * `description` * Description of the agent * Type: string * `tags` * Tags (key=value) associated with the agent * Type: string * For the examples below, the initial agent is configured as: ``` +------------------+------------------+--------+-----+ |NAME |DESCRIPTION |CREATED |TAGS | |------------------|------------------|--------|-----| |demo-agent-update |demo-agent-update |Now | | +------------------+------------------+--------+-----+ ``` * Update an agent's description ```bash Command theme={null} cpln agent update demo-agent-update --set description="new description" --org demo-cpln-organization ``` ```text Output theme={null} +------------------+----------------+--------+-----+ |NAME |DESCRIPTION |CREATED |TAGS | |------------------|----------------|--------|-----| |demo-agent-update |new description |Now | | +------------------+----------------+--------+-----+ ``` * Update an agent's tag ```bash Command theme={null} cpln agent update demo-agent-update --set tags.demo-key=demo-value --org demo-cpln-organization ``` ```text Output theme={null} +------------------+----------------+--------+----------------------+ |NAME |DESCRIPTION |CREATED |TAGS | |------------------|----------------|--------|----------------------| |demo-agent-update |new description |Now |demo-key="demo-value" | +------------------+----------------+--------+----------------------+ ``` *** # apply Source: https://docs.controlplane.com/cli-reference/commands/apply cpln apply — Create or update resources using an input file. Create or update a resource using an input file. Refer to the [cpln apply guide](/guides/cpln-apply) for additional details. **Usage** ```none theme={null} cpln apply [OPTIONS] ``` ```text theme={null} Command options: --file, -f File to load and use for the command. Use `--file -` to enable input from stdin. [required] --k8s Set this true if input file is k8s config file [boolean] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] --ready Set this true if apply should wait for objects to be ready before exiting [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] --version, -v Show version number [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln apply --file action.json ``` ```text Output theme={null} The actions within the JSON file will be performed against the API. ``` *** # auditctx Source: https://docs.controlplane.com/cli-reference/commands/auditctx cpln auditctx — Manage audit contexts for tracking and reviewing organizational activity. Manage audit contexts For a detailed overview, please visit the [audit context's](/reference/auditctx) reference page. ```none theme={null} cpln auditctx [SUBCOMMAND] ``` ```text theme={null} Commands: cpln auditctx access-report Show the access report for the referenced audit context cpln auditctx audit [ref] Retrieve audit trail events for the referenced audit context cpln auditctx clone Create a clone of the referenced audit context; this will only duplicate its spec. [aliases: copy] cpln auditctx create Create a new audit context cpln auditctx edit Edit the referenced audit context, as YAML, within an editor cpln auditctx eventlog Show the event log of the referenced audit context [aliases: log] cpln auditctx get [ref...] Retrieve one or more referenced audit contexts cpln auditctx patch Update the referenced audit context's metadata using an input file cpln auditctx permissions Show the grantable permissions for an audit context object type cpln auditctx query Find all the audit contexts based on the given query cpln auditctx tag Manage the tags belonging to one or more referenced audit contexts cpln auditctx update Update properties of the referenced audit context Options: --help Show help [boolean] ``` *** #### auditctx access-report Show the access report for the referenced audit context **Usage** ```none theme={null} cpln auditctx access-report [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln auditctx access-report demo-audit-context --org demo-cpln-organization ``` ```text Output theme={null} +-----------+--------------------------------------------------------+--------+ |PERMISSION |PRINCIPAL |GRANTED | |-----------|--------------------------------------------------------|--------| |create |/org/demo-cpln-organization/group/superusers |manage | |create |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |edit |/org/demo-cpln-organization/group/superusers |manage | |edit |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |manage |/org/demo-cpln-organization/group/superusers |manage | |manage |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |readAudit |/org/demo-cpln-organization/group/superusers |manage | |readAudit |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |view |/org/demo-cpln-organization/group/viewers |view | |view |/org/demo-cpln-organization/group/superusers |manage | |view |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |writeAudit |/org/demo-cpln-organization/group/superusers |manage | |writeAudit |/org/demo-cpln-organization/serviceaccount/controlplane |manage | +-----------+--------------------------------------------------------+--------+ ``` *** #### auditctx audit Retrieve audit trail events for the referenced audit context **Usage** ```none theme={null} cpln auditctx audit [ref] [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. Command options: --subject Filter by subject (serviceaccount name or user email) [string] --context Audit context name (the 'cpln' context displays Control Plane actions) [string] [default: "cpln"] --since Relative lookback window from now (e.g., 1h, 24h, 7d). Mutually exclusive with --from/--to [string] [default: "7d"] --from Start time for audit events (ISO 8601 format or relative duration, e.g., 2025-10-23T07:00:00Z, 7d, 1h). Cannot be used with --since [string] --to End time for audit events (ISO 8601 format or relative duration, e.g., 2025-10-23T07:00:00Z, 1d, 30m) [string] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### auditctx clone Create a clone of the referenced audit context; this will only duplicate its spec. **Usage** ```none theme={null} cpln auditctx clone --name CLONE_NAME [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --name Set the name for the clone [required] --description Optional description, defaults to the name if not set --tag Attach tags (e.g., --tag drink=water) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln auditctx clone demo-audit-context --name demo-audit-context-clone --org demo-cpln-organization ``` ```text Output theme={null} +-------------------------+-----------------------------------------+--------+-----+ |NAME |DESCRIPTION |ORIGIN |TAGS | |-------------------------|-----------------------------------------|--------|-----| |demo-audit-context-clone |Clone of demo-audit-context |default | | +-------------------------+-----------------------------------------+--------+-----+ ``` *** #### auditctx create Create a new audit context **Usage** ```none theme={null} cpln auditctx create --name NAME [OPTIONS] ``` ```text theme={null} Command options: --name Name of the new audit context [required] --description, --desc Optional description, defaults to the name if not set --tag Attach tags (e.g., --tag drink=water) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln auditctx create --name demo-audit-context --org demo-cpln-organization ``` ```text Output theme={null} +-------------------+-----------------------------------------+--------+-----+ |NAME |DESCRIPTION |ORIGIN |TAGS | |-------------------|-----------------------------------------|--------|-----| |demo-audit-context |demo-audit-context |default | | +-------------------+-----------------------------------------+--------+-----+ ``` *** #### auditctx edit Edit the referenced audit context, as YAML, within an editor **Usage** ```none theme={null} cpln auditctx edit [OPTIONS] ``` The default editor can be set by using the EDITOR environment variable. ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --replace, -r Replace instead of patch/merge [boolean] [default: false] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln auditctx edit demo-audit-context --org demo-cpln-organization ``` ```text Output theme={null} Your default editor will be launched. Once any modifications are saved, the updates will be sent to the API. ``` *** #### auditctx eventlog Show the event log of the referenced audit context **Usage** ```none theme={null} cpln auditctx eventlog [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln auditctx eventlog demo-audit-context --org demo-cpln-organization ``` ```text Output theme={null} The event log for the referenced audit context will be shown. ``` *** #### auditctx get Retrieve one or more referenced audit contexts **Usage** ```none theme={null} cpln auditctx get [ref...] [OPTIONS] ``` An empty reference will return all references ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [default: []] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Get all audit contexts ```bash Command theme={null} cpln auditctx get --org demo-cpln-organization ``` ```text Output theme={null} +-------------------------+----------------------------+--------+------------+ |NAME |DESCRIPTION |ORIGIN |TAGS | |-------------------------|----------------------------|--------|------------| |cpln |Audit of your CPLN org |builtin | | |demo-audit-context |demo-audit-context |default |type="demo" | |demo-audit-context-clone |Clone of demo-audit-context |default | | +-------------------------+----------------------------+--------+------------+ ``` * Get one audit context ```bash Command theme={null} cpln auditctx get demo-audit-context --org demo-cpln-organization ``` ```text Output theme={null} +-------------------+-------------------+--------+------------+ |NAME |DESCRIPTION |ORIGIN |TAGS | |-------------------|-------------------|--------|------------| |demo-audit-context |demo-audit-context |default |type="demo" | +-------------------+-------------------+--------+------------+ ``` * Get multiple audit contexts ```bash Command theme={null} cpln auditctx get demo-audit-context cpln --org demo-cpln-organization ``` ```text Output theme={null} +-------------------+-----------------------+--------+------------+ |NAME |DESCRIPTION |ORIGIN |TAGS | |-------------------|-----------------------|--------|------------| |demo-audit-context |demo-audit-context |default |type="demo" | |cpln |Audit of your CPLN org |builtin | | +-------------------+-----------------------+--------+------------+ ``` *** #### auditctx patch Update the referenced audit context's metadata using an input file **Usage** ```none theme={null} cpln auditctx patch --file FILE_NAME [OPTIONS] ``` Running this command with the option **`--file -`** sets the input to stdin. The input is required to be a valid JSON or YAML string (e.g., `{"description": "My new description"}` or `description: My new description`). To complete the update, press Control-D (or Control-Z if you are using Windows). ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --file, -f File to load the patch from. Use `--file -` to enable input from stdin. [required] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln auditctx patch demo-audit-context --file update.json --org demo-cpln-organization ``` ```text Output theme={null} The changes within the JSON file will be performed against the referenced audit context. ``` *** #### auditctx permissions Show the grantable permissions for an audit context object type **Usage** ```none theme={null} cpln auditctx permissions [OPTIONS] ``` ```text theme={null} Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln auditctx permissions --org demo-cpln-organization ``` ```text Output theme={null} +-----------+-------------------------+--------------------------------------------------+ |NAME |DESCRIPTION |IMPLIES | |-----------|-------------------------|--------------------------------------------------| |create |Create new contexts | | |edit |Modify existing contexts |view | |manage |Full access |create, edit, manage, readAudit, view, writeAudit | |readAudit |Read from this context |view | |view |Read-only view | | |writeAudit |Write to this context |view | +-----------+-------------------------+--------------------------------------------------+ ``` *** #### auditctx query Find all the audit contexts based on the given query **Usage** ```none theme={null} cpln auditctx query QUERY_OPTIONS [OPTIONS] ``` ```text theme={null} Query options: --match Query filter type [choices: "all", "none", "any"] [default: "all"] --property, --prop Property to query --tag Tag to query --rel Relation to query Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) Use the `--match` option to display **all**, **none**, or **any** of the results based on a given property or tag * Search for an audit context based on the name property ```bash Command theme={null} cpln auditctx query --match any --prop name=demo-audit-context --org demo-cpln-organization ``` ```text Output theme={null} +-------------------+-------------------+--------+------------+ |NAME |DESCRIPTION |ORIGIN |TAGS | |-------------------|-------------------|--------|------------| |demo-audit-context |demo-audit-context |default |type="demo" | +-------------------+-------------------+--------+------------+ ``` * Search for an audit context based on the description property ```bash Command theme={null} cpln auditctx query --match any --prop description="demo-audit-context" --org demo-cpln-organization ``` ```text Output theme={null} +-------------------+-------------------+--------+------------+ |NAME |DESCRIPTION |ORIGIN |TAGS | |-------------------|-------------------|--------|------------| |demo-audit-context |demo-audit-context |default |type="demo" | +-------------------+-------------------+--------+------------+ ``` * Search for an audit context based on a tag ```bash Command theme={null} cpln auditctx query --match any --tag type=demo --org demo-cpln-organization ``` ```text Output theme={null} +-------------------+-------------------+--------+------------+ |NAME |DESCRIPTION |ORIGIN |TAGS | |-------------------|-------------------|--------|------------| |demo-audit-context |demo-audit-context |default |type="demo" | +-------------------+-------------------+--------+------------+ ``` * Search for an audit context based on a property and tag ```bash Command theme={null} cpln auditctx query --match any --prop description="demo-audit-context" --tag type=demo --org demo-cpln-organization ``` ```text Output theme={null} +-------------------+-------------------+--------+------------+ |NAME |DESCRIPTION |ORIGIN |TAGS | |-------------------|-------------------|--------|------------| |demo-audit-context |demo-audit-context |default |type="demo" | +-------------------+-------------------+--------+------------+ ``` *** #### auditctx tag Manage the tags belonging to one or more referenced audit contexts **Usage** ```none theme={null} cpln auditctx tag --tag KEY=VALUE [OPTIONS] ``` Built-in tags, which begin with cpln/\*, can't be modified. ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [required] [default: []] Command options: --tag Attach tags (e.g., --tag drink=water) --remove-tag Remove tags (e.g., --remove-tag tagname) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Add a tag ```bash Command theme={null} cpln auditctx tag demo-audit-context --tag drink=water --org demo-cpln-organization ``` ```text Output theme={null} +-------------------+-------------------+--------+--------------+ |NAME |DESCRIPTION |ORIGIN |TAGS | |-------------------|-------------------|--------|--------------| |demo-audit-context |demo-audit-context |default |drink="water" | | | | |type="demo" | +-------------------+-------------------+--------+--------------+ ``` * Update same tag (after running the example above) ```bash Command theme={null} cpln auditctx tag demo-audit-context --tag drink=coffee --org demo-cpln-organization ``` ```text Output theme={null} +-------------------+-------------------+--------+---------------+ |NAME |DESCRIPTION |ORIGIN |TAGS | |-------------------|-------------------|--------|---------------| |demo-audit-context |demo-audit-context |default |drink="coffee" | | | | |type="demo" | +-------------------+-------------------+--------+---------------+ ``` * Remove tag (after running the example above) ```bash Command theme={null} cpln auditctx tag demo-audit-context --remove drink --org demo-cpln-organization ``` ```text Output theme={null} +-------------------+-------------------+--------+------------+ |NAME |DESCRIPTION |ORIGIN |TAGS | |-------------------|-------------------|--------|------------| |demo-audit-context |demo-audit-context |default |type="demo" | +-------------------+-------------------+--------+------------+ ``` *** #### auditctx update Update properties of the referenced audit context **Usage** ```none theme={null} cpln auditctx update --set UPDATE_PROPERTY [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Update Properties: --set Update the following properties (e.g., --set description="Updated Description"): description string tags. string --unset Unset a property (same paths as --set, e.g., --unset description) Command options: --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * The `--set` option allows the modification of the following audit context properties: * `description` * Description of the audit context * Type: string * `tags` * Tags (key=value) associated with the audit context * Type: string * For the examples below, the initial audit context is configured as: ```text theme={null} +-------------------+-------------------+--------+------------+ |NAME |DESCRIPTION |ORIGIN |TAGS | |-------------------|-------------------|--------|------------| |demo-audit-context |demo-audit-context |default |type="demo" | +-------------------+-------------------+--------+------------+ ``` * Update an audit context's description ```bash Command theme={null} cpln auditctx update demo-audit-context --set description="new description" --org demo-cpln-organization ``` ```text Output theme={null} +-------------------+----------------+--------+------------+ |NAME |DESCRIPTION |ORIGIN |TAGS | |-------------------|----------------|--------|------------| |demo-audit-context |new description |default |type="demo" | +-------------------+----------------+--------+------------+ ``` * Update an audit context's tag ```bash Command theme={null} cpln auditctx update demo-audit-context --set tags.type=updated --org demo-cpln-organization ``` ```text Output theme={null} +-------------------+----------------+--------+---------------+ |NAME |DESCRIPTION |ORIGIN |TAGS | |-------------------|----------------|--------|---------------| |demo-audit-context |new description |default |type="updated" | +-------------------+----------------+--------+---------------+ ``` *** # cloudaccount Source: https://docs.controlplane.com/cli-reference/commands/cloudaccount cpln cloudaccount — Manage cloud accounts that connect external cloud providers to Control Plane. Manage cloud accounts For a detailed overview, please visit the [cloud account's](/reference/cloudaccount) reference page. ```none theme={null} cpln cloudaccount [SUBCOMMAND] ``` ```text theme={null} Commands: cpln cloudaccount access-report Show the access report for the referenced cloud account cpln cloudaccount audit [ref] Retrieve audit trail events for the referenced cloud account cpln cloudaccount create-aws Create an AWS cloud account cpln cloudaccount create-azure Create an Azure cloud account cpln cloudaccount create-gcp Create a GCP cloud account cpln cloudaccount create-ngs Create a NGS cloud account cpln cloudaccount delete Delete one or more referenced cloud accounts cpln cloudaccount edit Edit the referenced cloud account, as YAML, within an editor cpln cloudaccount eventlog Show the event log of the referenced cloud account [aliases: log] cpln cloudaccount get [ref...] Retrieve one or more referenced cloud accounts cpln cloudaccount patch Update the referenced cloud account's metadata using an input file cpln cloudaccount permissions Show the grantable permissions for a cloud account object type cpln cloudaccount query Find all the cloud accounts based on the given query cpln cloudaccount tag Manage the tags belonging to one or more referenced cloud accounts Options: --help Show help [boolean] ``` *** #### cloudaccount access-report Show the access report for the referenced cloud account **Usage** ```none theme={null} cpln cloudaccount access-report [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln cloudaccount access-report demo-cloud-account-aws --org demo-cpln-organization ``` ```text Output theme={null} +-----------+--------------------------------------------------------+--------+ |PERMISSION |PRINCIPAL |GRANTED | |-----------|--------------------------------------------------------|--------| |browse |/org/demo-cpln-organization/group/superusers |manage | |browse |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |create |/org/demo-cpln-organization/group/superusers |manage | |create |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |delete |/org/demo-cpln-organization/group/superusers |manage | |delete |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |edit |/org/demo-cpln-organization/group/superusers |manage | |edit |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |manage |/org/demo-cpln-organization/group/superusers |manage | |manage |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |view |/org/demo-cpln-organization/group/viewers |view | |view |/org/demo-cpln-organization/group/superusers |manage | |view |/org/demo-cpln-organization/serviceaccount/controlplane |manage | +-----------+--------------------------------------------------------+--------+ ``` *** #### cloudaccount audit Retrieve audit trail events for the referenced cloud account **Usage** ```none theme={null} cpln cloudaccount audit [ref] [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. Command options: --subject Filter by subject (serviceaccount name or user email) [string] --context Audit context name (the 'cpln' context displays Control Plane actions) [string] [default: "cpln"] --since Relative lookback window from now (e.g., 1h, 24h, 7d). Mutually exclusive with --from/--to [string] [default: "7d"] --from Start time for audit events (ISO 8601 format or relative duration, e.g., 2025-10-23T07:00:00Z, 7d, 1h). Cannot be used with --since [string] --to End time for audit events (ISO 8601 format or relative duration, e.g., 2025-10-23T07:00:00Z, 1d, 30m) [string] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### cloudaccount create-aws Create an AWS cloud account **Usage** ```none theme={null} cpln cloudaccount create-aws --name ACCOUNT_NAME --role-arn ROLE_ARN [OPTIONS] ``` By defining an AWS cloud account, you enable workloads to consume services from a specific AWS account. The Control Plane CLI (`cpln`) provides account-specific instructions for registering an AWS cloud account with Control Plane. Simply execute `cpln cloudaccount create-aws --how` to obtain step-by-step instructions. The instructions emitted are specific to the organization for the currently logged-on cpln user. To override the organization, pass the `--org ORG_NAME` option. ```text theme={null} Command options: --name Name of the new account --description, --desc Optional description, defaults to the name if not set --role-arn ARN of the role used by Control Plane to manage the account, in format `arn:aws:iam:::role/` --how Show the steps on how to create and configure your AWS account [boolean] --tag Attach tags (e.g., --tag drink=water) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln cloudaccount create-aws --name demo-cloud-account-aws --role-arn arn:aws:iam::123412341234:role/cpln-demo-cpln-organization --org demo-cpln-organization ``` ```text Output theme={null} +-----------------------+-----------------------+---------+-----------------------------------------------------------+--------+-----+ |NAME |DESCRIPTION |PROVIDER |INFO |CREATED |TAGS | |-----------------------|-----------------------|---------|-----------------------------------------------------------|--------|-----| |demo-cloud-account-aws |demo-cloud-account-aws |aws |arn:aws:iam::123412341234:role/cpln-demo-cpln-organization |Now | | +-----------------------+-----------------------+---------+-----------------------------------------------------------+--------+-----+ ``` *** #### cloudaccount create-azure Create an Azure cloud account **Usage** ```none theme={null} cpln cloudaccount create-azure --name ACCOUNT_NAME --secret AZURE_SECRET [OPTIONS] ``` By defining an Azure cloud account, you enable workloads to consume services from a specific Azure account. The Control Plane CLI (`cpln`) provides account-specific instructions for registering an Azure cloud account with Control Plane. Simply execute `cpln cloudaccount create-azure --how` to obtain step-by-step instructions. The instructions emitted are specific to the organization for the currently logged-on cpln user. To override the organization, pass the `--org ORG_NAME` option. ```text theme={null} Command options: --name Name of the new account --description, --desc Optional description, defaults to the name if not set --secret The name of the secret which will be created. --url The URL of the deployed function app. --code The URL of the deployed function app. --how Show the steps on how to create and configure your Azure subscription [boolean] --tag Attach tags (e.g., --tag drink=water) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln cloudaccount create-azure --name demo-cloud-account-azure --secret azure-secret --url https://function.app.url --code code --org demo-cpln-organization ``` ```text Output theme={null} +-------------------------+-------------------------+---------+-----+--------+-----+ |NAME |DESCRIPTION |PROVIDER |INFO |CREATED |TAGS | |-------------------------|-------------------------|---------|-----|--------|-----| |demo-cloud-account-azure |demo-cloud-account-azure |azure | |Now | | +-------------------------+-------------------------+---------+-----+--------+-----+ ``` *** #### cloudaccount create-gcp Create a GCP cloud account **Usage** ```none theme={null} cpln cloudaccount create-gcp --name ACCOUNT_NAME --project-id PROJECT_ID [OPTIONS] ``` By defining a GCP cloud account, you enable workloads to consume services from a specific GCP account. The Control Plane CLI (`cpln`) provides account-specific instructions for registering a GCP cloud account with Control Plane. Simply execute `cpln cloudaccount create-gcp --how` to obtain step-by-step instructions. The instructions emitted are specific to the organization for the currently logged-on cpln user. To override the organization, pass the `--org ORG_NAME` option. ```text theme={null} Command options: --name Name of the new account --description, --desc Optional description, defaults to the name if not set --project-id The ID of the project that Control Plane will manage --how Show the steps on how to create and configure your GCP project [boolean] --tag Attach tags (e.g., --tag drink=water) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln cloudaccount create-gcp --name demo-cloud-account-gcp --project-id 1234abc --org demo-cpln-organization ``` ```text Output theme={null} +-----------------------+-----------------------+---------+--------+--------+-----+ |NAME |DESCRIPTION |PROVIDER |INFO |CREATED |TAGS | |-----------------------|-----------------------|---------|--------|--------|-----| |demo-cloud-account-gcp |demo-cloud-account-gcp |gcp |1234abc |Now | | +-----------------------+-----------------------+---------+--------+--------+-----+ ``` *** #### cloudaccount create-ngs Create a NGS cloud account **Usage** ```none theme={null} cpln cloudaccount create-ngs --name ACCOUNT_NAME --secret SECRET_REFERENCE [OPTIONS] ``` ```text theme={null} Command options: --name Name of the new account [required] --description, --desc Optional description, defaults to the name if not set --secret The secret used to authenticate to NGS [required] --tag Attach tags (e.g., --tag drink=water) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### cloudaccount delete Delete one or more referenced cloud accounts **Usage** ```none theme={null} cpln cloudaccount delete [OPTIONS] ``` Deleting a cloud account will not remove any configuration at the associated cloud provider ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [required] [default: []] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln cloudaccount delete demo-cloud-account-aws --org demo-cpln-organization ``` ```text Output theme={null} If the delete was successful, a new command prompt will be shown. ``` *** #### cloudaccount edit Edit the referenced cloud account, as YAML, within an editor **Usage** ```none theme={null} cpln cloudaccount edit [OPTIONS] ``` The default editor can be set by using the EDITOR environment variable. ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --replace, -r Replace instead of patch/merge [boolean] [default: false] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln cloudaccount edit demo-cloud-account-aws --org demo-cpln-organization ``` ```text Output theme={null} Your default editor will be launched. Once any modifications are saved, the updates will be sent to the API. ``` *** #### cloudaccount eventlog Show the event log of the referenced cloud account **Usage** ```none theme={null} cpln cloudaccount eventlog [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln cloudaccount eventlog demo-cloud-account-aws --org demo-cpln-organization ``` ```text Output theme={null} The event log for the referenced cloud account will be shown. ``` *** #### cloudaccount get Retrieve one or more referenced cloud accounts **Usage** ```none theme={null} cpln cloudaccount get [ref...] [OPTIONS] ``` An empty reference will return all references ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [default: []] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Get all cloud accounts ```bash Command theme={null} cpln cloudaccount get --org demo-cpln-organization ``` ```text Output theme={null} +-----------------------------+-----------------------------+---------+-----------------------------------------------------------+--------+-----+ |NAME |DESCRIPTION |PROVIDER |INFO |CREATED |TAGS | |-----------------------------|-----------------------------|---------|-----------------------------------------------------------|--------|-----| |demo-cloud-account-aws |demo-cloud-account-aws |aws |arn:aws:iam::123412341234:role/cpln-demo-cpln-organization |Now | | |demo-cloud-account-aws-get-1 |demo-cloud-account-aws-get-1 |aws |arn:aws:iam::123412341234:role/cpln-demo-cpln-organization |Now | | |demo-cloud-account-aws-get-2 |demo-cloud-account-aws-get-2 |aws |arn:aws:iam::123412341234:role/cpln-demo-cpln-organization |Now | | |demo-cloud-account-azure |demo-cloud-account-azure |azure | |Now | | |demo-cloud-account-gcp |demo-cloud-account-gcp |gcp |1234abc |Now | | +-----------------------------+-----------------------------+---------+-----------------------------------------------------------+--------+-----+ ``` * Get one cloud account ```bash Command theme={null} cpln cloudaccount get demo-cloud-account-aws-get-1 --org demo-cpln-organization ``` ```text Output theme={null} +-----------------------------+-----------------------------+---------+-----------------------------------------------------------+--------+-----+ |NAME |DESCRIPTION |PROVIDER |INFO |CREATED |TAGS | |-----------------------------|-----------------------------|---------|-----------------------------------------------------------|--------|-----| |demo-cloud-account-aws-get-1 |demo-cloud-account-aws-get-1 |aws |arn:aws:iam::123412341234:role/cpln-demo-cpln-organization |Now | | +-----------------------------+-----------------------------+---------+-----------------------------------------------------------+--------+-----+ ``` * Get multiple cloud accounts ```bash Command theme={null} cpln cloudaccount get demo-cloud-account-aws-get-1 demo-cloud-account-aws-get-2 --org demo-cpln-organization ``` ```text Output theme={null} +-----------------------------+-----------------------------+---------+-----------------------------------------------------------+--------+-----+ |NAME |DESCRIPTION |PROVIDER |INFO |CREATED |TAGS | |-----------------------------|-----------------------------|---------|-----------------------------------------------------------|--------|-----| |demo-cloud-account-aws-get-1 |demo-cloud-account-aws-get-1 |aws |arn:aws:iam::123412341234:role/cpln-demo-cpln-organization |Now | | |demo-cloud-account-aws-get-2 |demo-cloud-account-aws-get-2 |aws |arn:aws:iam::123412341234:role/cpln-demo-cpln-organization |Now | | +-----------------------------+-----------------------------+---------+-----------------------------------------------------------+--------+-----+ ``` *** #### cloudaccount patch Update the referenced cloud account's metadata using an input file **Usage** ```none theme={null} cpln cloudaccount patch --file FILE_NAME [OPTIONS] ``` Running this command with the option **`--file -`** sets the input to stdin. The input is required to be a valid JSON or YAML string (e.g., `{"description": "My new description"}` or `description: My new description`). To complete the update, press Control-D (or Control-Z if you are using Windows). ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --file, -f File to load the patch from. Use `--file -` to enable input from stdin. [required] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln cloudaccount patch demo-cloud-account-aws --file update.json --org demo-cpln-organization ``` ```text Output theme={null} The changes within the JSON file will be performed against the referenced cloud account. ``` *** #### cloudaccount permissions Show the grantable permissions for a cloud account object type **Usage** ```none theme={null} cpln cloudaccount permissions [OPTIONS] ``` ```text theme={null} Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln cloudaccount permissions --org demo-cpln-organization ``` ```text Output theme={null} +-------+-------------------------------+-------------------------------------------+ |NAME |DESCRIPTION |IMPLIES | |-------|-------------------------------|-------------------------------------------| |browse |Browse account contents |view | |create |Create new cloud accounts | | |delete |Delete existing cloud accounts | | |edit |Modify existing cloud accounts |view, browse | |manage |Full access |browse, create, delete, edit, manage, view | |view |Read-only access | | +-------+-------------------------------+-------------------------------------------+ ``` *** #### cloudaccount query Find all the cloud accounts based on the given query **Usage** ```none theme={null} cpln cloudaccount query QUERY_OPTIONS [OPTIONS] ``` ```text theme={null} Query options: --match Query filter type [choices: "all", "none", "any"] [default: "all"] --property, --prop Property to query --tag Tag to query --rel Relation to query Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) Use the `--match` option to display **all**, **none**, or **any** of the results based on a given property or tag * Search for a cloud account based on the name property ```bash Command theme={null} cpln cloudaccount query --match any --prop name=demo-cloud-account-aws --org demo-cpln-organization ``` ```text Output theme={null} +-----------------------+-----------------------+---------+-----------------------------------------------------------+--------+-----+ |NAME |DESCRIPTION |PROVIDER |INFO |CREATED |TAGS | |-----------------------|-----------------------|---------|-----------------------------------------------------------|--------|-----| |demo-cloud-account-aws |demo-cloud-account-aws |aws |arn:aws:iam::123412341234:role/cpln-demo-cpln-organization |Now | | +-----------------------+-----------------------+---------+-----------------------------------------------------------+--------+-----+ ``` * Search for a cloud account based on the description property ```bash Command theme={null} cpln cloudaccount query --match any --prop description="demo-cloud-account-aws" --org demo-cpln-organization ``` ```text Output theme={null} +-----------------------+-----------------------+---------+-----------------------------------------------------------+--------+-----+ |NAME |DESCRIPTION |PROVIDER |INFO |CREATED |TAGS | |-----------------------|-----------------------|---------|-----------------------------------------------------------|--------|-----| |demo-cloud-account-aws |demo-cloud-account-aws |aws |arn:aws:iam::123412341234:role/cpln-demo-cpln-organization |Now | | +-----------------------+-----------------------+---------+-----------------------------------------------------------+--------+-----+ ``` * Search for a cloud account based on a tag ```bash Command theme={null} cpln cloudaccount query --match any --tag drink=water --org demo-cpln-organization ``` ```text Output theme={null} +-----+------------+---------+-----+--------+-----+ |NAME |DESCRIPTION |PROVIDER |INFO |CREATED |TAGS | +-----+------------+---------+-----+--------+-----+ ``` * Search for a cloud account based on a property and tag ```bash Command theme={null} cpln cloudaccount query --match any --prop description="demo-cloud-account-aws" --tag drink=water --org demo-cpln-organization ``` ```text Output theme={null} +-----------------------+-----------------------+---------+-----------------------------------------------------------+-------------+-----+ |NAME |DESCRIPTION |PROVIDER |INFO |CREATED |TAGS | |-----------------------|-----------------------|---------|-----------------------------------------------------------|-------------|-----| |demo-cloud-account-aws |demo-cloud-account-aws |aws |arn:aws:iam::123412341234:role/cpln-demo-cpln-organization |1 minute ago | | +-----------------------+-----------------------+---------+-----------------------------------------------------------+-------------+-----+ ``` *** #### cloudaccount tag Manage the tags belonging to one or more referenced cloud accounts **Usage** ```none theme={null} cpln cloudaccount tag --tag KEY=VALUE [OPTIONS] ``` Built-in tags, which begin with cpln/\*, can't be modified. ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [required] [default: []] Command options: --tag Attach tags (e.g., --tag drink=water) --remove-tag Remove tags (e.g., --remove-tag tagname) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Add a tag to a cloud account ```bash Command theme={null} cpln cloudaccount tag demo-cloud-account-azure --tag drink=water --org demo-cpln-organization ``` ```text Output theme={null} +-------------------------+-------------------------+---------+-----+-------------+--------------+ |NAME |DESCRIPTION |PROVIDER |INFO |CREATED |TAGS | |-------------------------|-------------------------|---------|-----|-------------|--------------| |demo-cloud-account-azure |demo-cloud-account-azure |azure | |1 minute ago |drink="water" | +-------------------------+-------------------------+---------+-----+-------------+--------------+ ``` * Update same tag (after running the example above) ```bash Command theme={null} cpln cloudaccount tag demo-cloud-account-azure --tag drink=coffee --org demo-cpln-organization ``` ```text Output theme={null} +-------------------------+-------------------------+---------+-----+-------------+---------------+ |NAME |DESCRIPTION |PROVIDER |INFO |CREATED |TAGS | |-------------------------|-------------------------|---------|-----|-------------|---------------| |demo-cloud-account-azure |demo-cloud-account-azure |azure | |1 minute ago |drink="coffee" | +-------------------------+-------------------------+---------+-----+-------------+---------------+ ``` * Remove tag (after running the example above) ```bash Command theme={null} cpln cloudaccount tag demo-cloud-account-azure --remove drink --org demo-cpln-organization ``` ```text Output theme={null} +-------------------------+-------------------------+---------+-----+-------------+-----+ |NAME |DESCRIPTION |PROVIDER |INFO |CREATED |TAGS | |-------------------------|-------------------------|---------|-----|-------------|-----| |demo-cloud-account-azure |demo-cloud-account-azure |azure | |1 minute ago | | +-------------------------+-------------------------+---------+-----+-------------+-----+ ``` * Add same tag to multiple cloud accounts ```bash Command theme={null} cpln cloudaccount tag demo-cloud-account-azure demo-cloud-account-gcp --tag multiple-cloudaccounts=true --org demo-cpln-organization ``` ```text Output theme={null} +-------------------------+-------------------------+---------+--------+-------------+------------------------------+ |NAME |DESCRIPTION |PROVIDER |INFO |CREATED |TAGS | |-------------------------|-------------------------|---------|--------|-------------|------------------------------| |demo-cloud-account-azure |demo-cloud-account-azure |azure | |1 minute ago |multiple-cloudaccounts="true" | |demo-cloud-account-gcp |demo-cloud-account-gcp |gcp |1234abc |1 minute ago |multiple-cloudaccounts="true" | +-------------------------+-------------------------+---------+--------+-------------+------------------------------+ ``` * Update same tag on multiple cloud accounts (after running the example above) ```bash Command theme={null} cpln cloudaccount tag demo-cloud-account-azure demo-cloud-account-gcp --tag multiple-cloudaccounts=demo-cloudaccount --org demo-cpln-organization ``` ```text Output theme={null} +-------------------------+-------------------------+---------+--------+-------------+-------------------------------------------+ |NAME |DESCRIPTION |PROVIDER |INFO |CREATED |TAGS | |-------------------------|-------------------------|---------|--------|-------------|-------------------------------------------| |demo-cloud-account-azure |demo-cloud-account-azure |azure | |1 minute ago |multiple-cloudaccounts="demo-cloudaccount" | |demo-cloud-account-gcp |demo-cloud-account-gcp |gcp |1234abc |1 minute ago |multiple-cloudaccounts="demo-cloudaccount" | +-------------------------+-------------------------+---------+--------+-------------+-------------------------------------------+ ``` * Remove same tag from multiple cloud accounts (after running the example above) ```bash Command theme={null} cpln cloudaccount tag demo-cloud-account-azure demo-cloud-account-gcp --remove multiple-cloudaccounts --org demo-cpln-organization ``` ```text Output theme={null} +-------------------------+-------------------------+---------+--------+-------------+-----+ |NAME |DESCRIPTION |PROVIDER |INFO |CREATED |TAGS | |-------------------------|-------------------------|---------|--------|-------------|-----| |demo-cloud-account-azure |demo-cloud-account-azure |azure | |1 minute ago | | |demo-cloud-account-gcp |demo-cloud-account-gcp |gcp |1234abc |1 minute ago | | +-------------------------+-------------------------+---------+--------+-------------+-----+ ``` *** # convert Source: https://docs.controlplane.com/cli-reference/commands/convert cpln convert — Convert Kubernetes resource files to Control Plane resource files. Convert k8s files to cpln files Refer to the [cpln convert guide](/guides/cli/cpln-convert) for additional details. **Usage** ```none theme={null} cpln convert [OPTIONS] ``` ```text theme={null} Command options: --file, -f File to load and use for the command. Use `--file -` to enable input from stdin. [required] --protocol Protocol to use for all container ports [choices: "http", "http2", "grpc", "tcp"] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] --version, -v Show version number [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** # cp Source: https://docs.controlplane.com/cli-reference/commands/cp cpln cp — Copy files and directories to and from running workloads. Copy files and directories to and from workloads. Refer to the [cpln cp guide](/guides/cli/cpln-cp) for additional details. **Usage** ```none theme={null} cpln cp [OPTIONS] ``` ```text theme={null} Positionals: file-spec-src A path to the source file or directory to copy from. Use a local path or workloadName:path for paths within a workload [string] [required] file-spec-dest A path to the destination file or directory to copy to. Use a local path or workloadName:path for paths within a workload [string] [required] Command options: --location A global virtual cloud location [string] --container, -c The name of the workload container --replica The name of the workload deployment replica --no-preserve The copied file/directory's ownership and permissions will not be preserved in the container [boolean] [default: false] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] --version, -v Show version number [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Copy file from local machine to workload ```bash Command theme={null} cpln cp ./demo-file.txt demo-workload:path/within/workload/ --org demo-cpln-organization --gvc demo-gvc ``` ```text Output theme={null} If the copy was successful, a new command prompt will be shown. ``` * Copy directory from local machine to workload ```bash Command theme={null} cpln cp ./demo-directory demo-workload:path/within/workload/ --org demo-cpln-organization --gvc demo-gvc ``` ```text Output theme={null} If the copy was successful, a new command prompt will be shown. ``` * Copy file from workload to local machine ```bash Command theme={null} cpln cp demo-workload:path/within/workload/demo-file.txt ./demo-file.txt --org demo-cpln-organization --gvc demo-gvc ``` ```text Output theme={null} If the copy was successful, a new command prompt will be shown. ``` * Copy directory from workload to local machine ```bash Command theme={null} cpln cp demo-workload:path/within/workload/demo-directory ./demo-directory --org demo-cpln-organization --gvc demo-gvc ``` ```text Output theme={null} If the copy was successful, a new command prompt will be shown. ``` *** # delete Source: https://docs.controlplane.com/cli-reference/commands/delete cpln delete — Delete resources defined in input files. Delete resources from files Refer to the [cpln delete guide](/guides/cpln-delete) for additional details. **Usage** ```none theme={null} cpln delete [OPTIONS] ``` ```text theme={null} Command options: --file, -f File to load and use for the command. Use `--file -` to enable input from stdin. [required] --k8s Set this true if input file is k8s config file [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] --version, -v Show version number [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** # domain Source: https://docs.controlplane.com/cli-reference/commands/domain cpln domain — Manage custom domains for routing traffic to your workloads. Manage custom domains For a detailed overview, please visit the [domain's](/reference/domain) reference page. ```none theme={null} cpln domain [SUBCOMMAND] ``` ```text theme={null} Commands: cpln domain access-report Show the access report for the referenced domain cpln domain audit [ref] Retrieve audit trail events for the referenced domain cpln domain create Create a new domain cpln domain delete Delete one or more referenced domains cpln domain edit Edit the referenced domain, as YAML, within an editor cpln domain eventlog Show the event log of the referenced domain [aliases: log] cpln domain get [ref...] Retrieve one or more referenced domains cpln domain patch Update the referenced domain's metadata using an input file cpln domain permissions Show the grantable permissions for a domain object type cpln domain query Find all the domains based on the given query cpln domain tag Manage the tags belonging to one or more referenced domains Options: --help Show help [boolean] ``` *** #### domain access-report Show the access report for the referenced domain **Usage** ```none theme={null} cpln domain access-report [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) The domain `demo.globalvirtualcloud.com` has already been added to the `demo-cpln-organization` organization ```bash Command theme={null} cpln domain access-report demo.globalvirtualcloud.com --org demo-cpln-organization ``` ```text Output theme={null} +-----------+--------------------------------------------------------+--------+ |PERMISSION |PRINCIPAL |GRANTED | |-----------|--------------------------------------------------------|--------| |create |/org/demo-cpln-organization/group/superusers |manage | |create |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |delete |/org/demo-cpln-organization/group/superusers |manage | |delete |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |edit |/org/demo-cpln-organization/group/superusers |manage | |edit |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |manage |/org/demo-cpln-organization/group/superusers |manage | |manage |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |use |/org/demo-cpln-organization/group/superusers |manage | |use |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |view |/org/demo-cpln-organization/group/superusers |manage | |view |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |view |/org/demo-cpln-organization/group/viewers |view | +-----------+--------------------------------------------------------+--------+ ``` *** #### domain audit Retrieve audit trail events for the referenced domain **Usage** ```none theme={null} cpln domain audit [ref] [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. Command options: --subject Filter by subject (serviceaccount name or user email) [string] --context Audit context name (the 'cpln' context displays Control Plane actions) [string] [default: "cpln"] --since Relative lookback window from now (e.g., 1h, 24h, 7d). Mutually exclusive with --from/--to [string] [default: "7d"] --from Start time for audit events (ISO 8601 format or relative duration, e.g., 2025-10-23T07:00:00Z, 7d, 1h). Cannot be used with --since [string] --to End time for audit events (ISO 8601 format or relative duration, e.g., 2025-10-23T07:00:00Z, 1d, 30m) [string] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### domain create Create a new domain **Usage** ```none theme={null} cpln domain create --name DOMAIN_NAME [OPTIONS] ``` Ownership of the referenced domain is required by setting a TXT DNS entry. Review the [domain configuration instructions](/reference/domain) for details. ```text theme={null} Command options: --name Fully qualified domain name (e.g., widgets.example.com) [required] --description, --desc Optional description, defaults to the name if not set --tag Attach tags (e.g., --tag drink=water) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln domain create --name demo.globalvirtualcloud.com --org demo-cpln-organization ``` ```text Output theme={null} The ownership of the referenced domain will be validated and if successful, added to the referenced organization. ``` *** #### domain delete Delete one or more referenced domains **Usage** ```none theme={null} cpln domain delete [OPTIONS] ``` ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [required] [default: []] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln domain delete demo.globalvirtualcloud.com --org demo-cpln-organization ``` ```text Output theme={null} The referenced domain will be removed from the referenced organization. ``` *** #### domain edit Edit the referenced domain, as YAML, within an editor **Usage** ```none theme={null} cpln domain edit [OPTIONS] ``` The default editor can be set by using the EDITOR environment variable. ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --replace, -r Replace instead of patch/merge [boolean] [default: false] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln domain edit demo.globalvirtualcloud.com --org demo-cpln-organization ``` ```text Output theme={null} Your default editor will be launched. Once any modifications are saved, the updates will be sent to the API. ``` *** #### domain eventlog Show the event log of the referenced domain **Usage** ```none theme={null} cpln domain eventlog [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln domain eventlog demo.globalvirtualcloud.com --org demo-cpln-organization ``` ```text Output theme={null} The event log for the referenced domain will be shown. ``` *** #### domain get Retrieve one or more referenced domains **Usage** ```none theme={null} cpln domain get [ref...] [OPTIONS] ``` An empty reference will return all references ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [default: []] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Show a domain ```bash Command theme={null} cpln domain get demo.globalvirtualcloud.com --org demo-cpln-organization ``` ```text Output theme={null} +----------------------------+----------------------------------+----------------------+ |NAME |DESCRIPTION |TAGS | |----------------------------|----------------------------------|----------------------| |demo.globalvirtualcloud.com |FQDN: demo.globalvirtualcloud.com |cpln/protected="true" | | | |drink="coffee" | +----------------------------+----------------------------------+----------------------+ ``` * Show multiple domains ```bash Command theme={null} cpln domain get demo.globalvirtualcloud.com demo2.globalvirtualcloud.com --org demo-cpln-organization ``` ```text Output theme={null} +-----------------------------+----------------------------------+----------------------+ |NAME |DESCRIPTION |TAGS | |-----------------------------|----------------------------------|----------------------| |demo.globalvirtualcloud.com |FQDN: demo.globalvirtualcloud.com |cpln/protected="true" | | | |drink="coffee" | |demo2.globalvirtualcloud.com |demo2.globalvirtualcloud.com |cpln/protected="true" | | | |demo-domain="true" | +-----------------------------+----------------------------------+----------------------+ ``` *** #### domain patch Update the referenced domain's metadata using an input file **Usage** ```none theme={null} cpln domain patch --file FILE_NAME [OPTIONS] ``` Running this command with the option **`--file -`** sets the input to stdin. The input is required to be a valid JSON or YAML string (e.g., `{"description": "My new description"}` or `description: My new description`). To complete the update, press Control-D (or Control-Z if you are using Windows). ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --file, -f File to load the patch from. Use `--file -` to enable input from stdin. [required] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln domain patch demo.globalvirtualcloud.com --file update.json --org demo-cpln-organization ``` ```text Output theme={null} The changes within the JSON file will be performed against the referenced domain. ``` *** #### domain permissions Show the grantable permissions for a domain object type **Usage** ```none theme={null} cpln domain permissions [OPTIONS] ``` ```text theme={null} Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln domain permissions --org demo-cpln-organization ``` ```text Output theme={null} +-------+-----------------------------------------+----------------------------------------+ |NAME |DESCRIPTION |IMPLIES | |-------|-----------------------------------------|----------------------------------------| |create |Create new domain | | |delete |Delete a domain | | |edit |Modify existing domains (only tags ca... |view, use | |manage |Full access |create, delete, edit, manage, use, view | |use |Allow a principal to use this domain |view | |view |Read-only access | | +-------+-----------------------------------------+----------------------------------------+ ``` *** #### domain query Find all the domains based on the given query **Usage** ```none theme={null} cpln domain query QUERY_OPTIONS [OPTIONS] ``` ```text theme={null} Query options: --match Query filter type [choices: "all", "none", "any"] [default: "all"] --property, --prop Property to query --tag Tag to query --rel Relation to query Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) Use the `--match` option to display **all**, **none**, or **any** of the results based on a given property or tag * Search for a domain based on the name property ```bash Command theme={null} cpln domain query --match any --prop name=demo.globalvirtualcloud.com --org demo-cpln-organization ``` ```text Output theme={null} +----------------------------+----------------------------------+----------------------+ |NAME |DESCRIPTION |TAGS | |----------------------------|----------------------------------|----------------------| |demo.globalvirtualcloud.com |FQDN: demo.globalvirtualcloud.com |cpln/protected="true" | | | |drink="coffee" | +----------------------------+----------------------------------+----------------------+ ``` * Search for a domain based on the description property ```bash Command theme={null} cpln domain query --match any --prop description="FQDN: demo.globalvirtualcloud.com" --org demo-cpln-organization ``` ```text Output theme={null} +----------------------------+----------------------------------+----------------------+ |NAME |DESCRIPTION |TAGS | |----------------------------|----------------------------------|----------------------| |demo.globalvirtualcloud.com |FQDN: demo.globalvirtualcloud.com |cpln/protected="true" | | | |drink="coffee" | +----------------------------+----------------------------------+----------------------+ ``` * Search for a domain based on a tag ```bash Command theme={null} cpln domain query --match any --tag demo-domain="true" --org demo-cpln-organization ``` ```text Output theme={null} +-----------------------------+-----------------------------+----------------------+ |NAME |DESCRIPTION |TAGS | |-----------------------------|-----------------------------|----------------------| |demo2.globalvirtualcloud.com |demo2.globalvirtualcloud.com |cpln/protected="true" | | | |demo-domain="true" | +-----------------------------+-----------------------------+----------------------+ ``` * Search for a domain based on a property and tag ```bash Command theme={null} cpln domain query --match any --prop description="FQDN: demo.globalvirtualcloud.com" --tag demo-domain="true" --org demo-cpln-organization ``` ```text Output theme={null} +-----------------------------+----------------------------------+----------------------+ |NAME |DESCRIPTION |TAGS | |-----------------------------|----------------------------------|----------------------| |demo.globalvirtualcloud.com |FQDN: demo.globalvirtualcloud.com |cpln/protected="true" | | | |drink="coffee" | |demo2.globalvirtualcloud.com |demo2.globalvirtualcloud.com |cpln/protected="true" | | | |demo-domain="true" | +-----------------------------+----------------------------------+----------------------+ ``` *** #### domain tag Manage the tags belonging to one or more referenced domains **Usage** ```none theme={null} cpln domain tag --tag KEY=VALUE [OPTIONS] ``` Built-in tags, which begin with cpln/\*, can't be modified. ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [required] [default: []] Command options: --tag Attach tags (e.g., --tag drink=water) --remove-tag Remove tags (e.g., --remove-tag tagname) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Add a tag to a domain ```bash Command theme={null} cpln domain tag demo.globalvirtualcloud.com --tag drink=water --org demo-cpln-organization ``` ```text Output theme={null} +----------------------------+----------------------------------+----------------------+ |NAME |DESCRIPTION |TAGS | |----------------------------|----------------------------------|----------------------| |demo.globalvirtualcloud.com |FQDN: demo.globalvirtualcloud.com |cpln/protected="true" | | | |drink="water" | +----------------------------+----------------------------------+----------------------+ ``` * Update same tag (after running the example above) ```bash Command theme={null} cpln domain tag demo.globalvirtualcloud.com --tag drink=coffee --org demo-cpln-organization ``` ```text Output theme={null} +----------------------------+----------------------------------+----------------------+ |NAME |DESCRIPTION |TAGS | |----------------------------|----------------------------------|----------------------| |demo.globalvirtualcloud.com |FQDN: demo.globalvirtualcloud.com |cpln/protected="true" | | | |drink="coffee" | +----------------------------+----------------------------------+----------------------+ ``` * Remove tag (after running the example above) ```bash Command theme={null} cpln domain tag demo.globalvirtualcloud.com --remove name --org demo-cpln-organization ``` ```text Output theme={null} +----------------------------+----------------------------------+----------------------+ |NAME |DESCRIPTION |TAGS | |----------------------------|----------------------------------|----------------------| |demo.globalvirtualcloud.com |FQDN: demo.globalvirtualcloud.com |cpln/protected="true" | | | |drink="coffee" | +----------------------------+----------------------------------+----------------------+ ``` * Add same tag to multiple domain ```bash Command theme={null} cpln domain tag demo.globalvirtualcloud.com demo2.globalvirtualcloud.com --tag multiple-domains=true --org demo-cpln-organization ``` ```text Output theme={null} +-----------------------------+----------------------------------+------------------------+ |NAME |DESCRIPTION |TAGS | |-----------------------------|----------------------------------|------------------------| |demo.globalvirtualcloud.com |FQDN: demo.globalvirtualcloud.com |cpln/protected="true" | | | |drink="coffee" | | | |multiple-domains="true" | |demo2.globalvirtualcloud.com |demo2.globalvirtualcloud.com |cpln/protected="true" | | | |demo-domain="true" | | | |multiple-domains="true" | +-----------------------------+----------------------------------+------------------------+ ``` * Update same tag on multiple domain (after running the example above) ```bash Command theme={null} cpln domain tag demo.globalvirtualcloud.com demo2.globalvirtualcloud.com --tag multiple-domains=demo-domain --org demo-cpln-organization ``` ```text Output theme={null} +-----------------------------+----------------------------------+-------------------------------+ |NAME |DESCRIPTION |TAGS | |-----------------------------|----------------------------------|-------------------------------| |demo.globalvirtualcloud.com |FQDN: demo.globalvirtualcloud.com |cpln/protected="true" | | | |drink="coffee" | | | |multiple-domains="demo-domain" | |demo2.globalvirtualcloud.com |demo2.globalvirtualcloud.com |cpln/protected="true" | | | |demo-domain="true" | | | |multiple-domains="demo-domain" | +-----------------------------+----------------------------------+-------------------------------+ ``` * Remove same tag from multiple domain (after running the example above) ```bash Command theme={null} cpln domain tag demo.globalvirtualcloud.com demo2.globalvirtualcloud.com --remove multiple-domains --org demo-cpln-organization ``` ```text Output theme={null} +-----------------------------+----------------------------------+----------------------+ |NAME |DESCRIPTION |TAGS | |-----------------------------|----------------------------------|----------------------| |demo.globalvirtualcloud.com |FQDN: demo.globalvirtualcloud.com |cpln/protected="true" | | | |drink="coffee" | |demo2.globalvirtualcloud.com |demo2.globalvirtualcloud.com |cpln/protected="true" | | | |demo-domain="true" | +-----------------------------+----------------------------------+----------------------+ ``` *** # group Source: https://docs.controlplane.com/cli-reference/commands/group cpln group — Manage groups for organizing users and service accounts. Manage groups For a detailed overview, please visit the [group's](/reference/group) reference page. ```none theme={null} cpln group [SUBCOMMAND] ``` ```text theme={null} Commands: cpln group access-report Show the access report for the referenced group cpln group add-member Add members to the referenced group cpln group audit [ref] Retrieve audit trail events for the referenced group cpln group clone Create a clone of the referenced group; this will only duplicate its spec. [aliases: copy] cpln group create Create a new group cpln group delete Delete one or more referenced groups cpln group edit Edit the referenced group, as YAML, within an editor cpln group eventlog Show the event log of the referenced group [aliases: log] cpln group get [ref...] Retrieve one or more referenced groups cpln group patch Update the referenced group's metadata using an input file cpln group permissions Show the grantable permissions for a group object type cpln group query Find all the groups based on the given query cpln group remove-member Remove members from the referenced group cpln group tag Manage the tags belonging to one or more referenced groups cpln group update Update properties of the referenced group Options: --help Show help [boolean] ``` *** #### group access-report Show the access report for the referenced group **Usage** ```none theme={null} cpln group access-report [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln group access-report demo-group --org demo-cpln-organization ``` ```text Output theme={null} +-----------+--------------------------------------------------------+--------+ |PERMISSION |PRINCIPAL |GRANTED | |-----------|--------------------------------------------------------|--------| |create |/org/demo-cpln-organization/group/superusers |manage | |create |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |delete |/org/demo-cpln-organization/group/superusers |manage | |delete |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |edit |/org/demo-cpln-organization/group/superusers |manage | |edit |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |manage |/org/demo-cpln-organization/group/superusers |manage | |manage |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |view |/org/demo-cpln-organization/group/viewers |view | |view |/org/demo-cpln-organization/group/superusers |manage | |view |/org/demo-cpln-organization/serviceaccount/controlplane |manage | +-----------+--------------------------------------------------------+--------+ ``` *** #### group add-member Add members to the referenced group **Usage** ```none theme={null} cpln group add-member --email USER_EMAIL [OPTIONS] cpln group add-member --serviceaccount SERVICE_ACCOUNT [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --email Email of user to add --serviceaccount Name of the service account to add --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Add a user to a group ```bash Command theme={null} cpln group add-member demo-group --email test-128@example.com --org demo-cpln-organization ``` ```text Output theme={null} +-----------+------------+-------------+-------+--------+--------------+ |NAME |DESCRIPTION |CREATED |FILTER |ORIGIN |TAGS | |-----------|------------|-------------|-------|--------|--------------| |demo-group |demo-group |1 minute ago |links |default |drink="water" | +-----------+------------+-------------+-------+--------+--------------+ ``` * Add a service account to a group using email ```bash Command theme={null} cpln group add-member demo-group --serviceaccount controlplane --org demo-cpln-organization ``` ```text Output theme={null} +-----------+------------+-------------+-------+--------+--------------+ |NAME |DESCRIPTION |CREATED |FILTER |ORIGIN |TAGS | |-----------|------------|-------------|-------|--------|--------------| |demo-group |demo-group |1 minute ago |links |default |drink="water" | +-----------+------------+-------------+-------+--------+--------------+ ``` *** #### group audit Retrieve audit trail events for the referenced group **Usage** ```none theme={null} cpln group audit [ref] [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. Command options: --subject Filter by subject (serviceaccount name or user email) [string] --context Audit context name (the 'cpln' context displays Control Plane actions) [string] [default: "cpln"] --since Relative lookback window from now (e.g., 1h, 24h, 7d). Mutually exclusive with --from/--to [string] [default: "7d"] --from Start time for audit events (ISO 8601 format or relative duration, e.g., 2025-10-23T07:00:00Z, 7d, 1h). Cannot be used with --since [string] --to End time for audit events (ISO 8601 format or relative duration, e.g., 2025-10-23T07:00:00Z, 1d, 30m) [string] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### group clone Create a clone of the referenced group; this will only duplicate its spec. **Usage** ```none theme={null} cpln group clone --name CLONE_NAME [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --name Set the name for the clone [required] --description Optional description, defaults to the name if not set --tag Attach tags (e.g., --tag drink=water) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln group clone demo-group --name demo-group-clone --org demo-cpln-organization ``` ```text Output theme={null} +-----------------+--------------------+--------+-------+--------+--------------+ |NAME |DESCRIPTION |CREATED |FILTER |ORIGIN |TAGS | |-----------------|--------------------|--------|-------|--------|--------------| |demo-group-clone |Clone of demo-group |Now |links |default |drink="water" | +-----------------+--------------------+--------+-------+--------+--------------+ ``` *** #### group create Create a new group **Usage** ```none theme={null} cpln group create --name GROUP_NAME [OPTIONS] ``` After creating a new group, use the subcommand [add-member](#group-add-member) to populate the group with users and/or service accounts. ```text theme={null} Command options: --name Name of the new group [required] --description, --desc Optional description, defaults to the name if not set --tag Attach tags (e.g., --tag drink=water) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Query spec options: --query-match Query filter type [choices: "all", "none", "any"] [default: "all"] --query-property Property to query --query-tag Tag to query --query-rel Relation to query --query-kind Kind to query [choices: "user"] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln group create --name demo-group --desc demo-group --tag drink=water --org demo-cpln-organization ``` ```text Output theme={null} +-----------+------------+--------+-------+--------+--------------+ |NAME |DESCRIPTION |CREATED |FILTER |ORIGIN |TAGS | |-----------|------------|--------|-------|--------|--------------| |demo-group |demo-group |Now |links |default |drink="water" | +-----------+------------+--------+-------+--------+--------------+ ``` *** #### group delete Delete one or more referenced groups **Usage** ```none theme={null} cpln group delete [OPTIONS] ``` ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [required] [default: []] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln group delete demo-group --org demo-cpln-organization ``` ```text Output theme={null} If the delete was successful, a new command prompt will be shown. ``` *** #### group edit Edit the referenced group, as YAML, within an editor **Usage** ```none theme={null} cpln group edit [OPTIONS] ``` The default editor can be set by using the EDITOR environment variable. ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --replace, -r Replace instead of patch/merge [boolean] [default: false] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln group edit demo-group --org demo-cpln-organization ``` ```text Output theme={null} Your default editor will be launched. Once any modifications are saved, the updates will be sent to the API. ``` *** #### group eventlog Show the event log of the referenced group **Usage** ```none theme={null} cpln group eventlog [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln group eventlog demo-group --org demo-cpln-organization ``` ```text Output theme={null} The event log for the referenced group will be shown. ``` *** #### group get Retrieve one or more referenced groups **Usage** ```none theme={null} cpln group get [ref...] [OPTIONS] ``` An empty reference will return all references ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [default: []] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Get all groups ```bash Command theme={null} cpln group get --org demo-cpln-organization ``` ```text Output theme={null} +-----------------+-----------------------------------------+-------------+-------+--------+--------------+ |NAME |DESCRIPTION |CREATED |FILTER |ORIGIN |TAGS | |-----------------|-----------------------------------------|-------------|-------|--------|--------------| |demo-group |demo-group |1 minute ago |links |default |drink="water" | |demo-group-get-1 |demo-group-get-1 |Now |links |default | | |demo-group-get-2 |demo-group-get-2 |Now |links |default | | |superusers |Built-in group for all administrators... |5 years ago |links |builtin | | |viewers |Built-in group for read-only access |5 years ago |links |builtin | | +-----------------+-----------------------------------------+-------------+-------+--------+--------------+ ``` * Get one group ```bash Command theme={null} cpln group get demo-group-get-1 --org demo-cpln-organization ``` ```text Output theme={null} +-----------------+-----------------+--------+-------+--------+-----+ |NAME |DESCRIPTION |CREATED |FILTER |ORIGIN |TAGS | |-----------------|-----------------|--------|-------|--------|-----| |demo-group-get-1 |demo-group-get-1 |Now |links |default | | +-----------------+-----------------+--------+-------+--------+-----+ ``` * Get multiple groups ```bash Command theme={null} cpln group get demo-group-get-1 demo-group-get-2 --org demo-cpln-organization ``` ```text Output theme={null} +-----------------+-----------------+--------+-------+--------+-----+ |NAME |DESCRIPTION |CREATED |FILTER |ORIGIN |TAGS | |-----------------|-----------------|--------|-------|--------|-----| |demo-group-get-1 |demo-group-get-1 |Now |links |default | | |demo-group-get-2 |demo-group-get-2 |Now |links |default | | +-----------------+-----------------+--------+-------+--------+-----+ ``` *** #### group patch Update the referenced group's metadata using an input file **Usage** ```none theme={null} cpln group patch --file FILE_NAME [OPTIONS] ``` Running this command with the option **`--file -`** sets the input to stdin. The input is required to be a valid JSON or YAML string (e.g., `{"description": "My new description"}` or `description: My new description`). To complete the update, press Control-D (or Control-Z if you are using Windows). ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --file, -f File to load the patch from. Use `--file -` to enable input from stdin. [required] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln group patch demo-group --file update.json --org demo-cpln-organization ``` ```text Output theme={null} The changes within the JSON file will be performed against the referenced group. ``` *** #### group permissions Show the grantable permissions for a group object type **Usage** ```none theme={null} cpln group permissions [OPTIONS] ``` ```text theme={null} Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln group permissions --org demo-cpln-organization ``` ```text Output theme={null} +-------+-----------------------+-----------------------------------+ |NAME |DESCRIPTION |IMPLIES | |-------|-----------------------|-----------------------------------| |create |Create new groups | | |delete |Delete a group | | |edit |Modify existing groups |view | |manage |Full access |create, delete, edit, manage, view | |view |Read-only view | | +-------+-----------------------+-----------------------------------+ ``` *** #### group query Find all the groups based on the given query **Usage** ```none theme={null} cpln group query QUERY_OPTIONS [OPTIONS] ``` ```text theme={null} Query options: --match Query filter type [choices: "all", "none", "any"] [default: "all"] --property, --prop Property to query --tag Tag to query --rel Relation to query Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) Use the `--match` option to display **all**, **none**, or **any** of the results based on a given property or tag * Search for a group based on the name property ```bash Command theme={null} cpln group query --match any --prop name=demo-group --org demo-cpln-organization ``` ```text Output theme={null} +-----------+------------+-------------+-------+--------+--------------+ |NAME |DESCRIPTION |CREATED |FILTER |ORIGIN |TAGS | |-----------|------------|-------------|-------|--------|--------------| |demo-group |demo-group |1 minute ago |links |default |drink="water" | +-----------+------------+-------------+-------+--------+--------------+ ``` * Search for a group based on the description property ```bash Command theme={null} cpln group query --match any --prop description="demo-group" --org demo-cpln-organization ``` ```text Output theme={null} +-----------+------------+-------------+-------+--------+--------------+ |NAME |DESCRIPTION |CREATED |FILTER |ORIGIN |TAGS | |-----------|------------|-------------|-------|--------|--------------| |demo-group |demo-group |1 minute ago |links |default |drink="water" | +-----------+------------+-------------+-------+--------+--------------+ ``` * Search for a group based on a tag ```bash Command theme={null} cpln group query --match any --tag drink=water --org demo-cpln-organization ``` ```text Output theme={null} +-----------+------------+-------------+-------+--------+--------------+ |NAME |DESCRIPTION |CREATED |FILTER |ORIGIN |TAGS | |-----------|------------|-------------|-------|--------|--------------| |demo-group |demo-group |1 minute ago |links |default |drink="water" | +-----------+------------+-------------+-------+--------+--------------+ ``` * Search for a group based on a property and tag ```bash Command theme={null} cpln group query --match any --prop description="demo-group" --tag drink=water --org demo-cpln-organization ``` ```text Output theme={null} +-----------+------------+-------------+-------+--------+--------------+ |NAME |DESCRIPTION |CREATED |FILTER |ORIGIN |TAGS | |-----------|------------|-------------|-------|--------|--------------| |demo-group |demo-group |1 minute ago |links |default |drink="water" | +-----------+------------+-------------+-------+--------+--------------+ ``` *** #### group remove-member Remove members from the referenced group **Usage** ```none theme={null} cpln group remove-member --email USER_EMAIL [OPTIONS] cpln group remove-member --serviceaccount SERVICE_ACCOUNT [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --email Email of user to remove --serviceaccount Name of the service account to remove --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) The user and service account referenced in the examples below were previously added to the group * Remove a user to a group ```bash Command theme={null} cpln group remove-member demo-group --email test-128@example.com --org demo-cpln-organization ``` ```text Output theme={null} +-----------+------------+-------------+-------+--------+--------------+ |NAME |DESCRIPTION |CREATED |FILTER |ORIGIN |TAGS | |-----------|------------|-------------|-------|--------|--------------| |demo-group |demo-group |1 minute ago |links |default |drink="water" | +-----------+------------+-------------+-------+--------+--------------+ ``` * Remove a service account to a group using email ```bash Command theme={null} cpln group remove-member demo-group --serviceaccount controlplane --org demo-cpln-organization ``` ```text Output theme={null} +-----------+------------+-------------+-------+--------+--------------+ |NAME |DESCRIPTION |CREATED |FILTER |ORIGIN |TAGS | |-----------|------------|-------------|-------|--------|--------------| |demo-group |demo-group |1 minute ago |links |default |drink="water" | +-----------+------------+-------------+-------+--------+--------------+ ``` *** #### group tag Manage the tags belonging to one or more referenced groups **Usage** ```none theme={null} cpln group tag --tag KEY=VALUE [OPTIONS] ``` Built-in tags, which begin with cpln/\*, can't be modified. ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [required] [default: []] Command options: --tag Attach tags (e.g., --tag drink=water) --remove-tag Remove tags (e.g., --remove-tag tagname) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Add a tag to a group ```bash Command theme={null} cpln group tag demo-group-tag-1 --tag drink=water --org demo-cpln-organization ``` ```text Output theme={null} +-----------------+-----------------+--------+-------+--------+--------------+ |NAME |DESCRIPTION |CREATED |FILTER |ORIGIN |TAGS | |-----------------|-----------------|--------|-------|--------|--------------| |demo-group-tag-1 |demo-group-tag-1 |Now |links |default |drink="water" | +-----------------+-----------------+--------+-------+--------+--------------+ ``` * Update same tag (after running the example above) ```bash Command theme={null} cpln group tag demo-group-tag-1 --tag drink=coffee --org demo-cpln-organization ``` ```text Output theme={null} +-----------------+-----------------+--------+-------+--------+---------------+ |NAME |DESCRIPTION |CREATED |FILTER |ORIGIN |TAGS | |-----------------|-----------------|--------|-------|--------|---------------| |demo-group-tag-1 |demo-group-tag-1 |Now |links |default |drink="coffee" | +-----------------+-----------------+--------+-------+--------+---------------+ ``` * Remove tag (after running the example above) ```bash Command theme={null} cpln group tag demo-group-tag-1 --remove drink --org demo-cpln-organization ``` ```text Output theme={null} +-----------------+-----------------+--------+-------+--------+-----+ |NAME |DESCRIPTION |CREATED |FILTER |ORIGIN |TAGS | |-----------------|-----------------|--------|-------|--------|-----| |demo-group-tag-1 |demo-group-tag-1 |Now |links |default | | +-----------------+-----------------+--------+-------+--------+-----+ ``` * Add same tag to multiple groups ```bash Command theme={null} cpln group tag demo-group-tag-1 demo-group-tag-2 --tag multiple-groups=true --org demo-cpln-organization ``` ```text Output theme={null} +-----------------+-----------------+--------+-------+--------+-----------------------+ |NAME |DESCRIPTION |CREATED |FILTER |ORIGIN |TAGS | |-----------------|-----------------|--------|-------|--------|-----------------------| |demo-group-tag-1 |demo-group-tag-1 |Now |links |default |multiple-groups="true" | |demo-group-tag-2 |demo-group-tag-2 |Now |links |default |multiple-groups="true" | +-----------------+-----------------+--------+-------+--------+-----------------------+ ``` * Update same tag on multiple groups (after running the example above) ```bash Command theme={null} cpln group tag demo-group-tag-1 demo-group-tag-2 --tag multiple-groups=demo-group --org demo-cpln-organization ``` ```text Output theme={null} +-----------------+-----------------+--------+-------+--------+-----------------------------+ |NAME |DESCRIPTION |CREATED |FILTER |ORIGIN |TAGS | |-----------------|-----------------|--------|-------|--------|-----------------------------| |demo-group-tag-1 |demo-group-tag-1 |Now |links |default |multiple-groups="demo-group" | |demo-group-tag-2 |demo-group-tag-2 |Now |links |default |multiple-groups="demo-group" | +-----------------+-----------------+--------+-------+--------+-----------------------------+ ``` * Remove same tag from multiple groups (after running the example above) ```bash Command theme={null} cpln group tag demo-group-tag-1 demo-group-tag-2 --remove multiple-groups --org demo-cpln-organization ``` ```text Output theme={null} +-----------------+-----------------+--------+-------+--------+-----+ |NAME |DESCRIPTION |CREATED |FILTER |ORIGIN |TAGS | |-----------------|-----------------|--------|-------|--------|-----| |demo-group-tag-1 |demo-group-tag-1 |Now |links |default | | |demo-group-tag-2 |demo-group-tag-2 |Now |links |default | | +-----------------+-----------------+--------+-------+--------+-----+ ``` *** #### group update Update properties of the referenced group **Usage** ```none theme={null} cpln group update --set UPDATE_PROPERTY [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Update Properties: --set Update the following properties (e.g., --set description="Updated Description"): description string tags. string memberLinks string[] Arrays can be appended to, replaced, or removed using the `+=`, `=`, or `-=` operators, respectively. --unset Unset a property (same paths as --set, e.g., --unset description) Command options: --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * The `--set` option allows the modification of the following group properties: * `description` * Description of the group * Type: string * `tags` * Tags (key=value) associated with the group * Type: string * `memberLinks` * Array of links belonging to members of the group * Type: string\[] * For the examples below, the initial group is configured as: ```text theme={null} +------------------+------------------+--------+-------+--------+-----+ |NAME |DESCRIPTION |CREATED |FILTER |ORIGIN |TAGS | |------------------|------------------|--------|-------|--------|-----| |demo-group-update |demo-group-update |Now |links |default | | +------------------+------------------+--------+-------+--------+-----+ ``` * Update a group's description ```bash Command theme={null} cpln group update demo-group-update --set description="new description" --org demo-cpln-organization ``` ```text Output theme={null} +------------------+----------------+--------+-------+--------+-----+ |NAME |DESCRIPTION |CREATED |FILTER |ORIGIN |TAGS | |------------------|----------------|--------|-------|--------|-----| |demo-group-update |new description |Now |links |default | | +------------------+----------------+--------+-------+--------+-----+ ``` * Update a group's tags ```bash Command theme={null} cpln group update demo-group-update --set tags.demo-key=demo-value --org demo-cpln-organization ``` ```text Output theme={null} +------------------+----------------+--------+-------+--------+----------------------+ |NAME |DESCRIPTION |CREATED |FILTER |ORIGIN |TAGS | |------------------|----------------|--------|-------|--------|----------------------| |demo-group-update |new description |Now |links |default |demo-key="demo-value" | +------------------+----------------+--------+-------+--------+----------------------+ ``` * Add to a group's member links. This examples adds the built-in controlplane service account ```bash Command theme={null} cpln group update demo-group-update --set memberLinks+="/org/demo-cpln-organization/serviceaccount/controlplane" --org demo-cpln-organization ``` ```text Output theme={null} +------------------+----------------+--------+-------+--------+----------------------+ |NAME |DESCRIPTION |CREATED |FILTER |ORIGIN |TAGS | |------------------|----------------|--------|-------|--------|----------------------| |demo-group-update |new description |Now |links |default |demo-key="demo-value" | +------------------+----------------+--------+-------+--------+----------------------+ ``` * Remove from a group's member links. This examples removes the built-in controlplane service account ```bash Command theme={null} cpln group update demo-group-update --set memberLinks-="/org/demo-cpln-organization/serviceaccount/controlplane" --org demo-cpln-organization ``` ```text Output theme={null} +------------------+----------------+--------+-------+--------+----------------------+ |NAME |DESCRIPTION |CREATED |FILTER |ORIGIN |TAGS | |------------------|----------------|--------|-------|--------|----------------------| |demo-group-update |new description |Now |links |default |demo-key="demo-value" | +------------------+----------------+--------+-------+--------+----------------------+ ``` *** # gvc Source: https://docs.controlplane.com/cli-reference/commands/gvc cpln gvc — Manage Global Virtual Clouds (GVCs) that define multi-cloud deployment environments. Manage global virtual clouds For a detailed overview, please visit the [gvc's](/reference/gvc) reference page. ```none theme={null} cpln gvc [SUBCOMMAND] ``` ```text theme={null} Commands: cpln gvc access-report Show the access report for the referenced global virtual cloud cpln gvc add-location Add one or more locations to the referenced global virtual cloud cpln gvc audit [ref] Retrieve audit trail events for the referenced global virtual cloud cpln gvc clone Create a clone of the referenced global virtual cloud; this will only duplicate its spec. [aliases: copy] cpln gvc create Create a new global virtual cloud cpln gvc delete Delete GVCs by name cpln gvc delete-all-workloads Delete all workloads for the referenced global virtual cloud cpln gvc edit Edit the referenced global virtual cloud, as YAML, within an editor cpln gvc eventlog Show the event log of the referenced global virtual cloud [aliases: log] cpln gvc get [ref...] Retrieve one or more referenced global virtual clouds cpln gvc patch Update the referenced global virtual cloud's metadata using an input file cpln gvc permissions Show the grantable permissions for a global virtual cloud object type cpln gvc query Find all the global virtual clouds based on the given query cpln gvc remove-location Remove one or more locations from the referenced global virtual cloud cpln gvc tag Manage the tags belonging to one or more referenced global virtual clouds cpln gvc update Update properties of the referenced global virtual cloud Options: --help Show help [boolean] ``` *** #### gvc access-report Show the access report for the referenced global virtual cloud **Usage** ```none theme={null} cpln gvc access-report [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Display the user / service account / group permissions for the 'demo-gvc' global virtual cloud within the 'demo-cpln-organization' organization ```bash Command theme={null} cpln gvc access-report demo-gvc --org demo-cpln-organization ``` ```text Output theme={null} +----------------------+--------------------------------------------------------+--------+ |PERMISSION |PRINCIPAL |GRANTED | |----------------------|--------------------------------------------------------|--------| |configureLoadBalancer |/org/demo-cpln-organization/group/superusers |manage | |configureLoadBalancer |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |create |/org/demo-cpln-organization/group/superusers |manage | |create |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |delete |/org/demo-cpln-organization/group/superusers |manage | |delete |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |edit |/org/demo-cpln-organization/group/superusers |manage | |edit |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |manage |/org/demo-cpln-organization/group/superusers |manage | |manage |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |view |/org/demo-cpln-organization/group/viewers |view | |view |/org/demo-cpln-organization/group/superusers |manage | |view |/org/demo-cpln-organization/serviceaccount/controlplane |manage | +----------------------+--------------------------------------------------------+--------+ ``` *** #### gvc add-location Add one or more locations to the referenced global virtual cloud **Usage** ```none theme={null} cpln gvc add-location --location {aws-eu-central-1|aws-us-west-2|azure-eastus2|gcp-us-east1} [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --location Location to add [required] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) The gvc, named 'demo-gvc', in the examples below was already created with 'aws-eu-central-1' as one of its locations. * Add one location to a global virtual cloud ```bash Command theme={null} cpln gvc add-location demo-gvc --location aws-us-west-2 --org demo-cpln-organization ``` ```text Output theme={null} +---------+------------+--------------+--------------+-------+-----------------+--------------+ |NAME |DESCRIPTION |CREATED |ALIAS |DOMAIN |LOCATIONS |TAGS | |---------|------------|--------------|--------------|-------|-----------------|--------------| |demo-gvc |demo-gvc |2 minutes ago |sedj919s8jjzr | |aws-eu-central-1 |drink="water" | | | | | | |aws-us-west-2 | | +---------+------------+--------------+--------------+-------+-----------------+--------------+ ``` * Add multiple locations to a global virtual cloud ```bash Command theme={null} cpln gvc add-location demo-gvc --location aws-us-west-2 --location gcp-us-east1 --org demo-cpln-organization ``` ```text Output theme={null} +---------+------------+--------------+--------------+-------+-----------------+--------------+ |NAME |DESCRIPTION |CREATED |ALIAS |DOMAIN |LOCATIONS |TAGS | |---------|------------|--------------|--------------|-------|-----------------|--------------| |demo-gvc |demo-gvc |2 minutes ago |sedj919s8jjzr | |aws-eu-central-1 |drink="water" | | | | | | |aws-us-west-2 | | | | | | | |gcp-us-east1 | | +---------+------------+--------------+--------------+-------+-----------------+--------------+ ``` *** #### gvc audit Retrieve audit trail events for the referenced global virtual cloud **Usage** ```none theme={null} cpln gvc audit [ref] [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. Command options: --subject Filter by subject (serviceaccount name or user email) [string] --context Audit context name (the 'cpln' context displays Control Plane actions) [string] [default: "cpln"] --since Relative lookback window from now (e.g., 1h, 24h, 7d). Mutually exclusive with --from/--to [string] [default: "7d"] --from Start time for audit events (ISO 8601 format or relative duration, e.g., 2025-10-23T07:00:00Z, 7d, 1h). Cannot be used with --since [string] --to End time for audit events (ISO 8601 format or relative duration, e.g., 2025-10-23T07:00:00Z, 1d, 30m) [string] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### gvc clone Create a clone of the referenced global virtual cloud; this will only duplicate its spec. **Usage** ```none theme={null} cpln gvc clone --name CLONE_NAME [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --name Set the name for the clone [required] --description Optional description, defaults to the name if not set --tag Attach tags (e.g., --tag drink=water) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln gvc clone demo-gvc --name demo-gvc-clone --org demo-cpln-organization ``` ```text Output theme={null} +---------------+------------------+--------+--------------+-------+-----------------+--------------+ |NAME |DESCRIPTION |CREATED |ALIAS |DOMAIN |LOCATIONS |TAGS | |---------------|------------------|--------|--------------|-------|-----------------|--------------| |demo-gvc-clone |Clone of demo-gvc |Now |g81szqqhfhkep | |aws-eu-central-1 |drink="water" | | | | | | |aws-us-west-2 | | | | | | | |gcp-us-east1 | | +---------------+------------------+--------+--------------+-------+-----------------+--------------+ ``` *** #### gvc create Create a new global virtual cloud **Usage** ```none theme={null} cpln gvc create --name GVC_NAME --location {aws-eu-central-1|aws-us-west-2|azure-eastus2|gcp-us-east1} [OPTIONS] ``` ```text theme={null} Command options: --name Name of the new global virtual cloud [required] --description, --desc Optional description, defaults to the name if not set --location One or more locations to associate with this new global virtual cloud [required] --env Environment variables in KEY=VALUE format --tag Attach tags (e.g., --tag drink=water) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Query spec options: --query-match Query filter type [choices: "all", "none", "any"] [default: "all"] --query-property Property to query --query-tag Tag to query --query-rel Relation to query Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * `name` * Minimum Length: 3 * Maximum Length: 64 * Only lowercase characters * Must begin with a character * Allowed special characters: - * Create an new GVC with a single location ```bash Command theme={null} cpln gvc create --name demo-gvc --location aws-eu-central-1 --desc demo-gvc --tag drink=water --org demo-cpln-organization ``` ```text Output theme={null} +---------+------------+--------+--------------+-------+-----------------+--------------+ |NAME |DESCRIPTION |CREATED |ALIAS |DOMAIN |LOCATIONS |TAGS | |---------|------------|--------|--------------|-------|-----------------|--------------| |demo-gvc |demo-gvc |Now |sedj919s8jjzr | |aws-eu-central-1 |drink="water" | +---------+------------+--------+--------------+-------+-----------------+--------------+ ``` *** #### gvc delete Delete GVCs by name **Usage** ```none theme={null} cpln gvc delete [OPTIONS] ``` Deleting a gvc will remove all associated resources (workloads, etc.) ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [required] [default: []] Command options: --force Bypass warnings and/or safety checks [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Delete a gvc ```bash Command theme={null} cpln gvc delete demo-gvc-delete-1 --org demo-cpln-organization ``` ```text Output theme={null} If the delete was successful, a new command prompt will be shown. ``` * Delete multiple gvcs ```bash Command theme={null} cpln gvc delete demo-gvc-delete-1 demo-gvc-delete-2 --org demo-cpln-organization ``` ```text Output theme={null} If the delete was successful, a new command prompt will be shown. ``` *** #### gvc delete-all-workloads Delete all workloads for the referenced global virtual cloud **Usage** ```none theme={null} cpln gvc delete-all-workloads [OPTIONS] ``` Deleting all the associated workloads will remove all associated resources (containers, etc.) ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Delete all workloads ```bash Command theme={null} cpln gvc delete-all-workloads demo-gvc-with-workloads --org demo-cpln-organization ``` ```text Output theme={null} +------------+------------+------------------------+------+--------+-----------------------------------------------------------+-----+ |NAME |DESCRIPTION |GVC |READY |CREATED |ENDPOINT |TAGS | |------------|------------|------------------------|------|--------|-----------------------------------------------------------|-----| |workload-01 |workload-01 |demo-gvc-with-workloads | |Now |https://workload-01-fspqqt1wtnxp4.geta3p46gfrsp.t.cpln.app | | |workload-02 |workload-02 |demo-gvc-with-workloads | |Now |https://workload-02-fspqqt1wtnxp4.geta3p46gfrsp.t.cpln.app | | +------------+------------+------------------------+------+--------+-----------------------------------------------------------+-----+ ``` *** #### gvc edit Edit the referenced global virtual cloud, as YAML, within an editor **Usage** ```none theme={null} cpln gvc edit [OPTIONS] ``` The default editor can be set by using the EDITOR environment variable. ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --replace, -r Replace instead of patch/merge [boolean] [default: false] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln gvc edit demo-gvc --org demo-cpln-organization ``` ```text Output theme={null} Your default editor will be launched. Once any modifications are saved, the updates will be sent to the API. ``` *** #### gvc eventlog Show the event log of the referenced global virtual cloud **Usage** ```none theme={null} cpln gvc eventlog [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln gvc eventlog demo-gvc --org demo-cpln-organization ``` ```text Output theme={null} The event log for the referenced gvc will be shown. ``` *** #### gvc get Retrieve one or more referenced global virtual clouds **Usage** ```none theme={null} cpln gvc get [ref...] [OPTIONS] ``` An empty reference will return all references ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [default: []] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Get all gvcs ```bash Command theme={null} cpln gvc get --org demo-cpln-organization ``` ```text Output theme={null} +------------------------+------------------------+--------------+--------------+-------+-----------------+--------------+ |NAME |DESCRIPTION |CREATED |ALIAS |DOMAIN |LOCATIONS |TAGS | |------------------------|------------------------|--------------|--------------|-------|-----------------|--------------| |demo-gvc |demo-gvc |2 minutes ago |sedj919s8jjzr | |aws-eu-central-1 |drink="water" | | | | | | |aws-us-west-2 | | | | | | | |gcp-us-east1 | | |demo-gvc-get-1 |demo-gvc-get-1 |Now |zjbsgjakyckhw | |aws-eu-central-1 | | |demo-gvc-get-2 |demo-gvc-get-2 |Now |ggt9sj3j147dc | |aws-eu-central-1 | | |demo-gvc-identity |demo-gvc-identity |2 minutes ago |b9acy9sz9a65r | |aws-eu-central-1 | | |demo-gvc-with-workloads |demo-gvc-with-workloads |Now |fspqqt1wtnxp4 | |aws-eu-central-1 | | |demo-gvc-workload |demo-gvc-workload |2 minutes ago |2x5qqmrp3zzt6 | |aws-eu-central-1 | | +------------------------+------------------------+--------------+--------------+-------+-----------------+--------------+ ``` * Get one gvc ```bash Command theme={null} cpln gvc get demo-gvc-get-1 --org demo-cpln-organization ``` ```text Output theme={null} +---------------+---------------+--------+--------------+-------+-----------------+-----+ |NAME |DESCRIPTION |CREATED |ALIAS |DOMAIN |LOCATIONS |TAGS | |---------------|---------------|--------|--------------|-------|-----------------|-----| |demo-gvc-get-1 |demo-gvc-get-1 |Now |zjbsgjakyckhw | |aws-eu-central-1 | | +---------------+---------------+--------+--------------+-------+-----------------+-----+ ``` * Get multiple gvcs ```bash Command theme={null} cpln gvc get demo-gvc-get-1 demo-gvc-get-2 --org demo-cpln-organization ``` ```text Output theme={null} +---------------+---------------+--------+--------------+-------+-----------------+-----+ |NAME |DESCRIPTION |CREATED |ALIAS |DOMAIN |LOCATIONS |TAGS | |---------------|---------------|--------|--------------|-------|-----------------|-----| |demo-gvc-get-1 |demo-gvc-get-1 |Now |zjbsgjakyckhw | |aws-eu-central-1 | | |demo-gvc-get-2 |demo-gvc-get-2 |Now |ggt9sj3j147dc | |aws-eu-central-1 | | +---------------+---------------+--------+--------------+-------+-----------------+-----+ ``` *** #### gvc patch Update the referenced global virtual cloud's metadata using an input file **Usage** ```none theme={null} cpln gvc patch --file FILE_NAME [OPTIONS] ``` Running this command with the option **`--file -`** sets the input to stdin. The input is required to be a valid JSON or YAML string (e.g., `{"description": "My new description"}` or `description: My new description`). To complete the update, press Control-D (or Control-Z if you are using Windows). ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --file, -f File to load the patch from. Use `--file -` to enable input from stdin. [required] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln gvc patch demo-gvc --file update.json --org demo-cpln-organization ``` ```text Output theme={null} The changes within the JSON file will be performed against the referenced gvc. ``` *** #### gvc permissions Show the grantable permissions for a global virtual cloud object type **Usage** ```none theme={null} cpln gvc permissions [OPTIONS] ``` ```text theme={null} Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln gvc permissions --org demo-cpln-organization ``` ```text Output theme={null} +----------------------+---------------------------+----------------------------------------------------------+ |NAME |DESCRIPTION |IMPLIES | |----------------------|---------------------------|----------------------------------------------------------| |configureLoadBalancer |Principal can configure LB | | |create |Create new gvcs | | |delete |Delete existing gvcs | | |edit |Modify existing gvcs |view | |manage |Full access |configureLoadBalancer, create, delete, edit, manage, view | |view |Read-only access | | +----------------------+---------------------------+----------------------------------------------------------+ ``` *** #### gvc query Find all the global virtual clouds based on the given query **Usage** ```none theme={null} cpln gvc query QUERY_OPTIONS [OPTIONS] ``` ```text theme={null} Query options: --match Query filter type [choices: "all", "none", "any"] [default: "all"] --property, --prop Property to query --tag Tag to query --rel Relation to query Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) Use the `--match` option to display **all**, **none**, or **any** of the results based on a given property or tag * Search for a gvc based on the name property ```bash Command theme={null} cpln gvc query --match any --prop name=demo-gvc --org demo-cpln-organization ``` ```text Output theme={null} +---------+------------+--------------+--------------+-------+-----------------+--------------+ |NAME |DESCRIPTION |CREATED |ALIAS |DOMAIN |LOCATIONS |TAGS | |---------|------------|--------------|--------------|-------|-----------------|--------------| |demo-gvc |demo-gvc |2 minutes ago |sedj919s8jjzr | |aws-eu-central-1 |drink="water" | | | | | | |aws-us-west-2 | | | | | | | |gcp-us-east1 | | +---------+------------+--------------+--------------+-------+-----------------+--------------+ ``` * Search for a gvc based on the description property ```bash Command theme={null} cpln gvc query --match any --prop description="demo-gvc" --org demo-cpln-organization ``` ```text Output theme={null} +---------+------------+--------------+--------------+-------+-----------------+--------------+ |NAME |DESCRIPTION |CREATED |ALIAS |DOMAIN |LOCATIONS |TAGS | |---------|------------|--------------|--------------|-------|-----------------|--------------| |demo-gvc |demo-gvc |2 minutes ago |sedj919s8jjzr | |aws-eu-central-1 |drink="water" | | | | | | |aws-us-west-2 | | | | | | | |gcp-us-east1 | | +---------+------------+--------------+--------------+-------+-----------------+--------------+ ``` * Search for a gvc based on a tag ```bash Command theme={null} cpln gvc query --match any --tag drink=water --org demo-cpln-organization ``` ```text Output theme={null} +---------+------------+--------------+--------------+-------+-----------------+--------------+ |NAME |DESCRIPTION |CREATED |ALIAS |DOMAIN |LOCATIONS |TAGS | |---------|------------|--------------|--------------|-------|-----------------|--------------| |demo-gvc |demo-gvc |2 minutes ago |sedj919s8jjzr | |aws-eu-central-1 |drink="water" | | | | | | |aws-us-west-2 | | | | | | | |gcp-us-east1 | | +---------+------------+--------------+--------------+-------+-----------------+--------------+ ``` * Search for a gvc based on a property and tag ```bash Command theme={null} cpln gvc query --match any --prop description="demo-gvc" --tag drink=water --org demo-cpln-organization ``` ```text Output theme={null} +---------+------------+--------------+--------------+-------+-----------------+--------------+ |NAME |DESCRIPTION |CREATED |ALIAS |DOMAIN |LOCATIONS |TAGS | |---------|------------|--------------|--------------|-------|-----------------|--------------| |demo-gvc |demo-gvc |2 minutes ago |sedj919s8jjzr | |aws-eu-central-1 |drink="water" | | | | | | |aws-us-west-2 | | | | | | | |gcp-us-east1 | | +---------+------------+--------------+--------------+-------+-----------------+--------------+ ``` *** #### gvc remove-location Remove one or more locations from the referenced global virtual cloud **Usage** ```none theme={null} cpln gvc remove-location --location {aws-eu-central-1|aws-us-west-2|azure-eastus2|gcp-us-east1} [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --location Location to remove [required] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) The gvc, named 'demo-gvc-with-locations', in the examples below was already created with four locations. A gvc must have at least one location. * Remove one location from a global virtual cloud ```bash Command theme={null} cpln gvc remove-location demo-gvc-with-locations --location azure-eastus2 --org demo-cpln-organization ``` ```text Output theme={null} +------------------------+------------------------+--------+--------------+-------+-----------------+-----+ |NAME |DESCRIPTION |CREATED |ALIAS |DOMAIN |LOCATIONS |TAGS | |------------------------|------------------------|--------|--------------|-------|-----------------|-----| |demo-gvc-with-locations |demo-gvc-with-locations |Now |f9mpjzw6wxqmr | |aws-eu-central-1 | | | | | | | |aws-us-west-2 | | | | | | | |gcp-us-east1 | | +------------------------+------------------------+--------+--------------+-------+-----------------+-----+ ``` * Remove multiple locations from a global virtual cloud ```bash Command theme={null} cpln gvc remove-location demo-gvc-with-locations --location aws-us-west-2 --location aws-eu-central-1 --org demo-cpln-organization ``` ```text Output theme={null} +------------------------+------------------------+--------+--------------+-------+-------------+-----+ |NAME |DESCRIPTION |CREATED |ALIAS |DOMAIN |LOCATIONS |TAGS | |------------------------|------------------------|--------|--------------|-------|-------------|-----| |demo-gvc-with-locations |demo-gvc-with-locations |Now |f9mpjzw6wxqmr | |gcp-us-east1 | | +------------------------+------------------------+--------+--------------+-------+-------------+-----+ ``` *** #### gvc tag Manage the tags belonging to one or more referenced global virtual clouds **Usage** ```none theme={null} cpln gvc tag --tag KEY=VALUE [OPTIONS] ``` Built-in tags, which begin with cpln/\*, can't be modified. ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [required] [default: []] Command options: --tag Attach tags (e.g., --tag drink=water) --remove-tag Remove tags (e.g., --remove-tag tagname) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Add a tag to a gvc ```bash Command theme={null} cpln gvc tag demo-gvc-tag-1 --tag drink=water --org demo-cpln-organization ``` ```text Output theme={null} +---------------+---------------+--------+--------------+-------+-----------------+--------------+ |NAME |DESCRIPTION |CREATED |ALIAS |DOMAIN |LOCATIONS |TAGS | |---------------|---------------|--------|--------------|-------|-----------------|--------------| |demo-gvc-tag-1 |demo-gvc-tag-1 |Now |mdvcnz97by71w | |aws-eu-central-1 |drink="water" | +---------------+---------------+--------+--------------+-------+-----------------+--------------+ ``` * Update same tag (after running the example above) ```bash Command theme={null} cpln gvc tag demo-gvc-tag-1 --tag drink=coffee --org demo-cpln-organization ``` ```text Output theme={null} +---------------+---------------+--------+--------------+-------+-----------------+---------------+ |NAME |DESCRIPTION |CREATED |ALIAS |DOMAIN |LOCATIONS |TAGS | |---------------|---------------|--------|--------------|-------|-----------------|---------------| |demo-gvc-tag-1 |demo-gvc-tag-1 |Now |mdvcnz97by71w | |aws-eu-central-1 |drink="coffee" | +---------------+---------------+--------+--------------+-------+-----------------+---------------+ ``` * Remove tag (after running the example above) ```bash Command theme={null} cpln gvc tag demo-gvc-tag-1 --remove drink --org demo-cpln-organization ``` ```text Output theme={null} +---------------+---------------+--------+--------------+-------+-----------------+-----+ |NAME |DESCRIPTION |CREATED |ALIAS |DOMAIN |LOCATIONS |TAGS | |---------------|---------------|--------|--------------|-------|-----------------|-----| |demo-gvc-tag-1 |demo-gvc-tag-1 |Now |mdvcnz97by71w | |aws-eu-central-1 | | +---------------+---------------+--------+--------------+-------+-----------------+-----+ ``` * Add same tag to multiple gvcs ```bash Command theme={null} cpln gvc tag demo-gvc-tag-1 demo-gvc-tag-2 --tag multiple-gvcs=true --org demo-cpln-organization ``` ```text Output theme={null} +---------------+---------------+--------+--------------+-------+-----------------+---------------------+ |NAME |DESCRIPTION |CREATED |ALIAS |DOMAIN |LOCATIONS |TAGS | |---------------|---------------|--------|--------------|-------|-----------------|---------------------| |demo-gvc-tag-1 |demo-gvc-tag-1 |Now |mdvcnz97by71w | |aws-eu-central-1 |multiple-gvcs="true" | |demo-gvc-tag-2 |demo-gvc-tag-2 |Now |qxbmnc6ssqg14 | |aws-eu-central-1 |multiple-gvcs="true" | +---------------+---------------+--------+--------------+-------+-----------------+---------------------+ ``` * Update same tag on multiple gvcs (after running the example above) ```bash Command theme={null} cpln gvc tag demo-gvc-tag-1 demo-gvc-tag-2 --tag multiple-gvcs=demo-gvc --org demo-cpln-organization ``` ```text Output theme={null} +---------------+---------------+--------+--------------+-------+-----------------+-------------------------+ |NAME |DESCRIPTION |CREATED |ALIAS |DOMAIN |LOCATIONS |TAGS | |---------------|---------------|--------|--------------|-------|-----------------|-------------------------| |demo-gvc-tag-1 |demo-gvc-tag-1 |Now |mdvcnz97by71w | |aws-eu-central-1 |multiple-gvcs="demo-gvc" | |demo-gvc-tag-2 |demo-gvc-tag-2 |Now |qxbmnc6ssqg14 | |aws-eu-central-1 |multiple-gvcs="demo-gvc" | +---------------+---------------+--------+--------------+-------+-----------------+-------------------------+ ``` * Remove same tag from multiple gvcs (after running the example above) ```bash Command theme={null} cpln gvc tag demo-gvc-tag-1 demo-gvc-tag-2 --remove multiple-gvcs --org demo-cpln-organization ``` ```text Output theme={null} +---------------+---------------+--------+--------------+-------+-----------------+-----+ |NAME |DESCRIPTION |CREATED |ALIAS |DOMAIN |LOCATIONS |TAGS | |---------------|---------------|--------|--------------|-------|-----------------|-----| |demo-gvc-tag-1 |demo-gvc-tag-1 |Now |mdvcnz97by71w | |aws-eu-central-1 | | |demo-gvc-tag-2 |demo-gvc-tag-2 |Now |qxbmnc6ssqg14 | |aws-eu-central-1 | | +---------------+---------------+--------+--------------+-------+-----------------+-----+ ``` *** #### gvc update Update properties of the referenced global virtual cloud **Usage** ```none theme={null} cpln gvc update --set UPDATE_PROPERTY [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Update Properties: --set Update the following properties (e.g., --set description="Updated Description"): description string tags. string spec.pullSecretLinks string[] spec.staticPlacement.locationLinks string[] spec.tracing.sampling number spec.tracing.lightstep.endpoint string spec.tracing.lightstep.credentials string[] spec.env..value string Arrays can be appended to, replaced, or removed using the `+=`, `=`, or `-=` operators, respectively. --unset Unset a property (same paths as --set, e.g., --unset description) Command options: --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * The `--set` option allows the modification of the following global virtual cloud properties: * `description` * Description of the global virtual cloud * Type: string * `tags` * Tags (key=value) associated with the global virtual cloud * Type: string * `spec.pullSecretLinks` * Array of pull secret links belonging to the global virtual cloud * Type: string\[] * `spec.domain` * The domain linked to this global virtual cloud * Type: string * `spec.staticPlacement.locationLinks` * Array of location links belonging to the global virtual cloud * Type: string\[] * For the examples below, the initial gvc is configured as: ``` +----------------+----------------+--------+--------------+-------+-----------------+-----+ |NAME |DESCRIPTION |CREATED |ALIAS |DOMAIN |LOCATIONS |TAGS | |----------------|----------------|--------|--------------|-------|-----------------|-----| |demo-gvc-update |demo-gvc-update |Now |w0f18k5v8beap | |aws-eu-central-1 | | +----------------+----------------+--------+--------------+-------+-----------------+-----+ ``` * Update a gvc's description ```bash Command theme={null} cpln gvc update demo-gvc-update --set description="new description" --org demo-cpln-organization ``` ```text Output theme={null} +----------------+----------------+--------+--------------+-------+-----------------+-----+ |NAME |DESCRIPTION |CREATED |ALIAS |DOMAIN |LOCATIONS |TAGS | |----------------|----------------|--------|--------------|-------|-----------------|-----| |demo-gvc-update |new description |Now |w0f18k5v8beap | |aws-eu-central-1 | | +----------------+----------------+--------+--------------+-------+-----------------+-----+ ``` * Update a gvc's tag ```bash Command theme={null} cpln gvc update demo-gvc-update --set tags.demo-key=demo-value --org demo-cpln-organization ``` ```text Output theme={null} +----------------+----------------+--------+--------------+-------+-----------------+----------------------+ |NAME |DESCRIPTION |CREATED |ALIAS |DOMAIN |LOCATIONS |TAGS | |----------------|----------------|--------|--------------|-------|-----------------|----------------------| |demo-gvc-update |new description |Now |w0f18k5v8beap | |aws-eu-central-1 |demo-key="demo-value" | +----------------+----------------+--------+--------------+-------+-----------------+----------------------+ ``` *** # helm Source: https://docs.controlplane.com/cli-reference/commands/helm cpln helm — Install and manage Helm chart releases deployed on Control Plane. Manage helm releases on cpln Refer to the [cpln helm guide](/guides/cpln-helm) for additional details. ```none theme={null} cpln helm [SUBCOMMAND] ``` ```text theme={null} Commands: cpln helm get Download extended information of a named release cpln helm history Fetch release history cpln helm install [release] [chart] Install a release [aliases: apply] cpln helm list List releases cpln helm rollback [revision] Roll back a release to a previous revision cpln helm template [release] [chart] Generate cpln resources from a template cpln helm uninstall Uninstall a release [aliases: destroy, del, delete, un] cpln helm upgrade [release] [chart] Upgrade a release Options: --help Show help [boolean] ``` *** #### helm get Download extended information of a named release ```none theme={null} cpln helm get [SUBCOMMAND] ``` An empty reference will return all references ```text theme={null} Commands: cpln helm get all Download all information for a named release cpln helm get manifest Download the manifest for a named release cpln helm get notes Download the notes for a named release cpln helm get values Download the values file for a named release Options: --help Show help [boolean] ``` #### helm get all Download all information for a named release **Usage** ```none theme={null} cpln helm get all [OPTIONS] ``` ```text theme={null} Positionals: release The release name [string] [required] Command options: --revision get the named release with revision [number] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### helm get manifest Download the manifest for a named release **Usage** ```none theme={null} cpln helm get manifest [OPTIONS] ``` ```text theme={null} Positionals: release The release name [string] [required] Command options: --revision get the named release with revision [number] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### helm get notes Download the notes for a named release **Usage** ```none theme={null} cpln helm get notes [OPTIONS] ``` ```text theme={null} Positionals: release The release name [string] [required] Command options: --revision get the named release with revision [number] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### helm get values Download the values file for a named release **Usage** ```none theme={null} cpln helm get values [OPTIONS] ``` ```text theme={null} Positionals: release The release name [string] [required] Command options: --all, -a dump all (computed) values [boolean] --revision get the named release with revision [number] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### helm history Fetch release history **Usage** ```none theme={null} cpln helm history [OPTIONS] ``` ```text theme={null} Positionals: release The release name [string] [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### helm install Install a release **Usage** ```none theme={null} cpln helm install [release] [chart] [OPTIONS] ``` ```text theme={null} Positionals: release The release name [string] chart Path to chart [string] Command options: --wait If set, will wait until all Workloads are in a ready state before marking the release as successful. It will wait for as long as --timeout [boolean] --timeout The amount of seconds to wait for workloads to be ready before timing out. Works only if the "wait" option is set to true. [number] [default: 300] --dependency-update Update dependencies if they are missing before installing the chart [boolean] --description, --desc Add a custom description [string] --generate-name, -g Generate the name (and omit the NAME parameter) [boolean] --post-renderer The path to an executable to be used for post rendering. If it exists in $PATH, the binary will be used, otherwise it will try to look for the executable at the given path [string] --post-renderer-args An argument to the post-renderer (can specify multiple or separate values: --post-renderer-args arg1 --post-renderer-args arg2) (default []) [default: []] --repo Chart repository url where to locate the requested chart [string] --set Set values on the command line (can specify multiple or separate values: --set key1=val1 --set key2=val2) --set-string Set STRING values on the command line (can specify multiple or separate values: --set-string key1=val1 --set-string key2=val2) --set-file Set values from respective files specified via the command line (can specify multiple or separate values: --set-file key1=path1 --set-file key2=path2) --values, -f Specify values in a YAML file or a URL (can specify multiple or separate values: --values value1.yaml --values values2.yaml) [string] --verify Verify the package before using it [boolean] --version Specify a version constraint for the chart version to use. This constraint can be a specific tag (e.g. 1.1.1) or it may reference a valid range (e.g. ^2.0.0). If this is not specified, the latest version is used [string] --username Chart repository username where to locate the requested chart [string] --password Chart repository password where to locate the requested chart [string] --ca-file Verify certificates of HTTPS-enabled servers using this CA bundle [string] --cert-file Identify HTTPS client using this SSL certificate file [string] --key-file Identify HTTPS client using this SSL key file [string] --insecure-skip-tls-verify Skip tls certificate checks for the chart download [boolean] --render-subchart-notes If set, render subchart notes along with the parent on install/upgrade [boolean] --tag Attach tags (e.g., --tag drink=water) --remove-tag Remove tags (e.g., --remove-tag tagname) --state-tag Attach tags to the Helm secret (e.g., --state-tag drink=water) --remove-state-tag Remove tags from the Helm secret (e.g., --remove-state-tag tagname) Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### helm list List releases **Usage** ```none theme={null} cpln helm list [OPTIONS] ``` ```text theme={null} Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### helm rollback Roll back a release to a previous revision **Usage** ```none theme={null} cpln helm rollback [revision] [OPTIONS] ``` ```text theme={null} Positionals: release The release name [string] [required] revision Revision (version) number. If this argument is omitted or set to 0, it will roll back to the previous release. To see revision numbers, run 'cpln helm history RELEASE'. [string] Command options: --cleanup-on-fail allow deletion of new resources created in this rollback when rollback fails [boolean] [default: false] --wait If set, will wait until all Workloads are in a ready state before marking the release as successful. It will wait for as long as --timeout [boolean] --timeout The amount of seconds to wait for workloads to be ready before timing out. Works only if the "wait" option is set to true. [number] [default: 300] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### helm template Generate cpln resources from a template **Usage** ```none theme={null} cpln helm template [release] [chart] [OPTIONS] ``` ```text theme={null} Positionals: release The release name [string] chart Path to chart [string] Command options: --dependency-update Update dependencies if they are missing before installing the chart [boolean] --description, --desc Add a custom description [string] --generate-name, -g Generate the name (and omit the NAME parameter) [boolean] --post-renderer The path to an executable to be used for post rendering. If it exists in $PATH, the binary will be used, otherwise it will try to look for the executable at the given path [string] --post-renderer-args An argument to the post-renderer (can specify multiple or separate values: --post-renderer-args arg1 --post-renderer-args arg2) (default []) [default: []] --repo Chart repository url where to locate the requested chart [string] --set Set values on the command line (can specify multiple or separate values: --set key1=val1 --set key2=val2) --set-string Set STRING values on the command line (can specify multiple or separate values: --set-string key1=val1 --set-string key2=val2) --set-file Set values from respective files specified via the command line (can specify multiple or separate values: --set-file key1=path1 --set-file key2=path2) --values, -f Specify values in a YAML file or a URL (can specify multiple or separate values: --values value1.yaml --values values2.yaml) [string] --verify Verify the package before using it [boolean] --version Specify a version constraint for the chart version to use. This constraint can be a specific tag (e.g. 1.1.1) or it may reference a valid range (e.g. ^2.0.0). If this is not specified, the latest version is used [string] --username Chart repository username where to locate the requested chart [string] --password Chart repository password where to locate the requested chart [string] --ca-file Verify certificates of HTTPS-enabled servers using this CA bundle [string] --cert-file Identify HTTPS client using this SSL certificate file [string] --key-file Identify HTTPS client using this SSL key file [string] --insecure-skip-tls-verify Skip tls certificate checks for the chart download [boolean] --render-subchart-notes If set, render subchart notes along with the parent on install/upgrade [boolean] --namespace, -n Specify the Kubernetes namespace for exporting in CRD format (applicable only with '--output crd'). --keep, -k Prevent deletion of the corresponding Control Plane resource (only when using '--output crd'). [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### helm uninstall Uninstall a release **Usage** ```none theme={null} cpln helm uninstall [OPTIONS] ``` ```text theme={null} Positionals: release The release name [string] [required] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k, -k Ignore TLS errors [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### helm upgrade Upgrade a release **Usage** ```none theme={null} cpln helm upgrade [release] [chart] [OPTIONS] ``` ```text theme={null} Positionals: release The release name [string] chart Path to chart [string] Command options: --wait If set, will wait until all Workloads are in a ready state before marking the release as successful. It will wait for as long as --timeout [boolean] --timeout The amount of seconds to wait for workloads to be ready before timing out. Works only if the "wait" option is set to true. [number] [default: 300] --history-limit Maximum number of revisions saved per release. Use 0 for no limit [number] [default: 10] --install If a release by this name doesn't already exist, run an install [boolean] --dependency-update Update dependencies if they are missing before installing the chart [boolean] --description, --desc Add a custom description [string] --generate-name, -g Generate the name (and omit the NAME parameter) [boolean] --post-renderer The path to an executable to be used for post rendering. If it exists in $PATH, the binary will be used, otherwise it will try to look for the executable at the given path [string] --post-renderer-args An argument to the post-renderer (can specify multiple or separate values: --post-renderer-args arg1 --post-renderer-args arg2) (default []) [default: []] --repo Chart repository url where to locate the requested chart [string] --set Set values on the command line (can specify multiple or separate values: --set key1=val1 --set key2=val2) --set-string Set STRING values on the command line (can specify multiple or separate values: --set-string key1=val1 --set-string key2=val2) --set-file Set values from respective files specified via the command line (can specify multiple or separate values: --set-file key1=path1 --set-file key2=path2) --values, -f Specify values in a YAML file or a URL (can specify multiple or separate values: --values value1.yaml --values values2.yaml) [string] --verify Verify the package before using it [boolean] --version Specify a version constraint for the chart version to use. This constraint can be a specific tag (e.g. 1.1.1) or it may reference a valid range (e.g. ^2.0.0). If this is not specified, the latest version is used [string] --username Chart repository username where to locate the requested chart [string] --password Chart repository password where to locate the requested chart [string] --ca-file Verify certificates of HTTPS-enabled servers using this CA bundle [string] --cert-file Identify HTTPS client using this SSL certificate file [string] --key-file Identify HTTPS client using this SSL key file [string] --insecure-skip-tls-verify Skip tls certificate checks for the chart download [boolean] --render-subchart-notes If set, render subchart notes along with the parent on install/upgrade [boolean] --tag Attach tags (e.g., --tag drink=water) --remove-tag Remove tags (e.g., --remove-tag tagname) --state-tag Attach tags to the Helm secret (e.g., --state-tag drink=water) --remove-state-tag Remove tags from the Helm secret (e.g., --remove-state-tag tagname) Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** # identity Source: https://docs.controlplane.com/cli-reference/commands/identity cpln identity — Manage identities within a GVC to grant workloads access to cloud resources and secrets. Manage identities within a global virtual cloud For a detailed overview, please visit the [identity's](/reference/identity) reference page. ```none theme={null} cpln identity [SUBCOMMAND] ``` ```text theme={null} Commands: cpln identity access-report Show the access report for the referenced identity cpln identity audit [ref] Retrieve audit trail events for the referenced identity cpln identity create Create a new identity cpln identity delete Delete one or more referenced identities cpln identity edit Edit the referenced identity, as YAML, within an editor cpln identity eventlog Show the event log of the referenced identity [aliases: log] cpln identity get [ref...] Retrieve one or more referenced identities cpln identity patch Update the referenced identity's metadata using an input file cpln identity permissions Show the grantable permissions for an identity object type cpln identity query Find all the identities based on the given query cpln identity tag Manage the tags belonging to one or more referenced identities cpln identity update Update properties of the referenced identity Options: --help Show help [boolean] ``` *** #### identity access-report Show the access report for the referenced identity **Usage** ```none theme={null} cpln identity access-report [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln identity access-report demo-identity --org demo-cpln-organization --gvc demo-gvc-identity ``` ```text Output theme={null} +-----------+--------------------------------------------------------+--------+ |PERMISSION |PRINCIPAL |GRANTED | |-----------|--------------------------------------------------------|--------| |create |/org/demo-cpln-organization/group/superusers |manage | |create |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |delete |/org/demo-cpln-organization/group/superusers |manage | |delete |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |edit |/org/demo-cpln-organization/group/superusers |manage | |edit |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |manage |/org/demo-cpln-organization/group/superusers |manage | |manage |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |use |/org/demo-cpln-organization/group/superusers |manage | |use |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |view |/org/demo-cpln-organization/group/superusers |manage | |view |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |view |/org/demo-cpln-organization/group/viewers |view | +-----------+--------------------------------------------------------+--------+ ``` *** #### identity audit Retrieve audit trail events for the referenced identity **Usage** ```none theme={null} cpln identity audit [ref] [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. Command options: --subject Filter by subject (serviceaccount name or user email) [string] --context Audit context name (the 'cpln' context displays Control Plane actions) [string] [default: "cpln"] --since Relative lookback window from now (e.g., 1h, 24h, 7d). Mutually exclusive with --from/--to [string] [default: "7d"] --from Start time for audit events (ISO 8601 format or relative duration, e.g., 2025-10-23T07:00:00Z, 7d, 1h). Cannot be used with --since [string] --to End time for audit events (ISO 8601 format or relative duration, e.g., 2025-10-23T07:00:00Z, 1d, 30m) [string] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### identity create Create a new identity **Usage** ```none theme={null} cpln identity create --name IDENTITY_NAME [OPTIONS] ``` ```text theme={null} Command options: --name Name of the new identity [required] --description, --desc Optional description, defaults to the name if not set --tag Attach tags (e.g., --tag drink=water) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln identity create --name demo-identity --desc demo-identity --tag drink=water --org demo-cpln-organization --gvc demo-gvc-identity ``` ```text Output theme={null} +--------------+--------------+------------------+-------------------------------+--------+----------+--------------+ |NAME |DESCRIPTION |GVC |LOCAL NAME |CREATED |PROVIDERS |TAGS | |--------------|--------------|------------------|-------------------------------|--------|----------|--------------| |demo-identity |demo-identity |demo-gvc-identity |cpln-demo-cpln-organi-3h7v8edk |Now | |drink="water" | +--------------+--------------+------------------+-------------------------------+--------+----------+--------------+ ``` *** #### identity delete Delete one or more referenced identities **Usage** ```none theme={null} cpln identity delete [OPTIONS] ``` ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [required] [default: []] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln identity delete demo-identity --org demo-cpln-organization --gvc demo-gvc-identity ``` ```text Output theme={null} If the delete was successful, a new command prompt will be shown. ``` *** #### identity edit Edit the referenced identity, as YAML, within an editor **Usage** ```none theme={null} cpln identity edit [OPTIONS] ``` The default editor can be set by using the EDITOR environment variable. ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --replace, -r Replace instead of patch/merge [boolean] [default: false] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln identity edit demo-identity --org demo-cpln-organization --gvc demo-gvc-identity ``` ```text Output theme={null} Your default editor will be launched. Once any modifications are saved, the updates will be sent to the API. ``` *** #### identity eventlog Show the event log of the referenced identity **Usage** ```none theme={null} cpln identity eventlog [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln identity eventlog demo-identity --org demo-cpln-organization --gvc demo-gvc-identity ``` ```text Output theme={null} The event log for the referenced identity will be shown. ``` *** #### identity get Retrieve one or more referenced identities **Usage** ```none theme={null} cpln identity get [ref...] [OPTIONS] ``` An empty reference will return all references ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [default: []] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Command options: --all-gvcs Show identities from all gvcs in the org [boolean] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Get all identities ```bash Command theme={null} cpln identity get --org demo-cpln-organization --gvc demo-gvc-identity ``` ```text Output theme={null} +--------------------+--------------------+------------------+-------------------------------+--------------+----------+--------------+ |NAME |DESCRIPTION |GVC |LOCAL NAME |CREATED |PROVIDERS |TAGS | |--------------------|--------------------|------------------|-------------------------------|--------------|----------|--------------| |demo-identity |demo-identity |demo-gvc-identity |cpln-demo-cpln-organi-3h7v8edk |2 minutes ago | |drink="water" | |demo-identity-get-2 |demo-identity-get-2 |demo-gvc-identity |cpln-demo-cpln-organi-1k08e34y |Now | | | |demo-identity-get-1 |demo-identity-get-1 |demo-gvc-identity |cpln-demo-cpln-organi-e2j3eqrz |Now | | | +--------------------+--------------------+------------------+-------------------------------+--------------+----------+--------------+ ``` * Get one identity ```bash Command theme={null} cpln identity get demo-identity-get-1 --org demo-cpln-organization --gvc demo-gvc-identity ``` ```text Output theme={null} +--------------------+--------------------+------------------+-------------------------------+--------+----------+-----+ |NAME |DESCRIPTION |GVC |LOCAL NAME |CREATED |PROVIDERS |TAGS | |--------------------|--------------------|------------------|-------------------------------|--------|----------|-----| |demo-identity-get-1 |demo-identity-get-1 |demo-gvc-identity |cpln-demo-cpln-organi-e2j3eqrz |Now | | | +--------------------+--------------------+------------------+-------------------------------+--------+----------+-----+ ``` * Get multiple identities ```bash Command theme={null} cpln identity get demo-identity-get-1 demo-identity-get-2 --org demo-cpln-organization --gvc demo-gvc-identity ``` ```text Output theme={null} +--------------------+--------------------+------------------+-------------------------------+--------+----------+-----+ |NAME |DESCRIPTION |GVC |LOCAL NAME |CREATED |PROVIDERS |TAGS | |--------------------|--------------------|------------------|-------------------------------|--------|----------|-----| |demo-identity-get-1 |demo-identity-get-1 |demo-gvc-identity |cpln-demo-cpln-organi-e2j3eqrz |Now | | | |demo-identity-get-2 |demo-identity-get-2 |demo-gvc-identity |cpln-demo-cpln-organi-1k08e34y |Now | | | +--------------------+--------------------+------------------+-------------------------------+--------+----------+-----+ ``` *** #### identity patch Update the referenced identity's metadata using an input file **Usage** ```none theme={null} cpln identity patch --file FILE_NAME [OPTIONS] ``` Running this command with the option **`--file -`** sets the input to stdin. The input is required to be a valid JSON or YAML string (e.g., `{"description": "My new description"}` or `description: My new description`). To complete the update, press Control-D (or Control-Z if you are using Windows). ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --file, -f File to load the patch from. Use `--file -` to enable input from stdin. [required] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln identity patch demo-identity --file update.json --org demo-cpln-organization --gvc demo-gvc-identity ``` ```text Output theme={null} The changes within the JSON file will be performed against the referenced identity. ``` *** #### identity permissions Show the grantable permissions for an identity object type **Usage** ```none theme={null} cpln identity permissions [OPTIONS] ``` ```text theme={null} Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln identity permissions --org demo-cpln-organization --gvc demo-gvc-identity ``` ```text Output theme={null} +-------+-----------------------------------------+----------------------------------------+ |NAME |DESCRIPTION |IMPLIES | |-------|-----------------------------------------|----------------------------------------| |create |Create new identities | | |delete |Delete existing identities | | |edit |Modify existing identities |view | |manage |Full access |create, delete, edit, manage, use, view | |use |Refer to this identity from other ent... |view | |view |Read-only access | | +-------+-----------------------------------------+----------------------------------------+ ``` *** #### identity query Find all the identities based on the given query **Usage** ```none theme={null} cpln identity query QUERY_OPTIONS [OPTIONS] ``` ```text theme={null} Query options: --match Query filter type [choices: "all", "none", "any"] [default: "all"] --property, --prop Property to query --tag Tag to query --rel Relation to query Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) Use the `--match` option to display **all**, **none**, or **any** of the results based on a given property or tag * Search for an identity based on the name property ```bash Command theme={null} cpln identity query --match any --prop name=demo-identity --org demo-cpln-organization --gvc demo-gvc-identity ``` ```text Output theme={null} +--------------------+--------------------+------------------+-------------------------------+--------------+----------+--------------+ |NAME |DESCRIPTION |GVC |LOCAL NAME |CREATED |PROVIDERS |TAGS | |--------------------|--------------------|------------------|-------------------------------|--------------|----------|--------------| |demo-identity |demo-identity |demo-gvc-identity |cpln-demo-cpln-organi-3h7v8edk |2 minutes ago | |drink="water" | |demo-identity-get-2 |demo-identity-get-2 |demo-gvc-identity |cpln-demo-cpln-organi-1k08e34y |Now | | | |demo-identity-get-1 |demo-identity-get-1 |demo-gvc-identity |cpln-demo-cpln-organi-e2j3eqrz |Now | | | +--------------------+--------------------+------------------+-------------------------------+--------------+----------+--------------+ ``` * Search for an identity based on the description property ```bash Command theme={null} cpln identity query --match any --prop description="demo-identity" --org demo-cpln-organization --gvc demo-gvc-identity ``` ```text Output theme={null} +--------------------+--------------------+------------------+-------------------------------+--------------+----------+--------------+ |NAME |DESCRIPTION |GVC |LOCAL NAME |CREATED |PROVIDERS |TAGS | |--------------------|--------------------|------------------|-------------------------------|--------------|----------|--------------| |demo-identity |demo-identity |demo-gvc-identity |cpln-demo-cpln-organi-3h7v8edk |2 minutes ago | |drink="water" | |demo-identity-get-2 |demo-identity-get-2 |demo-gvc-identity |cpln-demo-cpln-organi-1k08e34y |Now | | | |demo-identity-get-1 |demo-identity-get-1 |demo-gvc-identity |cpln-demo-cpln-organi-e2j3eqrz |Now | | | +--------------------+--------------------+------------------+-------------------------------+--------------+----------+--------------+ ``` * Search for an identity based on a tag ```bash Command theme={null} cpln identity query --match any --tag drink=water --org demo-cpln-organization --gvc demo-gvc-identity ``` ```text Output theme={null} +--------------------+--------------------+------------------+-------------------------------+--------------+----------+--------------+ |NAME |DESCRIPTION |GVC |LOCAL NAME |CREATED |PROVIDERS |TAGS | |--------------------|--------------------|------------------|-------------------------------|--------------|----------|--------------| |demo-identity |demo-identity |demo-gvc-identity |cpln-demo-cpln-organi-3h7v8edk |2 minutes ago | |drink="water" | |demo-identity-get-2 |demo-identity-get-2 |demo-gvc-identity |cpln-demo-cpln-organi-1k08e34y |Now | | | |demo-identity-get-1 |demo-identity-get-1 |demo-gvc-identity |cpln-demo-cpln-organi-e2j3eqrz |Now | | | +--------------------+--------------------+------------------+-------------------------------+--------------+----------+--------------+ ``` * Search for an identity based on a property and tag ```bash Command theme={null} cpln identity query --match any --prop description="demo-identity" --tag drink=water --org demo-cpln-organization --gvc demo-gvc-identity ``` ```text Output theme={null} +--------------------+--------------------+------------------+-------------------------------+--------------+----------+--------------+ |NAME |DESCRIPTION |GVC |LOCAL NAME |CREATED |PROVIDERS |TAGS | |--------------------|--------------------|------------------|-------------------------------|--------------|----------|--------------| |demo-identity |demo-identity |demo-gvc-identity |cpln-demo-cpln-organi-3h7v8edk |2 minutes ago | |drink="water" | |demo-identity-get-2 |demo-identity-get-2 |demo-gvc-identity |cpln-demo-cpln-organi-1k08e34y |Now | | | |demo-identity-get-1 |demo-identity-get-1 |demo-gvc-identity |cpln-demo-cpln-organi-e2j3eqrz |Now | | | +--------------------+--------------------+------------------+-------------------------------+--------------+----------+--------------+ ``` *** #### identity tag Manage the tags belonging to one or more referenced identities **Usage** ```none theme={null} cpln identity tag --tag KEY=VALUE [OPTIONS] ``` Built-in tags, which begin with cpln/\*, can't be modified. ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [required] [default: []] Command options: --tag Attach tags (e.g., --tag drink=water) --remove-tag Remove tags (e.g., --remove-tag tagname) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Add a tag ```bash Command theme={null} cpln identity tag demo-identity --tag drink=water --org demo-cpln-organization --gvc demo-gvc-identity ``` ```text Output theme={null} +--------------+--------------+------------------+-------------------------------+--------------+----------+--------------+ |NAME |DESCRIPTION |GVC |LOCAL NAME |CREATED |PROVIDERS |TAGS | |--------------|--------------|------------------|-------------------------------|--------------|----------|--------------| |demo-identity |demo-identity |demo-gvc-identity |cpln-demo-cpln-organi-3h7v8edk |2 minutes ago | |drink="water" | +--------------+--------------+------------------+-------------------------------+--------------+----------+--------------+ ``` * Update same tag (after running the example above) ```bash Command theme={null} cpln identity tag demo-identity --tag drink=coffee --org demo-cpln-organization --gvc demo-gvc-identity ``` ```text Output theme={null} +--------------+--------------+------------------+-------------------------------+--------------+----------+---------------+ |NAME |DESCRIPTION |GVC |LOCAL NAME |CREATED |PROVIDERS |TAGS | |--------------|--------------|------------------|-------------------------------|--------------|----------|---------------| |demo-identity |demo-identity |demo-gvc-identity |cpln-demo-cpln-organi-3h7v8edk |2 minutes ago | |drink="coffee" | +--------------+--------------+------------------+-------------------------------+--------------+----------+---------------+ ``` * Remove tag (after running the example above) ```bash Command theme={null} cpln identity tag demo-identity --remove drink --org demo-cpln-organization --gvc demo-gvc-identity ``` ```text Output theme={null} +--------------+--------------+------------------+-------------------------------+--------------+----------+-----+ |NAME |DESCRIPTION |GVC |LOCAL NAME |CREATED |PROVIDERS |TAGS | |--------------|--------------|------------------|-------------------------------|--------------|----------|-----| |demo-identity |demo-identity |demo-gvc-identity |cpln-demo-cpln-organi-3h7v8edk |2 minutes ago | | | +--------------+--------------+------------------+-------------------------------+--------------+----------+-----+ ``` *** #### identity update Update properties of the referenced identity **Usage** ```none theme={null} cpln identity update --set UPDATE_PROPERTY [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Update Properties: --set Update the following properties (e.g., --set description="Updated Description"): description string tags. string --unset Unset a property (same paths as --set, e.g., --unset description) Command options: --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * The `--set` option allows the modification of the following identity properties: * `description` * Description of the identity * Type: string * `tags` * Tags (key=value) associated with the identity * Type: string * For the examples below, the initial identity is configured as: ``` +---------------------+---------------------+------------------+-------------------------------+--------+----------+-----+ |NAME |DESCRIPTION |GVC |LOCAL NAME |CREATED |PROVIDERS |TAGS | |---------------------|---------------------|------------------|-------------------------------|--------|----------|-----| |demo-identity-update |demo-identity-update |demo-gvc-identity |cpln-demo-cpln-organi-v6a6b38s |Now | | | +---------------------+---------------------+------------------+-------------------------------+--------+----------+-----+ ``` * Update an identity's description ```bash Command theme={null} cpln identity update demo-identity-update --set description="new description" --org demo-cpln-organization --gvc demo-gvc-identity ``` ```text Output theme={null} +---------------------+----------------+------------------+-------------------------------+--------+----------+-----+ |NAME |DESCRIPTION |GVC |LOCAL NAME |CREATED |PROVIDERS |TAGS | |---------------------|----------------|------------------|-------------------------------|--------|----------|-----| |demo-identity-update |new description |demo-gvc-identity |cpln-demo-cpln-organi-v6a6b38s |Now | | | +---------------------+----------------+------------------+-------------------------------+--------+----------+-----+ ``` * Update an identity's tag ```bash Command theme={null} cpln identity update demo-identity-update --set tags.demo-key=demo-value --org demo-cpln-organization --gvc demo-gvc-identity ``` ```text Output theme={null} +---------------------+----------------+------------------+-------------------------------+--------+----------+----------------------+ |NAME |DESCRIPTION |GVC |LOCAL NAME |CREATED |PROVIDERS |TAGS | |---------------------|----------------|------------------|-------------------------------|--------|----------|----------------------| |demo-identity-update |new description |demo-gvc-identity |cpln-demo-cpln-organi-v6a6b38s |Now | |demo-key="demo-value" | +---------------------+----------------+------------------+-------------------------------+--------+----------+----------------------+ ``` *** # image Source: https://docs.controlplane.com/cli-reference/commands/image cpln image — Build, push and manage container images to Control Plane and configure Docker registry authentication. Manage images and configure Docker login For a detailed overview, please visit the [image's](/reference/image) reference page. ```none theme={null} cpln image [SUBCOMMAND] ``` ```text theme={null} Commands: cpln image access-report Show the access report for the referenced image cpln image audit [ref] Retrieve audit trail events for the referenced image cpln image build Build and containerize an application into an image. If using buildpacks, everything after -- will be passed down to the pack executable. cpln image copy Copy an image from one org to another. This will make sure that docker-login has been run against the source and destination org, then will pull, tag and push the image to the destination org. cpln image delete Delete one or more referenced images cpln image docker-login Perform a Docker login to the organization's private registry cpln image edit Edit the referenced image, as YAML, within an editor cpln image get [ref...] Retrieve one or more referenced images cpln image patch Update the referenced image's metadata using an input file cpln image permissions Show the grantable permissions for an image object type cpln image query Find all the images based on the given query cpln image tag Manage the tags belonging to one or more referenced images Options: --help Show help [boolean] ``` *** #### image access-report Show the access report for the referenced image **Usage** ```none theme={null} cpln image access-report [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln image access-report demo-image:1 --org demo-cpln-organization ``` ```text Output theme={null} +-----------+--------------------------------------------------------+--------+ |PERMISSION |PRINCIPAL |GRANTED | |-----------|--------------------------------------------------------|--------| |create |/org/demo-cpln-organization/group/superusers |manage | |create |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |delete |/org/demo-cpln-organization/group/superusers |manage | |delete |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |edit |/org/demo-cpln-organization/group/superusers |manage | |edit |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |manage |/org/demo-cpln-organization/group/superusers |manage | |manage |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |pull |/org/demo-cpln-organization/group/superusers |manage | |pull |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |view |/org/demo-cpln-organization/group/viewers |view | |view |/org/demo-cpln-organization/group/superusers |manage | |view |/org/demo-cpln-organization/serviceaccount/controlplane |manage | +-----------+--------------------------------------------------------+--------+ ``` *** #### image audit Retrieve audit trail events for the referenced image **Usage** ```none theme={null} cpln image audit [ref] [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. Command options: --subject Filter by subject (serviceaccount name or user email) [string] --context Audit context name (the 'cpln' context displays Control Plane actions) [string] [default: "cpln"] --since Relative lookback window from now (e.g., 1h, 24h, 7d). Mutually exclusive with --from/--to [string] [default: "7d"] --from Start time for audit events (ISO 8601 format or relative duration, e.g., 2025-10-23T07:00:00Z, 7d, 1h). Cannot be used with --since [string] --to End time for audit events (ISO 8601 format or relative duration, e.g., 2025-10-23T07:00:00Z, 1d, 30m) [string] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### image build Build and containerize an application into an image. If using buildpacks, everything after -- will be passed down to the pack executable. **Usage** ```none theme={null} cpln image build --name IMAGE_NAME:TAG [OPTIONS] ``` To push an image from a local build, the [docker-credential-cpln](/cli-reference/installation#binary) executable must be installed if the CLI was not installed using npm. A remote build pushes the image itself and does not need it. The option `--builder` accepts any trusted [buildpacks](https://buildpacks.io/docs/) builder. The default builder is `heroku/builder:24_linux-amd64`. [Docker](https://www.docker.com/) is required for local builds. Pass `--remote` to build remotely: the `--dir` folder — or a GitHub or GitLab repository given with `--repo` — is built by Control Plane and the image is pushed to the org's private registry. See [Remote builds](/cli-reference/get-started/images#build-options) for details. ```text theme={null} Command options: --name, -n Name and tag for the image [string] [required] --dockerfile Path to Dockerfile (e.g.: PATH/Dockerfile). If set, the builder option is not used --builder, -B Builder image to use (e.g., heroku/builder:24_linux-amd64, gcr.io/buildpacks/builder:google-22, paketobuildpacks/builder-jammy-full) [default: "heroku/builder:24_linux-amd64"] --buildpack, -b Buildpack to use. One of: buildpack ID and version (@), path to a buildpack directory (not supported on Windows), path/URL to a .tar or .tgz file, or a packaged buildpack image (/[:]). Repeat for each buildpack in order, or supply once by comma-separated list --env, -e Build-time environment variable in the form 'VAR=VALUE' or 'VAR' (value taken from current environment). Repeat for each env var. NOTE: These are NOT available at image runtime. --env-file Build-time environment variables file. One variable per line, of the form 'VAR=VALUE' or 'VAR'. NOTE: These are NOT available at image runtime. --build-arg Set build-time variables (format: 'NAME=value', or 'NAME' to take the value from the environment). Repeat for each arg. NOTE: Not used by buildpack builds. --secret Secret to expose to the build in the form 'id=[,src=]'. src takes a local file path, or cpln://secret/[.] for a secret in your org. Local sources (a file path, an env variable) don't work with --remote. Repeat for each secret. Requires a Dockerfile build. --trust-builder Trust the provided builder. All lifecycle phases will be run in a single container for better performance. [boolean] [default: false] --trust-extra-buildpacks Trust buildpacks that are provided in addition to the buildpacks on the builder [boolean] [default: false] --dir Directory containing the application [string] [default: "."] --no-cache Builds the image without using any cached layers. [boolean] [default: false] --push Push the new image to the org's private registry [boolean] [default: false] --platform, -p Target platform(s) for the build (e.g., linux/amd64, linux/amd64,linux/arm64) [default: "linux/amd64"] --remote Build on the Control Plane build service instead of local Docker, then push the image to the org's private registry. Builds the --dir folder, or a git repository when --repo is set. The folder context honors .dockerignore, or .gitignore when no .dockerignore is present. [boolean] [default: false] --repo HTTPS URL of a GitHub or GitLab repository to build instead of a local folder. Requires --remote; private repositories build through the org's git connection, which the CLI sets up on first use. [string] --branch Branch to build. Requires --repo, and defaults to the repository's default branch. [string] --detach Start the build and return immediately instead of waiting for it to finish. Requires --remote. [boolean] [default: false] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln image build --name demo-image:1 --push --org demo-cpln-organization -- --env CPLN_IMAGE_BUILD=true ``` ```bash Remote Build theme={null} # Build the current folder remotely, no local Docker required cpln image build --name demo-image:1 --remote --org demo-cpln-organization # Build a git repository instead of a local folder cpln image build --name demo-image:1 --remote --repo https://github.com/demo-org/demo-app --branch main --org demo-cpln-organization # Start the build and return without waiting for it to finish cpln image build --name demo-image:1 --remote --detach --org demo-cpln-organization ``` ```text Output theme={null} If the build was successful, the newly generated container image will be pushed to the referenced org's private registry. If using buildpacks, every flag after the -- is passed to the pack binary. Below are the available flags from the pack build command. Pack Build uses Cloud Native Buildpacks to create a runnable app image from source code. Pack Build requires an image name, which will be generated from the source code. Build defaults to the current directory, but you can use \`--path\` to specify another source code directory. Build requires a \`builder\`, which can either be provided directly to build using \`--builder\`, or can be set using the \`set-default-builder\` command. For more on how to use \`pack build\`, see: https://buildpacks.io/docs/app-developer-guide/build-an-app/. Usage: pack build [flags] Examples: pack build test_img --path apps/test-app --builder cnbs/sample-builder:bionic Flags: -B, --builder string Builder image -b, --buildpack strings Buildpack to use. One of: a buildpack by id and version in the form of '@', path to a buildpack directory (not supported on Windows), path/URL to a buildpack .tar or .tgz file, or a packaged buildpack image name in the form of '/[:]' Repeat for each buildpack in order, or supply once by comma-separated list -r, --buildpack-registry string Buildpack Registry by name --cache-image string Cache build layers in remote registry. Requires --publish --clear-cache Clear image's associated cache before building -D, --default-process string Set the default process type. (default "web") -d, --descriptor string Path to the project descriptor file --docker-host string Address to docker daemon that will be exposed to the build container. If not set (or set to empty string) the standard socket location will be used. Special value 'inherit' may be used in which case DOCKER_HOST environment variable will be used. This option may set DOCKER_HOST environment variable for the build container if needed. -e, --env stringArray Build-time environment variable, in the form 'VAR=VALUE' or 'VAR'. When using latter value-less form, value will be taken from current environment at the time this command is executed. This flag may be specified multiple times and will override individual values defined by --env-file. Repeat for each env in order (comma-separated lists not accepted) NOTE: These are NOT available at image runtime. --env-file stringArray Build-time environment variables file One variable per line, of the form 'VAR=VALUE' or 'VAR' When using latter value-less form, value will be taken from current environment at the time this command is executed NOTE: These are NOT available at image runtime." --gid int Override GID of user's group in the stack's build and run images. The provided value must be a positive number -h, --help Help for 'build' --lifecycle-image string Custom lifecycle image to use for analysis, restore, and export when builder is untrusted. --network string Connect detect and build containers to network -p, --path string Path to app dir or zip-formatted file (defaults to current working directory) --previous-image string Set previous image to a particular tag reference, digest reference, or (when performing a daemon build) image ID --publish Publish to registry --pull-policy string Pull policy to use. Accepted values are always, never, and if-not-present. (default "always") --run-image string Run image (defaults to default stack's run image) -t, --tag strings Additional tags to push the output image to. Tags should be in the format 'image:tag' or 'repository/image:tag'. Repeat for each tag in order, or supply once by comma-separated list --trust-builder Trust the provided builder All lifecycle phases will be run in a single container (if supported by the lifecycle). --volume stringArray Mount host volume into the build container, in the form ':[:]'. - 'host path': Name of the volume or absolute directory path to mount. - 'target path': The path where the file or directory is available in the container. - 'options' (default "ro"): An optional comma separated list of mount options. - "ro", volume contents are read-only. - "rw", volume contents are readable and writeable. - "volume-opt==", can be specified more than once, takes a key-value pair consisting of the option name and its value. Repeat for each volume in order (comma-separated lists not accepted) --workspace string Location at which to mount the app dir in the build image Global Flags: --no-color Disable color output -q, --quiet Show less output --timestamps Enable timestamps in output -v, --verbose Show more output ``` *** #### image copy Copy an image from one org to another. This will make sure that docker-login has been run against the source and destination org, then will pull, tag and push the image to the destination org. **Usage** ```none theme={null} cpln image copy --to-name IMAGE:TAG --to-org TARGET_ORG --to-profile TARGET_ORG_PROFILE [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --to-name Name and tag for the image --to-org Target org to copy the image to --to-profile Profile to use for accessing the "to-org" argument --cleanup Cleans up the pulled and retagged image [boolean] [default: false] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Copying image between orgs within the same profile ```bash theme={null} cpln image copy SOURCE_IMAGE:TAG --to-name TARGET_IMAGE:NEW_TAG --to-org TARGET_ORG ``` The profile service principal (user/service account) must have the proper permissions to pull/push images in the current and target org. * Copying image between orgs using different profiles ```bash theme={null} cpln image copy SOURCE_IMAGE:TAG --to-name TARGET_IMAGE:NEW_TAG --to-org TARGET_ORG --to-profile TARGET_ORG_PROFILE ``` This command will copy an image from the current org to the target org. The current profile service principal (user/service account) must have the proper permissions to pull images in the current org (default profile). The referenced --to-profile service principal (user/service account) must have the proper permissions to push images to the target org. This command allows the copying of images between different orgs using user or service account profiles. *** #### image delete Delete one or more referenced images **Usage** ```none theme={null} cpln image delete [OPTIONS] ``` ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [required] [default: []] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln image delete demo-image:1 --org demo-cpln-organization ``` ```text Output theme={null} If the delete was successful, a new command prompt will be shown. ``` *** #### image docker-login Perform a Docker login to the organization's private registry **Usage** ```none theme={null} cpln image docker-login [OPTIONS] ``` ```text theme={null} Command options: --ignore-output Ignore the output of this command. Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln image docker-login demo-image:1 --org demo-cpln-organization ``` ```text Output theme={null} If the login was successful, the local Docker instance will have the abiltity to login to the referenced organization's private registry. ``` *** #### image edit Edit the referenced image, as YAML, within an editor **Usage** ```none theme={null} cpln image edit [OPTIONS] ``` The default editor can be set by using the EDITOR environment variable. ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --replace, -r Replace instead of patch/merge [boolean] [default: false] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln image edit demo-image:1 --org demo-cpln-organization ``` ```text Output theme={null} Your default editor will be launched. Once any modifications are saved, the updates will be sent to the API. ``` *** #### image get Retrieve one or more referenced images **Usage** ```none theme={null} cpln image get [ref...] [OPTIONS] ``` An empty reference will return all references ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [default: []] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Get all images ```bash Command theme={null} cpln image get --org demo-cpln-organization ``` ```text Output theme={null} +-------------+----------------------------------------------------------+-----------------+------------+----------------------------+ |NAME |PULL |DIGEST |CREATED |TAGS | |-------------|----------------------------------------------------------|-----------------|------------|----------------------------| |demo-image:1 |demo-cpln-organization.registry.test.cpln.io/demo-image:1 |sha256:3fe719... |2 years ago | | |demo-image:2 |demo-cpln-organization.registry.test.cpln.io/demo-image:2 |sha256:3fe719... |5 years ago |description="Demo Image #1" | +-------------+----------------------------------------------------------+-----------------+------------+----------------------------+ ``` * Get one image ```bash Command theme={null} cpln image get demo-image:1 --org demo-cpln-organization ``` ```text Output theme={null} +-------------+----------------------------------------------------------+-----------------+------------+-----+ |NAME |PULL |DIGEST |CREATED |TAGS | |-------------|----------------------------------------------------------|-----------------|------------|-----| |demo-image:1 |demo-cpln-organization.registry.test.cpln.io/demo-image:1 |sha256:3fe719... |2 years ago | | +-------------+----------------------------------------------------------+-----------------+------------+-----+ ``` * Get multiple images ```bash Command theme={null} cpln image get demo-image:1 demo-image:2 --org demo-cpln-organization ``` ```text Output theme={null} +-------------+----------------------------------------------------------+-----------------+------------+----------------------------+ |NAME |PULL |DIGEST |CREATED |TAGS | |-------------|----------------------------------------------------------|-----------------|------------|----------------------------| |demo-image:1 |demo-cpln-organization.registry.test.cpln.io/demo-image:1 |sha256:3fe719... |2 years ago | | |demo-image:2 |demo-cpln-organization.registry.test.cpln.io/demo-image:2 |sha256:3fe719... |5 years ago |description="Demo Image #1" | +-------------+----------------------------------------------------------+-----------------+------------+----------------------------+ ``` *** #### image patch Update the referenced image's metadata using an input file **Usage** ```none theme={null} cpln image patch --file FILE_NAME [OPTIONS] ``` Running this command with the option **`--file -`** sets the input to stdin. The input is required to be a valid JSON or YAML string (e.g., `{"description": "My new description"}` or `description: My new description`). To complete the update, press Control-D (or Control-Z if you are using Windows). ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --file, -f File to load the patch from. Use `--file -` to enable input from stdin. [required] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln image patch demo-image:1 --file update.json --org demo-cpln-organization ``` ```text Output theme={null} The changes within the JSON file will be performed against the referenced image. ``` *** #### image permissions Show the grantable permissions for an image object type **Usage** ```none theme={null} cpln image permissions [OPTIONS] ``` ```text theme={null} Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln image permissions --org demo-cpln-organization ``` ```text Output theme={null} +-------+-----------------------------------------+-----------------------------------------+ |NAME |DESCRIPTION |IMPLIES | |-------|-----------------------------------------|-----------------------------------------| |create |Create new image. You can push if you... |pull | |delete |Delete | | |edit |Modify existing image (only tags can ... |view | |manage |Full access |create, delete, edit, manage, pull, view | |pull |Image can be pulled |view | |view |Read-only access | | +-------+-----------------------------------------+-----------------------------------------+ ``` *** #### image query Find all the images based on the given query **Usage** ```none theme={null} cpln image query QUERY_OPTIONS [OPTIONS] ``` ```text theme={null} Query options: --match Query filter type [choices: "all", "none", "any"] [default: "all"] --property, --prop Property to query --tag Tag to query --rel Relation to query Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) Use the `--match` option to display **all**, **none**, or **any** of the results based on a given property or tag * Search for an image based on the name property ```bash Command theme={null} cpln image query --match any --prop name=demo-image:1 --org demo-cpln-organization ``` ```text Output theme={null} +-------------+----------------------------------------------------------+-----------------+------------+-----+ |NAME |PULL |DIGEST |CREATED |TAGS | |-------------|----------------------------------------------------------|-----------------|------------|-----| |demo-image:1 |demo-cpln-organization.registry.test.cpln.io/demo-image:1 |sha256:3fe719... |2 years ago | | +-------------+----------------------------------------------------------+-----------------+------------+-----+ ``` * Search for an image based on the description property ```bash Command theme={null} cpln image query --match any --prop tag="2" --org demo-cpln-organization ``` ```text Output theme={null} +-------------+----------------------------------------------------------+-----------------+------------+----------------------------+ |NAME |PULL |DIGEST |CREATED |TAGS | |-------------|----------------------------------------------------------|-----------------|------------|----------------------------| |demo-image:2 |demo-cpln-organization.registry.test.cpln.io/demo-image:2 |sha256:3fe719... |5 years ago |description="Demo Image #1" | +-------------+----------------------------------------------------------+-----------------+------------+----------------------------+ ``` * Search for an image based on a tag ```bash Command theme={null} cpln image query --match any --tag description="Demo Image #1" --org demo-cpln-organization ``` ```text Output theme={null} +-------------+----------------------------------------------------------+-----------------+------------+----------------------------+ |NAME |PULL |DIGEST |CREATED |TAGS | |-------------|----------------------------------------------------------|-----------------|------------|----------------------------| |demo-image:2 |demo-cpln-organization.registry.test.cpln.io/demo-image:2 |sha256:3fe719... |5 years ago |description="Demo Image #1" | +-------------+----------------------------------------------------------+-----------------+------------+----------------------------+ ``` * Search for an image based on a property and tag ```bash Command theme={null} cpln image query --match any --prop name="demo-image" --tag description="Demo Image #1" --org demo-cpln-organization ``` ```text Output theme={null} +-------------+----------------------------------------------------------+-----------------+------------+----------------------------+ |NAME |PULL |DIGEST |CREATED |TAGS | |-------------|----------------------------------------------------------|-----------------|------------|----------------------------| |demo-image:2 |demo-cpln-organization.registry.test.cpln.io/demo-image:2 |sha256:3fe719... |5 years ago |description="Demo Image #1" | +-------------+----------------------------------------------------------+-----------------+------------+----------------------------+ ``` *** #### image tag Manage the tags belonging to one or more referenced images **Usage** ```none theme={null} cpln image tag --tag KEY=VALUE [OPTIONS] ``` Built-in tags, which begin with cpln/\*, can't be modified. ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [required] [default: []] Command options: --tag Attach tags (e.g., --tag drink=water) --remove-tag Remove tags (e.g., --remove-tag tagname) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Add a tag ```bash Command theme={null} cpln image tag demo-image:1 --tag drink=water --org demo-cpln-organization ``` ```text Output theme={null} +-------------+----------------------------------------------------------+-----------------+------------+--------------+ |NAME |PULL |DIGEST |CREATED |TAGS | |-------------|----------------------------------------------------------|-----------------|------------|--------------| |demo-image:1 |demo-cpln-organization.registry.test.cpln.io/demo-image:1 |sha256:3fe719... |2 years ago |drink="water" | +-------------+----------------------------------------------------------+-----------------+------------+--------------+ ``` * Update same tag (after running the example above) ```bash Command theme={null} cpln image tag demo-image:1 --tag drink=coffee --org demo-cpln-organization ``` ```text Output theme={null} +-------------+----------------------------------------------------------+-----------------+------------+---------------+ |NAME |PULL |DIGEST |CREATED |TAGS | |-------------|----------------------------------------------------------|-----------------|------------|---------------| |demo-image:1 |demo-cpln-organization.registry.test.cpln.io/demo-image:1 |sha256:3fe719... |2 years ago |drink="coffee" | +-------------+----------------------------------------------------------+-----------------+------------+---------------+ ``` * Remove tag (after running the example above) ```bash Command theme={null} cpln image tag demo-image:1 --remove drink --org demo-cpln-organization ``` ```text Output theme={null} +-------------+----------------------------------------------------------+-----------------+------------+-----+ |NAME |PULL |DIGEST |CREATED |TAGS | |-------------|----------------------------------------------------------|-----------------|------------|-----| |demo-image:1 |demo-cpln-organization.registry.test.cpln.io/demo-image:1 |sha256:3fe719... |2 years ago | | +-------------+----------------------------------------------------------+-----------------+------------+-----+ ``` *** # ipset Source: https://docs.controlplane.com/cli-reference/commands/ipset cpln ipset — Reserve and manage static public IP addresses for workloads and GVC load balancers. Manage IP Sets ```none theme={null} cpln ipset [SUBCOMMAND] ``` ```text theme={null} Commands: cpln ipset access-report Show the access report for the referenced ip set cpln ipset add-location Add one or more locations to the referenced IP set cpln ipset audit [ref] Retrieve audit trail events for the referenced ip set cpln ipset clone Create a clone of the referenced ip set; this will only duplicate its spec. [aliases: copy] cpln ipset create Create a new ip set cpln ipset delete Delete one or more referenced ip sets cpln ipset edit Edit the referenced ip set, as YAML, within an editor cpln ipset eventlog Show the event log of the referenced ip set [aliases: log] cpln ipset get [ref...] Retrieve one or more referenced ip set cpln ipset patch Update the referenced ip set's metadata using an input file cpln ipset permissions Show the grantable permissions for an ip set object type cpln ipset query Find all the ip sets based on the given query cpln ipset remove-location Remove one or more locations from the referenced IP set cpln ipset tag Manage the tags belonging to one or more referenced ip sets cpln ipset update Update properties of the referenced ip set cpln ipset update-location Update one or more locations from the referenced IP set Options: --help Show help [boolean] ``` *** #### ipset access-report Show the access report for the referenced ip set **Usage** ```none theme={null} cpln ipset access-report [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln ipset access-report demo-ipset --org demo-cpln-organization ``` ```text Output theme={null} +-----------+--------------------------------------------------------+--------+ |PERMISSION |PRINCIPAL |GRANTED | |-----------|--------------------------------------------------------|--------| |create |/org/demo-cpln-organization/group/superusers |manage | |create |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |delete |/org/demo-cpln-organization/group/superusers |manage | |delete |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |edit |/org/demo-cpln-organization/group/superusers |manage | |edit |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |manage |/org/demo-cpln-organization/group/superusers |manage | |manage |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |view |/org/demo-cpln-organization/group/viewers |view | |view |/org/demo-cpln-organization/group/superusers |manage | |view |/org/demo-cpln-organization/serviceaccount/controlplane |manage | +-----------+--------------------------------------------------------+--------+ ``` *** #### ipset add-location Add one or more locations to the referenced IP set **Usage** ```none theme={null} cpln ipset add-location [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --location Add a location with an optional retention policy (`keep` or `free`), e.g., aws-eu-central-1,keep. [required] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} {COMMAND} ``` ```text Output theme={null} {CODE} ``` *** #### ipset audit Retrieve audit trail events for the referenced ip set **Usage** ```none theme={null} cpln ipset audit [ref] [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. Command options: --subject Filter by subject (serviceaccount name or user email) [string] --context Audit context name (the 'cpln' context displays Control Plane actions) [string] [default: "cpln"] --since Relative lookback window from now (e.g., 1h, 24h, 7d). Mutually exclusive with --from/--to [string] [default: "7d"] --from Start time for audit events (ISO 8601 format or relative duration, e.g., 2025-10-23T07:00:00Z, 7d, 1h). Cannot be used with --since [string] --to End time for audit events (ISO 8601 format or relative duration, e.g., 2025-10-23T07:00:00Z, 1d, 30m) [string] Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### ipset clone Create a clone of the referenced ip set; this will only duplicate its spec. **Usage** ```none theme={null} cpln ipset clone --name CLONE_NAME [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --name Set the name for the clone [required] --description Optional description, defaults to the name if not set --tag Attach tags (e.g., --tag drink=water) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln ipset clone demo-ipset --name demo-ipset-clone --org demo-cpln-organization ``` ```text Output theme={null} +-----------------+--------------------+-----+----------+--------------+ |NAME |DESCRIPTION |LINK |LOCATIONS |TAGS | |-----------------|--------------------|-----|----------|--------------| |demo-ipset-clone |Clone of demo-ipset | | |drink="water" | +-----------------+--------------------+-----+----------+--------------+ ``` *** #### ipset create Create a new ip set **Usage** ```none theme={null} cpln ipset create --name NAME [OPTIONS] ``` ```text theme={null} Command options: --name Name of the new volume set [required] --description, --desc Optional description, defaults to the name if not set --link A link to a workload --location Specifies a location and optional retention policy (keep or free), e.g., aws-eu-central-1,keep --tag Attach tags (e.g., --tag drink=water) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln ipset create --name demo-ipset --desc demo-ipset --tag drink=water --org demo-cpln-organization ``` ```text Output theme={null} +-----------+------------+-----+----------+--------------+ |NAME |DESCRIPTION |LINK |LOCATIONS |TAGS | |-----------|------------|-----|----------|--------------| |demo-ipset |demo-ipset | | |drink="water" | +-----------+------------+-----+----------+--------------+ ``` *** #### ipset delete Delete one or more referenced ip sets **Usage** ```none theme={null} cpln ipset delete [OPTIONS] ``` ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [required] [default: []] Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln ipset delete demo-ipset --org demo-cpln-organization ``` ```text Output theme={null} If the delete was successful, a new command prompt will be shown. ``` *** #### ipset edit Edit the referenced ip set, as YAML, within an editor **Usage** ```none theme={null} cpln ipset edit [OPTIONS] ``` The default editor can be set by using the EDITOR environment variable. ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --replace, -r Replace instead of patch/merge [boolean] [default: false] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln ipset edit demo-ipset --org demo-cpln-organization ``` ```text Output theme={null} Your default editor will be launched. Once any modifications are saved, the updates will be sent to the API. ``` *** #### ipset eventlog Show the event log of the referenced ip set **Usage** ```none theme={null} cpln ipset eventlog [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln ipset eventlog demo-ipset --org demo-cpln-organization ``` ```text Output theme={null} The event log for the referenced ipset will be shown. ``` *** #### ipset get Retrieve one or more referenced ip set **Usage** ```none theme={null} cpln ipset get [ref...] [OPTIONS] ``` An empty reference will return all references ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [default: []] Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Get all ipsets ```bash Command theme={null} cpln ipset get --org demo-cpln-organization ``` ```text Output theme={null} +-----------------+-----------------+-----+----------+--------------+ |NAME |DESCRIPTION |LINK |LOCATIONS |TAGS | |-----------------|-----------------|-----|----------|--------------| |demo-ipset |demo-ipset | | |drink="water" | |demo-ipset-get-1 |demo-ipset-get-1 | | | | |demo-ipset-get-2 |demo-ipset-get-2 | | | | +-----------------+-----------------+-----+----------+--------------+ ``` * Get one ipset ```bash Command theme={null} cpln ipset get demo-ipset-get-1 --org demo-cpln-organization ``` ```text Output theme={null} +-----------------+-----------------+-----+----------+-----+ |NAME |DESCRIPTION |LINK |LOCATIONS |TAGS | |-----------------|-----------------|-----|----------|-----| |demo-ipset-get-1 |demo-ipset-get-1 | | | | +-----------------+-----------------+-----+----------+-----+ ``` * Get multiple ipsets ```bash Command theme={null} cpln ipset get demo-ipset-get-1 demo-ipset-get-2 --org demo-cpln-organization ``` ```text Output theme={null} +-----------------+-----------------+-----+----------+-----+ |NAME |DESCRIPTION |LINK |LOCATIONS |TAGS | |-----------------|-----------------|-----|----------|-----| |demo-ipset-get-1 |demo-ipset-get-1 | | | | |demo-ipset-get-2 |demo-ipset-get-2 | | | | +-----------------+-----------------+-----+----------+-----+ ``` *** #### ipset patch Update the referenced ip set's metadata using an input file **Usage** ```none theme={null} cpln ipset patch --file FILE_NAME [OPTIONS] ``` Running this command with the option **`--file -`** sets the input to stdin. The input is required to be a valid JSON or YAML string (e.g., `{"description": "My new description"}` or `description: My new description`). To complete the update, press Control-D (or Control-Z if you are using Windows). ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --file, -f File to load the patch from. Use `--file -` to enable input from stdin. [required] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln ipset patch demo-ipset --file update.json --org demo-cpln-organization ``` ```text Output theme={null} The changes within the JSON file will be performed against the referenced ipset. ``` *** #### ipset permissions Show the grantable permissions for an ip set object type **Usage** ```none theme={null} cpln ipset permissions [OPTIONS] ``` ```text theme={null} Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln ipset permissions --org demo-cpln-organization ``` ```text Output theme={null} +-------+--------------------------+-----------------------------------+ |NAME |DESCRIPTION |IMPLIES | |-------|--------------------------|-----------------------------------| |create |Create new ip sets | | |delete |Delete ip sets | | |edit |Modify existing resources |view | |manage |Full access |create, delete, edit, manage, view | |view |Read-only access | | +-------+--------------------------+-----------------------------------+ ``` *** #### ipset query Find all the ip sets based on the given query **Usage** ```none theme={null} cpln ipset query QUERY_OPTIONS [OPTIONS] ``` ```text theme={null} Query options: --match Query filter type [choices: "all", "none", "any"] [default: "all"] --property, --prop Property to query --tag Tag to query --rel Relation to query Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) Use the `--match` option to display **all**, **none**, or **any** of the results based on a given property or tag * Search for an ipset based on the name property ```bash Command theme={null} cpln ipset query --match any --prop name=demo-ipset --org demo-cpln-organization ``` ```text Output theme={null} +-----------+------------+-----+----------+--------------+ |NAME |DESCRIPTION |LINK |LOCATIONS |TAGS | |-----------|------------|-----|----------|--------------| |demo-ipset |demo-ipset | | |drink="water" | +-----------+------------+-----+----------+--------------+ ``` * Search for an ipset based on the description property ```bash Command theme={null} cpln ipset query --match any --prop description="demo-ipset" --org demo-cpln-organization ``` ```text Output theme={null} +-----------+------------+-----+----------+--------------+ |NAME |DESCRIPTION |LINK |LOCATIONS |TAGS | |-----------|------------|-----|----------|--------------| |demo-ipset |demo-ipset | | |drink="water" | +-----------+------------+-----+----------+--------------+ ``` * Search for an ipset based on a tag ```bash Command theme={null} cpln ipset query --match any --tag drink=water --org demo-cpln-organization ``` ```text Output theme={null} +-----------+------------+-----+----------+--------------+ |NAME |DESCRIPTION |LINK |LOCATIONS |TAGS | |-----------|------------|-----|----------|--------------| |demo-ipset |demo-ipset | | |drink="water" | +-----------+------------+-----+----------+--------------+ ``` * Search for an ipset based on a property and tag ```bash Command theme={null} cpln ipset query --match any --prop description="demo-ipset" --tag drink=water --org demo-cpln-organization ``` ```text Output theme={null} +-----------+------------+-----+----------+--------------+ |NAME |DESCRIPTION |LINK |LOCATIONS |TAGS | |-----------|------------|-----|----------|--------------| |demo-ipset |demo-ipset | | |drink="water" | +-----------+------------+-----+----------+--------------+ ``` *** #### ipset remove-location Remove one or more locations from the referenced IP set **Usage** ```none theme={null} cpln ipset remove-location [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --location Remove a location by name, e.g., aws-eu-central-1. [required] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} {COMMAND} ``` ```text Output theme={null} {CODE} ``` *** #### ipset tag Manage the tags belonging to one or more referenced ip sets **Usage** ```none theme={null} cpln ipset tag --tag KEY=VALUE [OPTIONS] ``` Built-in tags, which begin with cpln/\*, can't be modified. ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [required] [default: []] Command options: --tag Attach tags (e.g., --tag drink=water) --remove-tag Remove tags (e.g., --remove-tag tagname) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Add a tag to an IP Set ```bash Command theme={null} {ADD_TAG_COMMAND} ``` ```text Output theme={null} {ADD_TAG_CODE} ``` * Update same tag (after running the example above) ```bash Command theme={null} {UPDATE_TAG_COMMAND} ``` ```text Output theme={null} {UPDATE_TAG_CODE} ``` * Remove tag (after running the example above) ```bash Command theme={null} {REMOVE_TAG_COMMAND} ``` ```text Output theme={null} {REMOVE_TAG_CODE} ``` * Add same tag to multiple IP Sets ```bash Command theme={null} {ADD_TAG_MULTIPLE_COMMAND} ``` ```text Output theme={null} {ADD_TAG_MULTIPLE_CODE} ``` * Update same tag on multiple IP Sets (after running the example above) ```bash Command theme={null} {UPDATE_TAG_MULTIPLE_COMMAND} ``` ```text Output theme={null} {UPDATE_TAG_MULTIPLE_CODE} ``` * Remove same tag from multiple IP Sets (after running the example above) ```bash Command theme={null} {REMOVE_TAG_MULTIPLE_COMMAND} ``` ```text Output theme={null} {REMOVE_TAG_MULTIPLE_CODE} ``` *** #### ipset update Update properties of the referenced ip set **Usage** ```none theme={null} cpln ipset update --set UPDATE_PROPERTY [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Update Properties: --set Update the following properties (e.g., --set description="Updated Description"): description string tags. string spec.link string --unset Unset a property (same paths as --set, e.g., --unset description) Command options: --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * For the examples below, the initial IP Set is configured as: ```text theme={null} {RESOURCE_CODE} ``` * Update an IP Set's description ```bash Command theme={null} {UPDATE_DESCRIPTION_COMMAND} ``` ```text Output theme={null} {UPDATE_DESCRIPTION_CODE} ``` * Update an IP Set's tags ```bash Command theme={null} {UPDATE_TAGS_COMMAND} ``` ```text Output theme={null} {UPDATE_TAGS_CODE} ``` * Update an IP Set's link ```bash Command theme={null} {UPDATE_LINK_COMMAND} ``` ```text Output theme={null} {UPDATE_LINK_CODE} ``` *** #### ipset update-location Update one or more locations from the referenced IP set **Usage** ```none theme={null} cpln ipset update-location [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --location Add a location with an optional retention policy (`keep` or `free`), e.g., aws-eu-central-1,keep. [required] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * For the examples below, the initial IP Set is configured as: ```text theme={null} {IPSET} ``` * Update one or more locations from the referenced IP set wtih an optional retention policy (one of `keep` or `free`). ```bash Command theme={null} {UPDATE_LOCATION_COMMAND} ``` ```text Output theme={null} {UPDATE_LOCATION_CODE} ``` *** # location Source: https://docs.controlplane.com/cli-reference/commands/location cpln location — Manage cloud provider regions and custom BYOK locations available for workload deployment. Manage locations For a detailed overview, please visit the [location's](/reference/location) reference page. ```none theme={null} cpln location [SUBCOMMAND] ``` ```text theme={null} Commands: cpln location access-report Show the access report for the referenced location cpln location audit [ref] Retrieve audit trail events for the referenced location cpln location create Create a new BYOK location cpln location delete Delete one or more referenced locations cpln location edit Edit the referenced location, as YAML, within an editor cpln location get [ref...] Retrieve one or more referenced locations cpln location install Get instructions for obtaining the installation script for a BYOK location cpln location patch Update the referenced location's metadata using an input file cpln location permissions Show the grantable permissions for a location object type cpln location query Find all the locations based on the given query cpln location tag Manage the tags belonging to one or more referenced locations cpln location uninstall Get instructions for obtaining the uninstallation script for a BYOK location Options: --help Show help [boolean] ``` *** #### location access-report Show the access report for the referenced location **Usage** ```none theme={null} cpln location access-report [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln location access-report aws-eu-central-1 --org demo-cpln-organization ``` ```text Output theme={null} +-----------+--------------------------------------------------------+--------+ |PERMISSION |PRINCIPAL |GRANTED | |-----------|--------------------------------------------------------|--------| |create |/org/demo-cpln-organization/group/superusers |manage | |create |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |delete |/org/demo-cpln-organization/group/superusers |manage | |delete |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |edit |/org/demo-cpln-organization/group/superusers |manage | |edit |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |manage |/org/demo-cpln-organization/group/superusers |manage | |manage |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |use |/org/demo-cpln-organization/group/superusers |manage | |use |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |view |/org/demo-cpln-organization/group/viewers |view | |view |/org/demo-cpln-organization/group/superusers |manage | |view |/org/demo-cpln-organization/serviceaccount/controlplane |manage | +-----------+--------------------------------------------------------+--------+ ``` *** #### location audit Retrieve audit trail events for the referenced location **Usage** ```none theme={null} cpln location audit [ref] [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. Command options: --subject Filter by subject (serviceaccount name or user email) [string] --context Audit context name (the 'cpln' context displays Control Plane actions) [string] [default: "cpln"] --since Relative lookback window from now (e.g., 1h, 24h, 7d). Mutually exclusive with --from/--to [string] [default: "7d"] --from Start time for audit events (ISO 8601 format or relative duration, e.g., 2025-10-23T07:00:00Z, 7d, 1h). Cannot be used with --since [string] --to End time for audit events (ISO 8601 format or relative duration, e.g., 2025-10-23T07:00:00Z, 1d, 30m) [string] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### location create Create a new BYOK location **Usage** ```none theme={null} cpln location create --name NAME [OPTIONS] ``` ```text theme={null} Command options: --name Name of the new BYOK location [required] --description, --desc Optional description, defaults to the name if not set --tag Attach tags (e.g., --tag drink=water) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Create a new [BYOK](/byok/overview) location. ```bash Command theme={null} cpln location create --name demo-byok-location --org demo-cpln-organization ``` ```text Output theme={null} +-------------------+-------------------+---------+--------+-------------------+-------+-----+ |NAME |DESCRIPTION |PROVIDER |ENABLED |REGION |ORIGIN |TAGS | |-------------------|-------------------|---------|--------|-------------------|-------|-----| |demo-byok-location |demo-byok-location |byok |true |demo-byok-location |custom | | +-------------------+-------------------+---------+--------+-------------------+-------+-----+ ``` *** #### location delete Delete one or more referenced locations **Usage** ```none theme={null} cpln location delete [OPTIONS] ``` ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [required] [default: []] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Delete a [BYOK](/byok/overview) location. ```bash Command theme={null} cpln location delete demo-byok-location-tbd --org demo-cpln-organization ``` ```text Output theme={null} If the delete was successful, a new command prompt will be shown. ``` *** #### location edit Edit the referenced location, as YAML, within an editor **Usage** ```none theme={null} cpln location edit [OPTIONS] ``` The default editor can be set by using the EDITOR environment variable. ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --replace, -r Replace instead of patch/merge [boolean] [default: false] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln location edit aws-eu-central-1 --org demo-cpln-organization ``` ```text Output theme={null} Your default editor will be launched. Once any modifications are saved, the updates will be sent to the API. ``` *** #### location get Retrieve one or more referenced locations **Usage** ```none theme={null} cpln location get [ref...] [OPTIONS] ``` An empty reference will return all references ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [default: []] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Get all locations ```bash Command theme={null} cpln location get --org demo-cpln-organization ``` ```text Output theme={null} +----------------------------+----------------------------------------+---------+--------+------------------------+--------+-------------------------------+ |NAME |DESCRIPTION |PROVIDER |ENABLED |REGION |ORIGIN |TAGS | |----------------------------|----------------------------------------|---------|--------|------------------------|--------|-------------------------------| |aws-ap-northeast-1 |AWS, Asia Pacific (Tokyo) |aws |true |ap-northeast-1 |builtin |cpln/city="Tokyo" | | | | | | | |cpln/continent="Asia" | | | | | | | |cpln/country="Japan" | |aws-ap-northeast-2 |AWS, Asia Pacific (Seoul) |aws |true |ap-northeast-2 |builtin |cpln/city="Seoul" | | | | | | | |cpln/continent="Asia" | | | | | | | |cpln/country="South Korea" | |aws-ap-northeast-3 |AWS, Asia Pacific (Osaka) |aws |true |ap-northeast-3 |builtin |cpln/city="Osaka" | | | | | | | |cpln/continent="Asia" | | | | | | | |cpln/country="Japan" | |aws-ap-south-1 |AWS, Asia Pacific (Mumbai) |aws |true |ap-south-1 |builtin |cpln/city="Mumbai" | | | | | | | |cpln/continent="Asia" | | | | | | | |cpln/country="India" | |aws-ap-southeast-1 |AWS, Asia Pacific (Singapore) |aws |true |ap-southeast-1 |builtin |cpln/city="Singapore" | | | | | | | |cpln/continent="Asia" | | | | | | | |cpln/country="Singapore" | |aws-ap-southeast-2 |AWS, Asia Pacific (Sydney) |aws |true |ap-southeast-2 |builtin |cpln/city="Sydney" | | | | | | | |cpln/continent="Australia" | | | | | | | |cpln/country="Australia" | |aws-ca-central-1 |AWS, Canada (Central) |aws |true |ca-central-1 |builtin |cpln/city="Montréal" | | | | | | | |cpln/continent="North America" | | | | | | | |cpln/country="Canada" | |aws-eu-central-1 |AWS, Europe (Frankfurt) |aws |true |eu-central-1 |builtin |cpln/city="Frankfurt" | | | | | | | |cpln/continent="Europe" | | | | | | | |cpln/country="Germany" | |aws-eu-north-1 |AWS, Europe (Stockholm) |aws |true |eu-north-1 |builtin |cpln/city="Stockholm" | | | | | | | |cpln/continent="Europe" | | | | | | | |cpln/country="Sweden" | |aws-eu-west-1 |AWS, Europe (Ireland) |aws |true |eu-west-1 |builtin |cpln/continent="Europe" | | | | | | | |cpln/country="Ireland" | |aws-eu-west-2 |AWS, Europe (London) |aws |true |eu-west-2 |builtin |cpln/city="London" | | | | | | | |cpln/continent="Europe" | | | | | | | |cpln/country="United Kingdom" | |aws-eu-west-3 |AWS, Europe (Paris) |aws |true |eu-west-3 |builtin |cpln/city="Paris" | | | | | | | |cpln/continent="Europe" | | | | | | | |cpln/country="France" | |aws-il-central-1 |AWS, Israel (Tel Aviv) |aws |true |il-central-1 |builtin |cpln/city="Tel Aviv" | | | | | | | |cpln/continent="Middle East" | | | | | | | |cpln/country="Israel" | |aws-sa-east-1 |AWS, South America (São Paulo) |aws |true |sa-east-1 |builtin |cpln/city="São Paulo" | | | | | | | |cpln/continent="South America" | | | | | | | |cpln/country="Brazil" | |aws-us-east-1 |AWS, US East (N. Virginia) |aws |true |us-east-1 |builtin |cpln/continent="North America" | | | | | | | |cpln/country="USA" | | | | | | | |cpln/state="VA" | |aws-us-east-2 |AWS, US East (Ohio) |aws |true |us-east-2 |builtin |cpln/continent="North America" | | | | | | | |cpln/country="USA" | | | | | | | |cpln/state="OH" | |aws-us-west-1 |AWS, US West (N. California) |aws |true |us-west-1 |builtin |cpln/continent="North America" | | | | | | | |cpln/country="USA" | | | | | | | |cpln/state="CA" | |aws-us-west-2 |AWS, US West (Oregon) |aws |true |us-west-2 |builtin |cpln/continent="North America" | | | | | | | |cpln/country="USA" | | | | | | | |cpln/state="OR" | |azure-eastus |Azure, East US |azure |true |eastus |builtin |cpln/continent="North America" | | | | | | | |cpln/country="USA" | | | | | | | |cpln/state="VA" | |azure-eastus2 |Azure, East US 2 |azure |true |eastus2 |builtin |cpln/continent="North America" | | | | | | | |cpln/country="USA" | | | | | | | |cpln/state="VA" | |azure-southcentralus |Azure, South Central US |azure |true |southcentralus |builtin |cpln/continent="North America" | | | | | | | |cpln/country="USA" | | | | | | | |cpln/state="TX" | |demo-byok-location |demo-byok-location |byok |true |demo-byok-location |custom | | |gcp-asia-east1 |GCP, Changhua County, Taiwan |gcp |true |asia-east1 |builtin |cpln/continent="Asia" | | | | | | | |cpln/country="Taiwan" | |gcp-asia-east2 |GCP, Hong Kong, China |gcp |true |asia-east2 |builtin |cpln/city="Hong Kong" | | | | | | | |cpln/continent="Asia" | | | | | | | |cpln/country="China" | |gcp-asia-northeast1 |GCP, Tokyo, Japan |gcp |true |asia-northeast1 |builtin |cpln/city="Tokyo" | | | | | | | |cpln/continent="Asia" | | | | | | | |cpln/country="Japan" | |gcp-asia-northeast2 |GCP, Osaka, Japan |gcp |true |asia-northeast2 |builtin |cpln/city="Osaka" | | | | | | | |cpln/continent="Asia" | | | | | | | |cpln/country="Japan" | |gcp-asia-northeast3 |GCP, Seoul, South Korea |gcp |true |asia-northeast3 |builtin |cpln/city="Seoul" | | | | | | | |cpln/continent="Asia" | | | | | | | |cpln/country="South Korea" | |gcp-asia-south1 |GCP, Mumbai, India |gcp |true |asia-south1 |builtin |cpln/city="Mumbai" | | | | | | | |cpln/continent="Asia" | | | | | | | |cpln/country="India" | |gcp-asia-south2 |GCP, Delhi, India |gcp |true |asia-south2 |builtin |cpln/city="Delhi" | | | | | | | |cpln/continent="Asia" | | | | | | | |cpln/country="India" | |gcp-asia-southeast1 |GCP, Jurong West, Singapore |gcp |true |asia-southeast1 |builtin |cpln/city="Jurong West" | | | | | | | |cpln/continent="Asia" | | | | | | | |cpln/country="Singapore" | |gcp-asia-southeast2 |GCP, Jakarta, Indonesia |gcp |true |asia-southeast2 |builtin |cpln/city="Jakarta" | | | | | | | |cpln/continent="Asia" | | | | | | | |cpln/country="Indonesia" | |gcp-australia-southeast1 |GCP, Sydney, Australia |gcp |true |australia-southeast1 |builtin |cpln/city="Sydney" | | | | | | | |cpln/continent="Australia" | | | | | | | |cpln/country="Australia" | |gcp-australia-southeast2 |GCP, Melbourne, Australia |gcp |true |australia-southeast2 |builtin |cpln/city="Melbourne" | | | | | | | |cpln/continent="Australia" | | | | | | | |cpln/country="Australia" | |gcp-europe-central2 |GCP, Warsaw, Poland, Europe |gcp |true |europe-central2 |builtin |cpln/city="Warsaw" | | | | | | | |cpln/continent="Europe" | | | | | | | |cpln/country="Poland" | |gcp-europe-north1 |GCP, Hamina, Finland, Europe |gcp |true |europe-north1 |builtin |cpln/city="Hamina" | | | | | | | |cpln/continent="Europe" | | | | | | | |cpln/country="Finland" | |gcp-europe-west1 |GCP, St. Ghislain, Belgium, Europe |gcp |true |europe-west1 |builtin |cpln/city="St. Ghislain" | | | | | | | |cpln/continent="Europe" | | | | | | | |cpln/country="Belgium" | |gcp-europe-west2 |GCP, London, England, Europe |gcp |true |europe-west2 |builtin |cpln/city="London" | | | | | | | |cpln/continent="Europe" | | | | | | | |cpln/country="England" | |gcp-europe-west3 |GCP, Frankfurt, Germany, Europe |gcp |true |europe-west3 |builtin |cpln/city="Frankfurt" | | | | | | | |cpln/continent="Europe" | | | | | | | |cpln/country="Germany" | |gcp-europe-west4 |GCP, Eemshaven, Netherlands, Europe |gcp |true |europe-west4 |builtin |cpln/city="Eemshaven" | | | | | | | |cpln/continent="Europe" | | | | | | | |cpln/country="Netherlands" | |gcp-europe-west6 |GCP, Zurich, Switzerland, Europe |gcp |true |europe-west6 |builtin |cpln/city="Zurich" | | | | | | | |cpln/continent="Europe" | | | | | | | |cpln/country="Switzerland" | |gcp-me-west1 |GCP, Tel Aviv, Israel, Middle East |gcp |true |me-west1 |builtin |cpln/city="Tel Aviv" | | | | | | | |cpln/continent="Middle East" | | | | | | | |cpln/country="Israel" | |gcp-northamerica-northeast1 |GCP, Montréal, Québec, Canada |gcp |true |northamerica-northeast1 |builtin |cpln/city="Montréal" | | | | | | | |cpln/continent="North America" | | | | | | | |cpln/country="Canada" | |gcp-northamerica-northeast2 |GCP, Toronto, Ontario, Canada |gcp |true |northamerica-northeast2 |builtin |cpln/city="Toronto" | | | | | | | |cpln/continent="North America" | | | | | | | |cpln/country="Canada" | |gcp-southamerica-east1 |GCP, Osasco, São Paulo, Brazil |gcp |true |southamerica-east1 |builtin |cpln/city="Osasco" | | | | | | | |cpln/continent="South America" | | | | | | | |cpln/country="Brazil" | |gcp-us-central1 |GCP, Council Bluffs, Iowa, USA |gcp |true |us-central1 |builtin |cpln/city="Council Bluffs" | | | | | | | |cpln/continent="North America" | | | | | | | |cpln/country="USA" | | | | | | | |cpln/state="IA" | |gcp-us-east1 |GCP, Moncks Corner, South Carolina, USA |gcp |true |us-east1 |builtin |cpln/continent="North America" | | | | | | | |cpln/country="USA" | | | | | | | |cpln/state="SC" | |gcp-us-east4 |GCP, Ashburn, Northern Virginia, USA |gcp |true |us-east4 |builtin |cpln/city="Ashburn" | | | | | | | |cpln/continent="North America" | | | | | | | |cpln/country="USA" | | | | | | | |cpln/state="VA" | |gcp-us-west1 |GCP, The Dalles, Oregon, USA |gcp |true |us-west1 |builtin |cpln/city="The Dalles" | | | | | | | |cpln/continent="North America" | | | | | | | |cpln/country="USA" | | | | | | | |cpln/state="OR" | |gcp-us-west2 |GCP, Los Angeles, California, USA |gcp |true |us-west2 |builtin |cpln/city="Los Angeles" | | | | | | | |cpln/continent="North America" | | | | | | | |cpln/country="USA" | | | | | | | |cpln/state="CA" | |gcp-us-west3 |GCP, Salt Lake City, Utah, USA |gcp |true |us-west3 |builtin |cpln/city="Salt Lake City" | | | | | | | |cpln/continent="North America" | | | | | | | |cpln/country="USA" | | | | | | | |cpln/state="UT" | +----------------------------+----------------------------------------+---------+--------+------------------------+--------+-------------------------------+ ``` * Get one location ```bash Command theme={null} cpln location get aws-eu-central-1 --org demo-cpln-organization ``` ```text Output theme={null} +-----------------+------------------------+---------+--------+-------------+--------+------------------------+ |NAME |DESCRIPTION |PROVIDER |ENABLED |REGION |ORIGIN |TAGS | |-----------------|------------------------|---------|--------|-------------|--------|------------------------| |aws-eu-central-1 |AWS, Europe (Frankfurt) |aws |true |eu-central-1 |builtin |cpln/city="Frankfurt" | | | | | | | |cpln/continent="Europe" | | | | | | | |cpln/country="Germany" | +-----------------+------------------------+---------+--------+-------------+--------+------------------------+ ``` * Get multiple locations ```bash Command theme={null} cpln location get gcp-us-east1 --org demo-cpln-organization ``` ```text Output theme={null} +-------------+----------------------------------------+---------+--------+---------+--------+-------------------------------+ |NAME |DESCRIPTION |PROVIDER |ENABLED |REGION |ORIGIN |TAGS | |-------------|----------------------------------------|---------|--------|---------|--------|-------------------------------| |gcp-us-east1 |GCP, Moncks Corner, South Carolina, USA |gcp |true |us-east1 |builtin |cpln/continent="North America" | | | | | | | |cpln/country="USA" | | | | | | | |cpln/state="SC" | +-------------+----------------------------------------+---------+--------+---------+--------+-------------------------------+ ``` *** #### location install Get instructions for obtaining the installation script for a BYOK location **Usage** ```none theme={null} cpln location install [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Obtain instructions to install a [BYOK](/byok/overview) location. ```bash Command theme={null} cpln location install demo-byok-location --org demo-cpln-organization ``` ```text Output theme={null} # this commmand must be performed before 2024-7-16 22:42:00 kubectl apply -f 'https://byok-api.cpln.io/install/download/xxxxxxxxxx/byok-agent.k8s.yaml' ``` *** #### location patch Update the referenced location's metadata using an input file **Usage** ```none theme={null} cpln location patch --file FILE_NAME [OPTIONS] ``` Running this command with the option **`--file -`** sets the input to stdin. The input is required to be a valid JSON or YAML string (e.g., `{"description": "My new description"}` or `description: My new description`). To complete the update, press Control-D (or Control-Z if you are using Windows). ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --file, -f File to load the patch from. Use `--file -` to enable input from stdin. [required] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln location patch aws-eu-central-1 --file update.json --org demo-cpln-organization ``` ```text Output theme={null} The changes within the JSON file will be performed against the referenced location. ``` *** #### location permissions Show the grantable permissions for a location object type **Usage** ```none theme={null} cpln location permissions [OPTIONS] ``` ```text theme={null} Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln location permissions --org demo-cpln-organization ``` ```text Output theme={null} +-------+-----------------------------------------+----------------------------------------+ |NAME |DESCRIPTION |IMPLIES | |-------|-----------------------------------------|----------------------------------------| |create |Create new locations | | |delete |Delete byok locations | | |edit |Modify existing locations |view | |manage |Full access |create, delete, edit, manage, use, view | |use |Use this location for workload placement |view | |view |Read-only access | | +-------+-----------------------------------------+----------------------------------------+ ``` *** #### location query Find all the locations based on the given query **Usage** ```none theme={null} cpln location query QUERY_OPTIONS [OPTIONS] ``` ```text theme={null} Query options: --match Query filter type [choices: "all", "none", "any"] [default: "all"] --property, --prop Property to query --tag Tag to query --rel Relation to query Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) Use the `--match` option to display **all**, **none**, or **any** of the results based on a given property or tag * Search for a location based on the name property ```bash Command theme={null} cpln location query --match any --prop name=aws-eu-central-1 --org demo-cpln-organization ``` ```text Output theme={null} +-----------------+------------------------+---------+--------+-------------+--------+------------------------+ |NAME |DESCRIPTION |PROVIDER |ENABLED |REGION |ORIGIN |TAGS | |-----------------|------------------------|---------|--------|-------------|--------|------------------------| |aws-eu-central-1 |AWS, Europe (Frankfurt) |aws |true |eu-central-1 |builtin |cpln/city="Frankfurt" | | | | | | | |cpln/continent="Europe" | | | | | | | |cpln/country="Germany" | +-----------------+------------------------+---------+--------+-------------+--------+------------------------+ ``` * Search for a location based on the description property ```bash Command theme={null} cpln location query --match any --prop description="Azure, East US 2" --org demo-cpln-organization ``` ```text Output theme={null} +--------------+-----------------+---------+--------+--------+--------+-------------------------------+ |NAME |DESCRIPTION |PROVIDER |ENABLED |REGION |ORIGIN |TAGS | |--------------|-----------------|---------|--------|--------|--------|-------------------------------| |azure-eastus2 |Azure, East US 2 |azure |true |eastus2 |builtin |cpln/continent="North America" | | | | | | | |cpln/country="USA" | | | | | | | |cpln/state="VA" | +--------------+-----------------+---------+--------+--------+--------+-------------------------------+ ``` * Search for a location based on a tag ```bash Command theme={null} cpln location query --match any --tag cpln/city="Frankfurt" --org demo-cpln-organization ``` ```text Output theme={null} +-----------------+--------------------------------+---------+--------+-------------+--------+------------------------+ |NAME |DESCRIPTION |PROVIDER |ENABLED |REGION |ORIGIN |TAGS | |-----------------|--------------------------------|---------|--------|-------------|--------|------------------------| |aws-eu-central-1 |AWS, Europe (Frankfurt) |aws |true |eu-central-1 |builtin |cpln/city="Frankfurt" | | | | | | | |cpln/continent="Europe" | | | | | | | |cpln/country="Germany" | |gcp-europe-west3 |GCP, Frankfurt, Germany, Europe |gcp |true |europe-west3 |builtin |cpln/city="Frankfurt" | | | | | | | |cpln/continent="Europe" | | | | | | | |cpln/country="Germany" | +-----------------+--------------------------------+---------+--------+-------------+--------+------------------------+ ``` * Search for a location based on a property and tag ```bash Command theme={null} cpln location query --match any --prop description="Azure, East US 2" --tag cpln/city="Frankfurt" --org demo-cpln-organization ``` ```text Output theme={null} +-----------------+--------------------------------+---------+--------+-------------+--------+-------------------------------+ |NAME |DESCRIPTION |PROVIDER |ENABLED |REGION |ORIGIN |TAGS | |-----------------|--------------------------------|---------|--------|-------------|--------|-------------------------------| |aws-eu-central-1 |AWS, Europe (Frankfurt) |aws |true |eu-central-1 |builtin |cpln/city="Frankfurt" | | | | | | | |cpln/continent="Europe" | | | | | | | |cpln/country="Germany" | |azure-eastus2 |Azure, East US 2 |azure |true |eastus2 |builtin |cpln/continent="North America" | | | | | | | |cpln/country="USA" | | | | | | | |cpln/state="VA" | |gcp-europe-west3 |GCP, Frankfurt, Germany, Europe |gcp |true |europe-west3 |builtin |cpln/city="Frankfurt" | | | | | | | |cpln/continent="Europe" | | | | | | | |cpln/country="Germany" | +-----------------+--------------------------------+---------+--------+-------------+--------+-------------------------------+ ``` *** #### location tag Manage the tags belonging to one or more referenced locations **Usage** ```none theme={null} cpln location tag --tag KEY=VALUE [OPTIONS] ``` Built-in tags, which begin with cpln/\*, can't be modified. ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [required] [default: []] Command options: --tag Attach tags (e.g., --tag drink=water) --remove-tag Remove tags (e.g., --remove-tag tagname) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Add a tag to a location ```bash Command theme={null} cpln location tag aws-eu-central-1 --tag drink=water --org demo-cpln-organization ``` ```text Output theme={null} +-----------------+------------------------+---------+--------+-------------+--------+------------------------+ |NAME |DESCRIPTION |PROVIDER |ENABLED |REGION |ORIGIN |TAGS | |-----------------|------------------------|---------|--------|-------------|--------|------------------------| |aws-eu-central-1 |AWS, Europe (Frankfurt) |aws |true |eu-central-1 |builtin |cpln/city="Frankfurt" | | | | | | | |cpln/continent="Europe" | | | | | | | |cpln/country="Germany" | | | | | | | |drink="water" | +-----------------+------------------------+---------+--------+-------------+--------+------------------------+ ``` * Update same tag (after running the example above) ```bash Command theme={null} cpln location tag aws-eu-central-1 --tag drink=coffee --org demo-cpln-organization ``` ```text Output theme={null} +-----------------+------------------------+---------+--------+-------------+--------+------------------------+ |NAME |DESCRIPTION |PROVIDER |ENABLED |REGION |ORIGIN |TAGS | |-----------------|------------------------|---------|--------|-------------|--------|------------------------| |aws-eu-central-1 |AWS, Europe (Frankfurt) |aws |true |eu-central-1 |builtin |cpln/city="Frankfurt" | | | | | | | |cpln/continent="Europe" | | | | | | | |cpln/country="Germany" | | | | | | | |drink="coffee" | +-----------------+------------------------+---------+--------+-------------+--------+------------------------+ ``` * Remove tag (after running the example above) ```bash Command theme={null} cpln location tag aws-eu-central-1 --remove drink --org demo-cpln-organization ``` ```text Output theme={null} +-----------------+------------------------+---------+--------+-------------+--------+------------------------+ |NAME |DESCRIPTION |PROVIDER |ENABLED |REGION |ORIGIN |TAGS | |-----------------|------------------------|---------|--------|-------------|--------|------------------------| |aws-eu-central-1 |AWS, Europe (Frankfurt) |aws |true |eu-central-1 |builtin |cpln/city="Frankfurt" | | | | | | | |cpln/continent="Europe" | | | | | | | |cpln/country="Germany" | +-----------------+------------------------+---------+--------+-------------+--------+------------------------+ ``` * Add same tag to multiple locations ```bash Command theme={null} cpln location tag aws-eu-central-1 azure-eastus2 --tag multiple-locations=true --org demo-cpln-organization ``` ```text Output theme={null} +-----------------+------------------------+---------+--------+-------------+--------+-------------------------------+ |NAME |DESCRIPTION |PROVIDER |ENABLED |REGION |ORIGIN |TAGS | |-----------------|------------------------|---------|--------|-------------|--------|-------------------------------| |aws-eu-central-1 |AWS, Europe (Frankfurt) |aws |true |eu-central-1 |builtin |cpln/city="Frankfurt" | | | | | | | |cpln/continent="Europe" | | | | | | | |cpln/country="Germany" | | | | | | | |multiple-locations="true" | |azure-eastus2 |Azure, East US 2 |azure |true |eastus2 |builtin |cpln/continent="North America" | | | | | | | |cpln/country="USA" | | | | | | | |cpln/state="VA" | | | | | | | |multiple-locations="true" | +-----------------+------------------------+---------+--------+-------------+--------+-------------------------------+ ``` * Update same tag on multiple locations (after running the example above) ```bash Command theme={null} cpln location tag aws-eu-central-1 azure-eastus2 --tag multiple-locations=demo-location --org demo-cpln-organization ``` ```text Output theme={null} +-----------------+------------------------+---------+--------+-------------+--------+-----------------------------------+ |NAME |DESCRIPTION |PROVIDER |ENABLED |REGION |ORIGIN |TAGS | |-----------------|------------------------|---------|--------|-------------|--------|-----------------------------------| |aws-eu-central-1 |AWS, Europe (Frankfurt) |aws |true |eu-central-1 |builtin |cpln/city="Frankfurt" | | | | | | | |cpln/continent="Europe" | | | | | | | |cpln/country="Germany" | | | | | | | |multiple-locations="demo-location" | |azure-eastus2 |Azure, East US 2 |azure |true |eastus2 |builtin |cpln/continent="North America" | | | | | | | |cpln/country="USA" | | | | | | | |cpln/state="VA" | | | | | | | |multiple-locations="demo-location" | +-----------------+------------------------+---------+--------+-------------+--------+-----------------------------------+ ``` * Remove same tag from multiple locations (after running the example above) ```bash Command theme={null} cpln location tag aws-eu-central-1 azure-eastus2 --remove multiple-locations --org demo-cpln-organization ``` ```text Output theme={null} +-----------------+------------------------+---------+--------+-------------+--------+-------------------------------+ |NAME |DESCRIPTION |PROVIDER |ENABLED |REGION |ORIGIN |TAGS | |-----------------|------------------------|---------|--------|-------------|--------|-------------------------------| |aws-eu-central-1 |AWS, Europe (Frankfurt) |aws |true |eu-central-1 |builtin |cpln/city="Frankfurt" | | | | | | | |cpln/continent="Europe" | | | | | | | |cpln/country="Germany" | |azure-eastus2 |Azure, East US 2 |azure |true |eastus2 |builtin |cpln/continent="North America" | | | | | | | |cpln/country="USA" | | | | | | | |cpln/state="VA" | +-----------------+------------------------+---------+--------+-------------+--------+-------------------------------+ ``` *** #### location uninstall Get instructions for obtaining the uninstallation script for a BYOK location **Usage** ```none theme={null} cpln location uninstall [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Obtain instructions to uninstall a [BYOK](/byok/overview) location. ```bash Command theme={null} cpln location uninstall demo-byok-location --org demo-cpln-organization ``` ```text Output theme={null} # this commmand must be performed before 2024-7-16 22:41:00 kubectl apply -f 'https://byok-api.cpln.io/install/download/xxxxxxxxxx/byok-agent-uninstall.k8s.yaml' ``` *** # login Source: https://docs.controlplane.com/cli-reference/commands/login cpln login — Create a default profile and authenticate with Control Plane using a login URL and confirmation code. Create a profile named "default" and print a URL and a confirmation code to complete sign-in in any browser **Usage** ```none theme={null} cpln login [ref] [OPTIONS] ``` ```text theme={null} Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Positionals: ref The resource reference. Usually it is the name of the resource. Options: --help Show help [boolean] --version, -v Show version number [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln login ``` ```text Output theme={null} First, copy your confirmation code: 960619 Opening your browser to log in. Browser didn't open? Use the URL below: https://auth.cpln.io/cli-login?id=A1b2C3d4E5f6G7h8I9j0KlMnOpQrStUvWxYz Waiting for login to complete... (code expires in ~10 minutes) ``` *** # logs Source: https://docs.controlplane.com/cli-reference/commands/logs cpln logs — Query and display logs from workloads and other Control Plane resources. Show logs For a detailed overview, please visit the [logs'](/reference/logs) reference page. **Usage** ```none theme={null} cpln logs [OPTIONS] ``` The `` positional requires a valid [LogQL](https://grafana.com/docs/loki/latest/logql/) string enclosed within single quotes. Available query labels: `gvc`, `workload`, `container`, `location`, `provider`, `replica`, `stream` ```text theme={null} Command options: --tail, -t, -f Tail the logs (follow) [boolean] --limit Maximum number of log entries to return. Use 0 for unlimited (auto-paginates through the full time range) [number] [default: 30] --delay-for Delay in tailing by number of seconds to accumulate logs for re-ordering [number] [default: 0] --since Lookback window [default: "1h"] --from Start looking for logs at this time, inclusive (ISO 8601 or relative duration, e.g., 2025-10-23T07:00:00Z, 7d, now-1M) --to Stop looking for logs at this time, exclusive (ISO 8601 or relative duration, e.g., 2025-10-23T07:00:00Z, 1d, now-30m) --output, -o Specify output mode. raw suppresses log labels and timestamp [choices: "default", "raw", "jsonl"] [default: "default"] --direction Sort order of logs [choices: "forward", "backward"] [default: "forward"] Positionals: query LogQL query [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] --version, -v Show version number [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln logs '{gvc="demo-gvc-logs",workload="demo-workload-logs"}' ``` ```text Output theme={null} 2021-02-23T18:45:03.411Z {workload="demo-workload-logs", container="main", gvc="demo-gvc-logs", location="aws-eu-central-1", provider="aws", replica="712438d4-aee8-44aa-b7a9-c9ed62b6ea69", stream="stderr"} 2021/02/23 18:45:03 Hello world sample started. 2021-02-23T18:45:06.177Z {workload="demo-workload-logs", container="main", gvc="demo-gvc-logs", location="aws-eu-central-1", provider="aws", replica="712438d4-aee8-44aa-b7a9-c9ed62b6ea69", stream="stderr"} 2021/02/23 18:45:06 Hello world received a request. ``` ```bash Command theme={null} cpln logs '{provider="aws"}' ``` ```text Output theme={null} 2021-02-23T18:45:03.411Z {container="main", gvc="demo-gvc-logs", location="aws-eu-central-1", provider="aws", replica="712438d4-aee8-44aa-b7a9-c9ed62b6ea69", stream="stderr", workload="demo-workload-logs"} 2021/02/23 18:45:03 Hello world sample started. 2021-02-23T18:45:06.177Z {container="main", gvc="demo-gvc-logs", location="aws-eu-central-1", provider="aws", replica="712438d4-aee8-44aa-b7a9-c9ed62b6ea69", stream="stderr", workload="demo-workload-logs"} 2021/02/23 18:45:06 Hello world received a request. ``` *** # metrics Source: https://docs.controlplane.com/cli-reference/commands/metrics cpln metrics — Run PromQL queries against your organization's metrics. Query metrics ```none theme={null} cpln metrics [SUBCOMMAND] ``` ```text theme={null} Commands: cpln metrics query [expr] Execute a PromQL query Options: --help Show help [boolean] ``` *** #### metrics query Execute a PromQL query **Usage** ```none theme={null} cpln metrics query [OPTIONS] ``` The PromQL expression can be passed as the positional argument or through the `--expr` option. Enclose it in single quotes so the shell does not expand it. ```text theme={null} Query options: --expr PromQL expression (alternative to the positional argument) --from Range start (ISO 8601, Unix timestamp, or relative duration, e.g., 2025-10-23T07:00:00Z, 7d, now-1h) --to Range end (ISO 8601, Unix timestamp, or relative duration; defaults to now) --since Lookback window before --to (or now); mutually exclusive with --from (default 1h when a range is requested) --step Range query resolution step, e.g., 15s, 1m, 5m (default 60s) --time Evaluation time for an instant query (ISO 8601, Unix timestamp, or relative like now-5m); mutually exclusive with --from/--to/--since Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Instant query ```bash Command theme={null} cpln metrics query 'up' --org demo-cpln-organization ``` ```text Output theme={null} +-------+--------------------------------------------+--------------------------+-------+ |METRIC |LABELS |TIMESTAMP |VALUE | |-------|--------------------------------------------|--------------------------|-------| |up |{workload="api", location="aws-us-west-2"} |2026-07-17T12:00:00.000Z |1 | |up |{workload="web", location="aws-us-west-2"} |2026-07-17T12:00:00.000Z |1 | +-------+--------------------------------------------+--------------------------+-------+ ``` * Range query over the last hour, sampled every five minutes ```bash Command theme={null} cpln metrics query 'sum(rate(http_requests_total[5m]))' --since 1h --step 5m --org demo-cpln-organization ``` ```text Output theme={null} +-------+--------+--------------------------+-------+ |METRIC |LABELS |TIMESTAMP |VALUE | |-------|--------|--------------------------|-------| | | |2026-07-17T11:00:00.000Z |42.7 | | | |2026-07-17T11:05:00.000Z |45.1 | | | |2026-07-17T11:10:00.000Z |39.8 | +-------+--------+--------------------------+-------+ ``` *** # misc Source: https://docs.controlplane.com/cli-reference/commands/misc cpln misc — Miscellaneous helper commands including shell completion and utility operations. Miscellaneous helper commands For a detailed guide on setting up shell completion, see [Shell Completion](/cli-reference/using-cli/shell-completion). ```none theme={null} cpln misc [SUBCOMMAND] ``` ```text theme={null} Commands: cpln misc install-completion Install shell completion to your local profile cpln misc uninstall-completion Uninstall shell completion from your local profile Options: --help Show help [boolean] --version, -v Show version number [boolean] ``` *** #### misc install-completion Install shell completion to your local profile. Supports Bash, Zsh, and Fish shells **Usage** ```none theme={null} cpln misc install-completion [OPTIONS] ``` ```text theme={null} Command options: --batch, -b Non-interactive (batch) mode [boolean] --shell Shell to install completion for (bash, zsh, fish). Only used with --batch [string] [choices: "bash", "zsh", "fish"] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] --version, -v Show version number [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln misc install-completion ``` ```text Output theme={null} Running the command will prompt the user with a few questions. Once compelete, the completion functionality will be installed. The shell will need to be restarted. ``` *** #### misc uninstall-completion Uninstall shell completion from your local profile **Usage** ```none theme={null} cpln misc uninstall-completion [OPTIONS] ``` ```text theme={null} Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] --version, -v Show version number [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln misc uninstall-completion ``` ```text Output theme={null} The completion functionality will be removed. ``` *** # mk8s Source: https://docs.controlplane.com/cli-reference/commands/mk8s cpln mk8s — Manage mk8s clusters for running managed Kubernetes on Control Plane. Manage an mk8s cluster ```none theme={null} cpln mk8s [SUBCOMMAND] ``` ```text theme={null} Commands: cpln mk8s access-report Show the access report for the referenced mk8s cluster cpln mk8s audit [ref] Retrieve audit trail events for the referenced mk8s cluster cpln mk8s clone Create a clone of the referenced mk8s cluster; this will only duplicate its spec. [aliases: copy] cpln mk8s dashboard Open the k8s dashboard for an mk8s cluster cpln mk8s delete Delete one or more referenced mk8s clusters cpln mk8s edit Edit the referenced mk8s cluster, as YAML, within an editor cpln mk8s eventlog Show the event log of the referenced mk8s cluster [aliases: log] cpln mk8s get [ref...] Retrieve one or more referenced mk8s cluster cpln mk8s health Show the readiness status of an mk8s cluster cpln mk8s join Join compute nodes to a cluster cpln mk8s kubeconfig Create a kubeconfig for a cluster. cpln mk8s patch Update the referenced mk8s cluster's metadata using an input file cpln mk8s permissions Show the grantable permissions for an mk8s cluster object type cpln mk8s query Find all the mk8s clusters based on the given query cpln mk8s tag Manage the tags belonging to one or more referenced mk8s clusters cpln mk8s update Update properties of the referenced mk8s cluster Options: --help Show help [boolean] ``` *** #### mk8s access-report Show the access report for the referenced mk8s cluster **Usage** ```none theme={null} cpln mk8s access-report [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### mk8s audit Retrieve audit trail events for the referenced mk8s cluster **Usage** ```none theme={null} cpln mk8s audit [ref] [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. Command options: --subject Filter by subject (serviceaccount name or user email) [string] --context Audit context name (the 'cpln' context displays Control Plane actions) [string] [default: "cpln"] --since Relative lookback window from now (e.g., 1h, 24h, 7d). Mutually exclusive with --from/--to [string] [default: "7d"] --from Start time for audit events (ISO 8601 format or relative duration, e.g., 2025-10-23T07:00:00Z, 7d, 1h). Cannot be used with --since [string] --to End time for audit events (ISO 8601 format or relative duration, e.g., 2025-10-23T07:00:00Z, 1d, 30m) [string] Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### mk8s clone Create a clone of the referenced mk8s cluster; this will only duplicate its spec. **Usage** ```none theme={null} cpln mk8s clone --name CLONE_NAME [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --name Set the name for the clone [required] --description Optional description, defaults to the name if not set --tag Attach tags (e.g., --tag drink=water) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### mk8s dashboard Open the k8s dashboard for an mk8s cluster **Usage** ```none theme={null} cpln mk8s dashboard [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --tag Attach tags (e.g., --tag drink=water) Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### mk8s delete Delete one or more referenced mk8s clusters **Usage** ```none theme={null} cpln mk8s delete [OPTIONS] ``` ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [required] [default: []] Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### mk8s edit Edit the referenced mk8s cluster, as YAML, within an editor **Usage** ```none theme={null} cpln mk8s edit [OPTIONS] ``` The default editor can be set by using the EDITOR environment variable. ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --replace, -r Replace instead of patch/merge [boolean] [default: false] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### mk8s eventlog Show the event log of the referenced mk8s cluster **Usage** ```none theme={null} cpln mk8s eventlog [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### mk8s get Retrieve one or more referenced mk8s cluster **Usage** ```none theme={null} cpln mk8s get [ref...] [OPTIONS] ``` An empty reference will return all references ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [default: []] Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### mk8s health Show the readiness status of an mk8s cluster **Usage** ```none theme={null} cpln mk8s health [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### mk8s join Join compute nodes to a cluster **Usage** ```none theme={null} cpln mk8s join [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Command options: --type Type of join configuration to produce: * cloud-init: produces a cloud-init script suitable for cloud deployments. * join-script: results in a simple script that can be evaluated as root on a node [required] [choices: "cloud-init", "join-script"] --options Some providers support extra options (e.g., --options nodepool=ingress) Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### mk8s kubeconfig Create a kubeconfig for a cluster. **Usage** ```none theme={null} cpln mk8s kubeconfig [OPTIONS] ``` ```text theme={null} Command options: --file, -f file to save kubeconfig to, default is $KUBECONFIG if set, otherwise ~/.kube/config. Use `--file -` to dump to stdout. New kubeconfig will be merged into the existing one Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### mk8s patch Update the referenced mk8s cluster's metadata using an input file **Usage** ```none theme={null} cpln mk8s patch --file FILE_NAME [OPTIONS] ``` Running this command with the option **`--file -`** sets the input to stdin. The input is required to be a valid JSON or YAML string (e.g., `{"description": "My new description"}` or `description: My new description`). To complete the update, press Control-D (or Control-Z if you are using Windows). ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --file, -f File to load the patch from. Use `--file -` to enable input from stdin. [required] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### mk8s permissions Show the grantable permissions for an mk8s cluster object type **Usage** ```none theme={null} cpln mk8s permissions [OPTIONS] ``` ```text theme={null} Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### mk8s query Find all the mk8s clusters based on the given query **Usage** ```none theme={null} cpln mk8s query QUERY_OPTIONS [OPTIONS] ``` ```text theme={null} Query options: --match Query filter type [choices: "all", "none", "any"] [default: "all"] --property, --prop Property to query --tag Tag to query --rel Relation to query Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### mk8s tag Manage the tags belonging to one or more referenced mk8s clusters **Usage** ```none theme={null} cpln mk8s tag --tag KEY=VALUE [OPTIONS] ``` Built-in tags, which begin with cpln/\*, can't be modified. ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [required] [default: []] Command options: --tag Attach tags (e.g., --tag drink=water) --remove-tag Remove tags (e.g., --remove-tag tagname) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### mk8s update Update properties of the referenced mk8s cluster **Usage** ```none theme={null} cpln mk8s update --set UPDATE_PROPERTY [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Update Properties: --set Update the following properties (e.g., --set description="Updated Description"): description string tags. string spec.version string --unset Unset a property (same paths as --set, e.g., --unset description) Command options: --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** # operator Source: https://docs.controlplane.com/cli-reference/commands/operator cpln operator — Manage the Kubernetes operator that syncs Control Plane resources with a cluster. A Kubernetes operator for Control Plane Refer to the [cpln operator guide](/guides/cli/cpln-operator) for additional details. ```none theme={null} cpln operator [SUBCOMMAND] ``` ```text theme={null} Commands: cpln operator install Install the Kubernetes operator to your cluster cpln operator uninstall Uninstall the Kubernetes operator from your cluster Options: --help Show help [boolean] ``` *** #### operator install Install the Kubernetes operator to your cluster **Usage** ```none theme={null} cpln operator install [OPTIONS] ``` Use `--export` to review or store the Kubernetes resources before applying: ```bash theme={null} # Export to a file for review or GitOps cpln operator install \ --serviceaccount k8s-operator \ --org YOUR_ORG_NAME \ --export > operator-secret.yaml # Apply later kubectl apply -f operator-secret.yaml ``` For production, assign the service account to a group with limited permissions: ```bash theme={null} cpln operator install \ --serviceaccount k8s-operator \ --serviceaccount-group operators \ --org YOUR_ORG_NAME ``` ```text theme={null} Command options: --serviceaccount, -s The service account name for the operator. If the service account does not exist, a new one will be created [required] --serviceaccount-group, -g The group to assign to the service account if it will be created. [default: "superusers"] --export Export the Kubernetes resources to a file instead of applying them to the cluster. [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### operator uninstall Uninstall the Kubernetes operator from your cluster **Usage** ```none theme={null} cpln operator uninstall [OPTIONS] ``` ```text theme={null} Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** # org Source: https://docs.controlplane.com/cli-reference/commands/org cpln org — Manage organizations, which are the top-level resource for grouping all other resources. Manage organizations For a detailed overview, please visit the [org's](/reference/org) reference page. ```none theme={null} cpln org [SUBCOMMAND] ``` ```text theme={null} Commands: cpln org access-report Show the access report for the referenced organization cpln org audit [ref] Retrieve audit trail events for the referenced organization cpln org create Create a new organization cpln org edit Edit the referenced organization, as YAML, within an editor cpln org eventlog Show the event log of the referenced organization [aliases: log] cpln org get [ref...] Retrieve one or more referenced organizations cpln org patch Update the referenced organization's metadata using an input file cpln org permissions Show the grantable permissions for an organization object type cpln org query Find all the organizations based on the given query cpln org tag Manage the tags belonging to one or more referenced organizations cpln org update Update properties of the referenced organization Options: --help Show help [boolean] ``` *** #### org access-report Show the access report for the referenced organization **Usage** ```none theme={null} cpln org access-report [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln org access-report demo-cpln-organization ``` ```text Output theme={null} +-----------------+--------------------------------------------------------+--------+ |PERMISSION |PRINCIPAL |GRANTED | |-----------------|--------------------------------------------------------|--------| |edit |/org/demo-cpln-organization/group/superusers |manage | |edit |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |exec |/org/demo-cpln-organization/group/superusers |manage | |exec |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |exec.echo |/org/demo-cpln-organization/group/superusers |manage | |exec.echo |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |grafanaAdmin |/org/demo-cpln-organization/group/superusers |manage | |grafanaAdmin |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |manage |/org/demo-cpln-organization/group/superusers |manage | |manage |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |readLogs |/org/demo-cpln-organization/group/superusers |manage | |readLogs |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |readMetrics |/org/demo-cpln-organization/group/superusers |manage | |readMetrics |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |readUsage |/org/demo-cpln-organization/group/superusers |manage | |readUsage |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |view |/org/demo-cpln-organization/group/superusers |manage | |view |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |view |/org/demo-cpln-organization/group/-allmembers |view | |viewAccessReport |/org/demo-cpln-organization/group/superusers |manage | |viewAccessReport |/org/demo-cpln-organization/serviceaccount/controlplane |manage | +-----------------+--------------------------------------------------------+--------+ ``` *** #### org audit Retrieve audit trail events for the referenced organization **Usage** ```none theme={null} cpln org audit [ref] [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. Command options: --subject Filter by subject (serviceaccount name or user email) [string] --context Audit context name (the 'cpln' context displays Control Plane actions) [string] [default: "cpln"] --since Relative lookback window from now (e.g., 1h, 24h, 7d). Mutually exclusive with --from/--to [string] [default: "7d"] --from Start time for audit events (ISO 8601 format or relative duration, e.g., 2025-10-23T07:00:00Z, 7d, 1h). Cannot be used with --since [string] --to End time for audit events (ISO 8601 format or relative duration, e.g., 2025-10-23T07:00:00Z, 1d, 30m) [string] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### org create Create a new organization **Usage** ```none theme={null} cpln org create --name ORG_NAME [OPTIONS] ``` ```text theme={null} Command options: --accountId Id of the billing account [required] --name Name of the new organization [required] --description, --desc Optional description, defaults to the name if not set --invitee Emails of users to invite to the org as superuser(admin) [required] --tag Attach tags (e.g., --tag drink=water) Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * `name` * Minimum Length: 3 * Maximum Length: 64 * Only lowercase characters * Must begin with a character * Allowed special characters: - ```bash Command theme={null} cpln org create --name demo-cpln-organization --acountId ACCOUNT_ID --invitee EMAIL ``` ```text Output theme={null} If successful, the new organization will be created. ``` *** #### org edit Edit the referenced organization, as YAML, within an editor **Usage** ```none theme={null} cpln org edit [OPTIONS] ``` The default editor can be set by using the EDITOR environment variable. ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --replace, -r Replace instead of patch/merge [boolean] [default: false] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln org edit demo-cpln-organization ``` ```text Output theme={null} Your default editor will be launched. Once any modifications are saved, the updates will be sent to the API. ``` *** #### org eventlog Show the event log of the referenced organization **Usage** ```none theme={null} cpln org eventlog [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln org eventlog demo-cpln-organization ``` ```text Output theme={null} The event log for the referenced organization will be shown. ``` *** #### org get Retrieve one or more referenced organizations **Usage** ```none theme={null} cpln org get [ref...] [OPTIONS] ``` An empty reference will return all references ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [default: []] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Get all organizations ```bash Command theme={null} cpln org get ``` ```text Output theme={null} +-----------------------+------------------+----------------------------+----------------------+ |NAME |DESCRIPTION |LINK |TAGS | |-----------------------|------------------|----------------------------|----------------------| |demo-cpln-organization |Demo Organization |/org/demo-cpln-organization |demo-key="demo-value" | | | | |type="demo" | +-----------------------+------------------+----------------------------+----------------------+ ``` * Get one organization ```bash Command theme={null} cpln org get demo-cpln-organization ``` ```text Output theme={null} +-----------------------+------------------+----------------------------+----------------------+ |NAME |DESCRIPTION |LINK |TAGS | |-----------------------|------------------|----------------------------|----------------------| |demo-cpln-organization |Demo Organization |/org/demo-cpln-organization |demo-key="demo-value" | | | | |type="demo" | +-----------------------+------------------+----------------------------+----------------------+ ``` *** #### org patch Update the referenced organization's metadata using an input file **Usage** ```none theme={null} cpln org patch --file FILE_NAME [OPTIONS] ``` Running this command with the option **`--file -`** sets the input to stdin. The input is required to be a valid JSON or YAML string (e.g., `{"description": "My new description"}` or `description: My new description`). To complete the update, press Control-D (or Control-Z if you are using Windows). ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --file, -f File to load the patch from. Use `--file -` to enable input from stdin. [required] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln org patch demo-cpln-organization --file update.json ``` ```text Output theme={null} The changes within the JSON file will be performed against the referenced organization. ``` *** #### org permissions Show the grantable permissions for an organization object type **Usage** ```none theme={null} cpln org permissions [OPTIONS] ``` ```text theme={null} Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln org permissions ``` ```text Output theme={null} +-----------------+-----------------------------------------+------------------------------------------------------------------------------------------------------+ |NAME |DESCRIPTION |IMPLIES | |-----------------|-----------------------------------------|------------------------------------------------------------------------------------------------------| |edit |Modify org |view | |exec |Grantees can execute all commands on ... |exec.echo | |exec.echo |Grantees can execute the echo command | | |grafanaAdmin |Grantees are made Admin in Grafana, o... | | |manage |Full access |edit, exec, exec.echo, grafanaAdmin, manage, readLogs, readMetrics, readUsage, view, viewAccessReport | |readLogs |Grantees can read logs from all workl... |view | |readMetrics |Grantees can access usage and perform... | | |readUsage |Grantees can access usage and billing... | | |view |Read-only view: every org member can ... | | |viewAccessReport |Grantees can inspect the granted acce... | | +-----------------+-----------------------------------------+------------------------------------------------------------------------------------------------------+ ``` *** #### org query Find all the organizations based on the given query **Usage** ```none theme={null} cpln org query QUERY_OPTIONS [OPTIONS] ``` ```text theme={null} Query options: --match Query filter type [choices: "all", "none", "any"] [default: "all"] --property, --prop Property to query --tag Tag to query --rel Relation to query Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) Use the `--match` option to display **all**, **none**, or **any** of the results based on a given property or tag * Search for an organization based on the name property ```bash Command theme={null} cpln org query --match any --prop name=demo-cpln-organization ``` ```text Output theme={null} +-----------------------+------------------+----------------------------+----------------------+ |NAME |DESCRIPTION |LINK |TAGS | |-----------------------|------------------|----------------------------|----------------------| |demo-cpln-organization |Demo Organization |/org/demo-cpln-organization |demo-key="demo-value" | | | | |type="demo" | +-----------------------+------------------+----------------------------+----------------------+ ``` * Search for an organization based on the description property ```bash Command theme={null} cpln org query --match any --prop description="Demo Organization" ``` ```text Output theme={null} +-----------------------+------------------+----------------------------+----------------------+ |NAME |DESCRIPTION |LINK |TAGS | |-----------------------|------------------|----------------------------|----------------------| |demo-cpln-organization |Demo Organization |/org/demo-cpln-organization |demo-key="demo-value" | | | | |type="demo" | +-----------------------+------------------+----------------------------+----------------------+ ``` * Search for an organization based on a tag ```bash Command theme={null} cpln org query --match any --tag type=demo ``` ```text Output theme={null} +-----------------------+------------------+----------------------------+----------------------+ |NAME |DESCRIPTION |LINK |TAGS | |-----------------------|------------------|----------------------------|----------------------| |demo-cpln-organization |Demo Organization |/org/demo-cpln-organization |demo-key="demo-value" | | | | |type="demo" | +-----------------------+------------------+----------------------------+----------------------+ ``` * Search for an organization based on a property and tag ```bash Command theme={null} cpln org query --match any --prop description="Demo Organization" --tag type=demo ``` ```text Output theme={null} +-----------------------+------------------+----------------------------+----------------------+ |NAME |DESCRIPTION |LINK |TAGS | |-----------------------|------------------|----------------------------|----------------------| |demo-cpln-organization |Demo Organization |/org/demo-cpln-organization |demo-key="demo-value" | | | | |type="demo" | +-----------------------+------------------+----------------------------+----------------------+ ``` *** #### org tag Manage the tags belonging to one or more referenced organizations **Usage** ```none theme={null} cpln org tag --tag KEY=VALUE [OPTIONS] ``` Built-in tags, which begin with cpln/\*, can't be modified. ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [required] [default: []] Command options: --tag Attach tags (e.g., --tag drink=water) --remove-tag Remove tags (e.g., --remove-tag tagname) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Add a tag ```bash Command theme={null} cpln org tag demo-cpln-organization --tag drink=water ``` ```text Output theme={null} +-----------------------+------------------+----------------------------+----------------------+ |NAME |DESCRIPTION |LINK |TAGS | |-----------------------|------------------|----------------------------|----------------------| |demo-cpln-organization |Demo Organization |/org/demo-cpln-organization |demo-key="demo-value" | | | | |drink="water" | | | | |type="demo" | +-----------------------+------------------+----------------------------+----------------------+ ``` * Update same tag (after running the example above) ```bash Command theme={null} cpln org tag demo-cpln-organization --tag drink=coffee ``` ```text Output theme={null} +-----------------------+------------------+----------------------------+----------------------+ |NAME |DESCRIPTION |LINK |TAGS | |-----------------------|------------------|----------------------------|----------------------| |demo-cpln-organization |Demo Organization |/org/demo-cpln-organization |demo-key="demo-value" | | | | |drink="coffee" | | | | |type="demo" | +-----------------------+------------------+----------------------------+----------------------+ ``` * Remove tag (after running the example above) ```bash Command theme={null} cpln org tag demo-cpln-organization --remove drink ``` ```text Output theme={null} +-----------------------+------------------+----------------------------+----------------------+ |NAME |DESCRIPTION |LINK |TAGS | |-----------------------|------------------|----------------------------|----------------------| |demo-cpln-organization |Demo Organization |/org/demo-cpln-organization |demo-key="demo-value" | | | | |type="demo" | +-----------------------+------------------+----------------------------+----------------------+ ``` *** #### org update Update properties of the referenced organization **Usage** ```none theme={null} cpln org update --set UPDATE_PROPERTY [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Update Properties: --set Update the following properties (e.g., --set description="Updated Description"): description string tags. string --unset Unset a property (same paths as --set, e.g., --unset description) Command options: --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * The `--set` option allows the modification of the following organization properties: * `description` * Description of the organization * Type: string * `tags` * Tags (key=value) associated with the organization * Type: string * For the examples below, the initial organization is configured as: ``` +-----------------------+------------------+----------------------------+----------------------+ |NAME |DESCRIPTION |LINK |TAGS | |-----------------------|------------------|----------------------------|----------------------| |demo-cpln-organization |Demo Organization |/org/demo-cpln-organization |demo-key="demo-value" | | | | |type="demo" | +-----------------------+------------------+----------------------------+----------------------+ ``` * Update an organization's description ```bash Command theme={null} cpln org update demo-cpln-organization --set description="new description" ``` ```text Output theme={null} +-----------------------+----------------+----------------------------+----------------------+ |NAME |DESCRIPTION |LINK |TAGS | |-----------------------|----------------|----------------------------|----------------------| |demo-cpln-organization |new description |/org/demo-cpln-organization |demo-key="demo-value" | | | | |type="demo" | +-----------------------+----------------+----------------------------+----------------------+ ``` * Update an organization's tag ```bash Command theme={null} cpln org update demo-cpln-organization --set tags.demo-key=demo-value ``` ```text Output theme={null} +-----------------------+----------------+----------------------------+----------------------+ |NAME |DESCRIPTION |LINK |TAGS | |-----------------------|----------------|----------------------------|----------------------| |demo-cpln-organization |new description |/org/demo-cpln-organization |demo-key="demo-value" | | | | |type="demo" | +-----------------------+----------------+----------------------------+----------------------+ ``` *** # policy Source: https://docs.controlplane.com/cli-reference/commands/policy cpln policy — Manage access policies to resource that control permissions for users, groups, and service accounts. Manage access policies For a detailed overview, please visit the [policy's](/reference/policy) reference page. ```none theme={null} cpln policy [SUBCOMMAND] ``` ```text theme={null} Commands: cpln policy access-report Show the access report for the referenced policy cpln policy add-binding Bind one or more permissions to the referenced policy and associate it with one or more users / service accounts / groups / identities cpln policy audit [ref] Retrieve audit trail events for the referenced policy cpln policy clone Clone a policy [aliases: copy] cpln policy create Create a new policy cpln policy delete Delete one or more referenced policies cpln policy edit Edit the referenced policy, as YAML, within an editor cpln policy eventlog Show the event log of the referenced policy [aliases: log] cpln policy get [ref...] Retrieve one or more referenced policies cpln policy patch Update the referenced policy's metadata using an input file cpln policy permissions Show the grantable permissions for a policy object type cpln policy query Find all the policies based on the given query cpln policy remove-binding Remove the bindings of one or more permissions from the referenced policy and disassociate it from one or more users / service accounts / groups / identities cpln policy tag Manage the tags belonging to one or more referenced policies cpln policy update Update properties of the referenced policy Options: --help Show help [boolean] ``` *** #### policy access-report Show the access report for the referenced policy **Usage** ```none theme={null} cpln policy access-report [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln policy access-report demo-policy --org demo-cpln-organization ``` ```text Output theme={null} +-----------+--------------------------------------------------------+--------+ |PERMISSION |PRINCIPAL |GRANTED | |-----------|--------------------------------------------------------|--------| |create |/org/demo-cpln-organization/group/superusers |manage | |create |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |delete |/org/demo-cpln-organization/group/superusers |manage | |delete |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |edit |/org/demo-cpln-organization/group/superusers |manage | |edit |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |manage |/org/demo-cpln-organization/group/superusers |manage | |manage |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |view |/org/demo-cpln-organization/group/viewers |view | |view |/org/demo-cpln-organization/group/superusers |manage | |view |/org/demo-cpln-organization/serviceaccount/controlplane |manage | +-----------+--------------------------------------------------------+--------+ ``` *** #### policy add-binding Bind one or more permissions to the referenced policy and associate it with one or more users / service accounts / groups / identities **Usage** ```none theme={null} cpln policy add-binding --permission PERMISSION_NAME COMMAND_OPTIONS [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --email User email to add permission --serviceaccount Service account name to add permission --group Group name to add permission --identity Identity name OR link to add permission --permission Permission to add [required] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Bind the view permission to the referenced policy named \`demo-policy-add-binding\` and associate it with the built-in controlplane service account ```bash Command theme={null} cpln policy add-binding demo-policy --permission view --serviceaccount controlplane --org demo-cpln-organization ``` ```text Output theme={null} +------------+------------+--------------+------------+--------+--------+--------------+ |NAME |DESCRIPTION |CREATED |TARGET KIND |MATCHES |ORIGIN |TAGS | |------------|------------|--------------|------------|--------|--------|--------------| |demo-policy |demo-policy |3 minutes ago |org |all |default |drink="water" | +------------+------------+--------------+------------+--------+--------+--------------+ ``` *** #### policy audit Retrieve audit trail events for the referenced policy **Usage** ```none theme={null} cpln policy audit [ref] [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. Command options: --subject Filter by subject (serviceaccount name or user email) [string] --context Audit context name (the 'cpln' context displays Control Plane actions) [string] [default: "cpln"] --since Relative lookback window from now (e.g., 1h, 24h, 7d). Mutually exclusive with --from/--to [string] [default: "7d"] --from Start time for audit events (ISO 8601 format or relative duration, e.g., 2025-10-23T07:00:00Z, 7d, 1h). Cannot be used with --since [string] --to End time for audit events (ISO 8601 format or relative duration, e.g., 2025-10-23T07:00:00Z, 1d, 30m) [string] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### policy clone Clone a policy **Usage** ```none theme={null} cpln policy clone --name CLONE_NAME [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --name Set the name for the clone [required] --description Optional description, defaults to the name if not set --tag Attach tags (e.g., --tag drink=water) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln policy clone demo-policy --name demo-policy-clone --org demo-cpln-organization ``` ```text Output theme={null} +------------------+---------------------+--------+------------+--------+--------+--------------+ |NAME |DESCRIPTION |CREATED |TARGET KIND |MATCHES |ORIGIN |TAGS | |------------------|---------------------|--------|------------|--------|--------|--------------| |demo-policy-clone |Clone of demo-policy |Now |org |all |default |drink="water" | +------------------+---------------------+--------+------------+--------+--------+--------------+ ``` *** #### policy create Create a new policy **Usage** ```none theme={null} cpln policy create --name POLICY_NAME --target-kind TARGET [OPTIONS] ``` ```text theme={null} Command options: --name Name of the new policy, type - to generate a valid name [required] --description, --desc Optional description, defaults to the name if not set --target-kind Select a target kind for this policy [required] [choices: "account", "agent", "auditctx", "cloudaccount", "domain", "dbcluster", "group", "gvc", "identity", "image", "location", "memcachecluster", "org", "policy", "quota", "secret", "serviceaccount", "spicedbcluster", "task", "user", "volumeset", "workload"] --all Apply policy to all instances of the kind [boolean] --resource Enumerate resource names to add to the policy --tag Attach tags (e.g., --tag drink=water) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Query spec options: --query-match Query filter type [choices: "all", "none", "any"] [default: "all"] --query-property Property to query --query-tag Tag to query --query-rel Relation to query Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln policy create --name demo-policy --target-kind org --all --desc demo-policy --tag drink=water --org demo-cpln-organization ``` ```text Output theme={null} +------------+------------+--------+------------+--------+--------+--------------+ |NAME |DESCRIPTION |CREATED |TARGET KIND |MATCHES |ORIGIN |TAGS | |------------|------------|--------|------------|--------|--------|--------------| |demo-policy |demo-policy |Now |org |all |default |drink="water" | +------------+------------+--------+------------+--------+--------+--------------+ ``` *** #### policy delete Delete one or more referenced policies **Usage** ```none theme={null} cpln policy delete [OPTIONS] ``` Deleting a policy will remove all associated resources (targets, bindings, etc.) ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [required] [default: []] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln policy delete demo-policy --org demo-cpln-organization ``` ```text Output theme={null} If the delete was successful, a new command prompt will be shown. ``` *** #### policy edit Edit the referenced policy, as YAML, within an editor **Usage** ```none theme={null} cpln policy edit [OPTIONS] ``` The default editor can be set by using the EDITOR environment variable. ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --replace, -r Replace instead of patch/merge [boolean] [default: false] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln policy edit demo-policy --org demo-cpln-organization ``` ```text Output theme={null} Your default editor will be launched. Once any modifications are saved, the updates will be sent to the API. ``` *** #### policy eventlog Show the event log of the referenced policy **Usage** ```none theme={null} cpln policy eventlog [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln policy eventlog demo-policy --org demo-cpln-organization ``` ```text Output theme={null} The event log for the referenced policy will be shown. ``` *** #### policy get Retrieve one or more referenced policies **Usage** ```none theme={null} cpln policy get [ref...] [OPTIONS] ``` An empty reference will return all references ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [default: []] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Get all policies ```bash Command theme={null} cpln policy get --org demo-cpln-organization ``` ```text Output theme={null} +---------------------------+-----------------------------------------+--------------+----------------+--------+--------+--------------+ |NAME |DESCRIPTION |CREATED |TARGET KIND |MATCHES |ORIGIN |TAGS | |---------------------------|-----------------------------------------|--------------|----------------|--------|--------|--------------| |demo-policy |demo-policy |3 minutes ago |org |all |default |drink="water" | |demo-policy-get-1 |demo-policy-get-1 |Now |org |all |default | | |demo-policy-get-2 |demo-policy-get-2 |Now |org |all |default | | |superusers-agent |Built-in policy granting full access ... |5 years ago |agent |all |builtin | | |superusers-auditctx |Built-in policy granting full access ... |5 years ago |auditctx |all |builtin | | |superusers-cloudaccount |Built-in policy granting full access ... |5 years ago |cloudaccount |all |builtin | | |superusers-domain |Built-in policy granting full access ... |5 years ago |domain |all |builtin | | |superusers-group |Built-in policy granting full access ... |5 years ago |group |all |builtin | | |superusers-gvc |Built-in policy granting full access ... |5 years ago |gvc |all |builtin | | |superusers-identity |Built-in policy granting full access ... |5 years ago |identity |all |builtin | | |superusers-image |Built-in policy granting full access ... |5 years ago |image |all |builtin | | |superusers-ipset |Built-in policy granting full access ... |1 year ago |ipset |all |builtin | | |superusers-location |Built-in policy granting full access ... |5 years ago |location |all |builtin | | |superusers-memcachecluster |Built-in policy granting full access ... |3 years ago |memcachecluster |all |builtin | | |superusers-mk8s |Built-in policy granting full access ... |3 years ago |mk8s |all |builtin | | |superusers-org |Built-in policy granting full access ... |5 years ago |org |all |builtin | | |superusers-policy |Built-in policy granting full access ... |5 years ago |policy |all |builtin | | |superusers-quota |Built-in policy granting full access ... |5 years ago |quota |all |builtin | | |superusers-secret |Built-in policy granting full access ... |5 years ago |secret |all |builtin | | |superusers-serviceaccount |Built-in policy granting full access ... |5 years ago |serviceaccount |all |builtin | | |superusers-spicedbcluster |Built-in policy granting full access ... |2 years ago |spicedbcluster |all |builtin | | |superusers-task |Built-in policy granting full access ... |5 years ago |task |all |builtin | | |superusers-user |Built-in policy granting full access ... |5 years ago |user |all |builtin | | |superusers-volumeset |Built-in policy granting full access ... |3 years ago |volumeset |all |builtin | | |superusers-workload |Built-in policy granting full access ... |5 years ago |workload |all |builtin | | |viewers-agent |Built-in policy granting read-only ac... |5 years ago |agent |all |builtin | | |viewers-auditctx |Built-in policy granting read-only ac... |5 years ago |auditctx |all |builtin | | |viewers-cloudaccount |Built-in policy granting read-only ac... |5 years ago |cloudaccount |all |builtin | | |viewers-domain |Built-in policy granting read-only ac... |5 years ago |domain |all |builtin | | |viewers-group |Built-in policy granting read-only ac... |5 years ago |group |all |builtin | | |viewers-gvc |Built-in policy granting read-only ac... |5 years ago |gvc |all |builtin | | |viewers-identity |Built-in policy granting read-only ac... |5 years ago |identity |all |builtin | | |viewers-image |Built-in policy granting read-only ac... |5 years ago |image |all |builtin | | |viewers-ipset |Built-in policy granting read-only ac... |1 year ago |ipset |all |builtin | | |viewers-location |Built-in policy granting read-only ac... |5 years ago |location |all |builtin | | |viewers-memcachecluster |Built-in policy granting read-only ac... |3 years ago |memcachecluster |all |builtin | | |viewers-mk8s |Built-in policy granting read-only ac... |3 years ago |mk8s |all |builtin | | |viewers-policy |Built-in policy granting read-only ac... |5 years ago |policy |all |builtin | | |viewers-quota |Built-in policy granting read-only ac... |5 years ago |quota |all |builtin | | |viewers-secret |Built-in policy granting read-only ac... |5 years ago |secret |all |builtin | | |viewers-serviceaccount |Built-in policy granting read-only ac... |5 years ago |serviceaccount |all |builtin | | |viewers-spicedbcluster |Built-in policy granting read-only ac... |2 years ago |spicedbcluster |all |builtin | | |viewers-task |Built-in policy granting read-only ac... |5 years ago |task |all |builtin | | |viewers-user |Built-in policy granting read-only ac... |5 years ago |user |all |builtin | | |viewers-volumeset |Built-in policy granting read-only ac... |3 years ago |volumeset |all |builtin | | |viewers-workload |Built-in policy granting read-only ac... |5 years ago |workload |all |builtin | | +---------------------------+-----------------------------------------+--------------+----------------+--------+--------+--------------+ ``` * Get one policy ```bash Command theme={null} cpln policy get demo-policy-get-1 --org demo-cpln-organization ``` ```text Output theme={null} +------------------+------------------+--------+------------+--------+--------+-----+ |NAME |DESCRIPTION |CREATED |TARGET KIND |MATCHES |ORIGIN |TAGS | |------------------|------------------|--------|------------|--------|--------|-----| |demo-policy-get-1 |demo-policy-get-1 |Now |org |all |default | | +------------------+------------------+--------+------------+--------+--------+-----+ ``` * Get multiple policies ```bash Command theme={null} cpln policy get demo-policy-get-1 demo-policy-get-2 --org demo-cpln-organization ``` ```text Output theme={null} +------------------+------------------+--------+------------+--------+--------+-----+ |NAME |DESCRIPTION |CREATED |TARGET KIND |MATCHES |ORIGIN |TAGS | |------------------|------------------|--------|------------|--------|--------|-----| |demo-policy-get-1 |demo-policy-get-1 |Now |org |all |default | | |demo-policy-get-2 |demo-policy-get-2 |Now |org |all |default | | +------------------+------------------+--------+------------+--------+--------+-----+ ``` *** #### policy patch Update the referenced policy's metadata using an input file **Usage** ```none theme={null} cpln policy patch --file FILE_NAME [OPTIONS] ``` Running this command with the option **`--file -`** sets the input to stdin. The input is required to be a valid JSON or YAML string (e.g., `{"description": "My new description"}` or `description: My new description`). To complete the update, press Control-D (or Control-Z if you are using Windows). ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --file, -f File to load the patch from. Use `--file -` to enable input from stdin. [required] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln policy patch demo-policy --file update.json --org demo-cpln-organization ``` ```text Output theme={null} The changes within the JSON file will be performed against the referenced policy. ``` *** #### policy permissions Show the grantable permissions for a policy object type **Usage** ```none theme={null} cpln policy permissions [OPTIONS] ``` ```text theme={null} Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln policy permissions --org demo-cpln-organization ``` ```text Output theme={null} +-------+-------------------------+-----------------------------------+ |NAME |DESCRIPTION |IMPLIES | |-------|-------------------------|-----------------------------------| |create |Create new policies | | |delete |Delete existing policies | | |edit |Modify existing policies |view | |manage |Full access |create, delete, edit, manage, view | |view |Read-only view | | +-------+-------------------------+-----------------------------------+ ``` *** #### policy query Find all the policies based on the given query **Usage** ```none theme={null} cpln policy query QUERY_OPTIONS [OPTIONS] ``` ```text theme={null} Query options: --match Query filter type [choices: "all", "none", "any"] [default: "all"] --property, --prop Property to query --tag Tag to query --rel Relation to query Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) Use the `--match` option to display **all**, **none**, or **any** of the results based on a given property or tag * Search for a policy based on the name property ```bash Command theme={null} cpln policy query --match any --prop name=demo-policy --org demo-cpln-organization ``` ```text Output theme={null} +------------+------------+--------------+------------+--------+--------+--------------+ |NAME |DESCRIPTION |CREATED |TARGET KIND |MATCHES |ORIGIN |TAGS | |------------|------------|--------------|------------|--------|--------|--------------| |demo-policy |demo-policy |3 minutes ago |org |all |default |drink="water" | +------------+------------+--------------+------------+--------+--------+--------------+ ``` * Search for a policy based on the description property ```bash Command theme={null} cpln policy query --match any --prop description="demo-policy" --org demo-cpln-organization ``` ```text Output theme={null} +------------+------------+--------------+------------+--------+--------+--------------+ |NAME |DESCRIPTION |CREATED |TARGET KIND |MATCHES |ORIGIN |TAGS | |------------|------------|--------------|------------|--------|--------|--------------| |demo-policy |demo-policy |4 minutes ago |org |all |default |drink="water" | +------------+------------+--------------+------------+--------+--------+--------------+ ``` * Search for a policy based on a tag ```bash Command theme={null} cpln policy query --match any --tag drink=water --org demo-cpln-organization ``` ```text Output theme={null} +------------+------------+--------------+------------+--------+--------+--------------+ |NAME |DESCRIPTION |CREATED |TARGET KIND |MATCHES |ORIGIN |TAGS | |------------|------------|--------------|------------|--------|--------|--------------| |demo-policy |demo-policy |4 minutes ago |org |all |default |drink="water" | +------------+------------+--------------+------------+--------+--------+--------------+ ``` * Search for a policy based on a property and tag ```bash Command theme={null} cpln policy query --match any --prop description="demo-policy" --tag drink=water --org demo-cpln-organization ``` ```text Output theme={null} +------------+------------+--------------+------------+--------+--------+--------------+ |NAME |DESCRIPTION |CREATED |TARGET KIND |MATCHES |ORIGIN |TAGS | |------------|------------|--------------|------------|--------|--------|--------------| |demo-policy |demo-policy |4 minutes ago |org |all |default |drink="water" | +------------+------------+--------------+------------+--------+--------+--------------+ ``` *** #### policy remove-binding Remove the bindings of one or more permissions from the referenced policy and disassociate it from one or more users / service accounts / groups / identities **Usage** ```none theme={null} cpln policy remove-binding --permission PERMISSION_NAME COMMAND_OPTIONS [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --email User email to remove permission --serviceaccount Service account name to remove permission --group Group name to remove permission --identity Identity name OR link to remove permission --permission Permission to remove [required] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Remove the binding of the view permission from the policy named `demo-policy` and disassociate it with the built-in controlplane service account. ```bash Command theme={null} cpln policy remove-binding demo-policy --permission manage --serviceaccount controlplane --org demo-cpln-organization ``` ```text Output theme={null} +------------+------------+--------------+------------+--------+--------+--------------+ |NAME |DESCRIPTION |CREATED |TARGET KIND |MATCHES |ORIGIN |TAGS | |------------|------------|--------------|------------|--------|--------|--------------| |demo-policy |demo-policy |4 minutes ago |org |all |default |drink="water" | +------------+------------+--------------+------------+--------+--------+--------------+ ``` *** #### policy tag Manage the tags belonging to one or more referenced policies **Usage** ```none theme={null} cpln policy tag --tag KEY=VALUE [OPTIONS] ``` Built-in tags, which begin with cpln/\*, can't be modified. ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [required] [default: []] Command options: --tag Attach tags (e.g., --tag drink=water) --remove-tag Remove tags (e.g., --remove-tag tagname) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Add a tag to a policy ```bash Command theme={null} cpln policy tag demo-policy-tag-1 --tag drink=water --org demo-cpln-organization ``` ```text Output theme={null} +------------------+------------------+--------+------------+--------+--------+--------------+ |NAME |DESCRIPTION |CREATED |TARGET KIND |MATCHES |ORIGIN |TAGS | |------------------|------------------|--------|------------|--------|--------|--------------| |demo-policy-tag-1 |demo-policy-tag-1 |Now |org |all |default |drink="water" | +------------------+------------------+--------+------------+--------+--------+--------------+ ``` * Update same tag (after running the example above) ```bash Command theme={null} cpln policy tag demo-policy-tag-1 --tag drink=coffee --org demo-cpln-organization ``` ```text Output theme={null} +------------------+------------------+--------+------------+--------+--------+---------------+ |NAME |DESCRIPTION |CREATED |TARGET KIND |MATCHES |ORIGIN |TAGS | |------------------|------------------|--------|------------|--------|--------|---------------| |demo-policy-tag-1 |demo-policy-tag-1 |Now |org |all |default |drink="coffee" | +------------------+------------------+--------+------------+--------+--------+---------------+ ``` * Remove tag (after running the example above) ```bash Command theme={null} cpln policy tag demo-policy-tag-1 --remove drink --org demo-cpln-organization ``` ```text Output theme={null} +------------------+------------------+--------+------------+--------+--------+-----+ |NAME |DESCRIPTION |CREATED |TARGET KIND |MATCHES |ORIGIN |TAGS | |------------------|------------------|--------|------------|--------|--------|-----| |demo-policy-tag-1 |demo-policy-tag-1 |Now |org |all |default | | +------------------+------------------+--------+------------+--------+--------+-----+ ``` * Add same tag to multiple policies ```bash Command theme={null} cpln policy tag demo-policy-tag-1 demo-policy-tag-2 --tag multiple-policies=true --org demo-cpln-organization ``` ```text Output theme={null} +------------------+------------------+--------+------------+--------+--------+-------------------------+ |NAME |DESCRIPTION |CREATED |TARGET KIND |MATCHES |ORIGIN |TAGS | |------------------|------------------|--------|------------|--------|--------|-------------------------| |demo-policy-tag-1 |demo-policy-tag-1 |Now |org |all |default |multiple-policies="true" | |demo-policy-tag-2 |demo-policy-tag-2 |Now |org |all |default |multiple-policies="true" | +------------------+------------------+--------+------------+--------+--------+-------------------------+ ``` * Update same tag on multiple policies (after running the example above) ```bash Command theme={null} cpln policy tag demo-policy-tag-1 demo-policy-tag-2 --tag multiple-policies=demo-policy --org demo-cpln-organization ``` ```text Output theme={null} +------------------+------------------+--------+------------+--------+--------+--------------------------------+ |NAME |DESCRIPTION |CREATED |TARGET KIND |MATCHES |ORIGIN |TAGS | |------------------|------------------|--------|------------|--------|--------|--------------------------------| |demo-policy-tag-1 |demo-policy-tag-1 |Now |org |all |default |multiple-policies="demo-policy" | |demo-policy-tag-2 |demo-policy-tag-2 |Now |org |all |default |multiple-policies="demo-policy" | +------------------+------------------+--------+------------+--------+--------+--------------------------------+ ``` * Remove same tag from multiple policies (after running the example above) ```bash Command theme={null} cpln policy tag demo-policy-tag-1 demo-policy-tag-2 --remove multiple-policies --org demo-cpln-organization ``` ```text Output theme={null} +------------------+------------------+--------+------------+--------+--------+-----+ |NAME |DESCRIPTION |CREATED |TARGET KIND |MATCHES |ORIGIN |TAGS | |------------------|------------------|--------|------------|--------|--------|-----| |demo-policy-tag-1 |demo-policy-tag-1 |Now |org |all |default | | |demo-policy-tag-2 |demo-policy-tag-2 |Now |org |all |default | | +------------------+------------------+--------+------------+--------+--------+-----+ ``` *** #### policy update Update properties of the referenced policy **Usage** ```none theme={null} cpln policy update --set UPDATE_PROPERTY [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Update Properties: --set Update the following properties (e.g., --set description="Updated Description"): description string tags. string targetLinks string[] target { 'all' } Arrays can be appended to, replaced, or removed using the `+=`, `=`, or `-=` operators, respectively. --unset Unset a property (same paths as --set, e.g., --unset description) Command options: --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * The `--set` option allows the modification of the following policy properties: * `description` * Description of the policy * Type: string * `tags` * Tags (key=value) associated with the policy * Type: string * `targetLinks` * Array of target links belonging to the policy * Type: string * `target` * The target of the policy * Type: string * Allowed values: 'all' * For the examples below, the initial policy is configured as: ``` +------------+------------+--------+------------+--------+--------+--------------+ |NAME |DESCRIPTION |CREATED |TARGET KIND |MATCHES |ORIGIN |TAGS | |------------|------------|--------|------------|--------|--------|--------------| |demo-policy |demo-policy |Now |org |all |default |drink="water" | +------------+------------+--------+------------+--------+--------+--------------+ ``` * Update a policy's description ```bash Command theme={null} cpln policy update demo-policy --set description="new description" --org demo-cpln-organization ``` ```text Output theme={null} +------------+----------------+--------------+------------+--------+--------+--------------+ |NAME |DESCRIPTION |CREATED |TARGET KIND |MATCHES |ORIGIN |TAGS | |------------|----------------|--------------|------------|--------|--------|--------------| |demo-policy |new description |4 minutes ago |org |all |default |drink="water" | +------------+----------------+--------------+------------+--------+--------+--------------+ ``` * Update a policy's tags ```bash Command theme={null} cpln policy update demo-policy --set tags.demo-key=demo-value --org demo-cpln-organization ``` ```text Output theme={null} +------------+----------------+--------------+------------+--------+--------+----------------------+ |NAME |DESCRIPTION |CREATED |TARGET KIND |MATCHES |ORIGIN |TAGS | |------------|----------------|--------------|------------|--------|--------|----------------------| |demo-policy |new description |4 minutes ago |org |all |default |demo-key="demo-value" | | | | | | | |drink="water" | +------------+----------------+--------------+------------+--------+--------+----------------------+ ``` * Add to a policy's target links. ```bash Command theme={null} cpln policy update demo-policy --set targetLinks+="/org/demo-cpln-organization/gvc/demo-gvc-policy" --org demo-cpln-organization ``` ```text Output theme={null} +------------+----------------+--------------+------------+--------+--------+----------------------+ |NAME |DESCRIPTION |CREATED |TARGET KIND |MATCHES |ORIGIN |TAGS | |------------|----------------|--------------|------------|--------|--------|----------------------| |demo-policy |new description |4 minutes ago |org |all |default |demo-key="demo-value" | | | | | | | |drink="water" | +------------+----------------+--------------+------------+--------+--------+----------------------+ ``` * Remove from a policy's target links. ```bash Command theme={null} cpln policy update demo-policy --set targetLinks-="/org/demo-cpln-organization/gvc/demo-gvc-policy" --org demo-cpln-organization ``` ```text Output theme={null} +------------+----------------+--------------+------------+--------+--------+----------------------+ |NAME |DESCRIPTION |CREATED |TARGET KIND |MATCHES |ORIGIN |TAGS | |------------|----------------|--------------|------------|--------|--------|----------------------| |demo-policy |new description |4 minutes ago |org |all |default |demo-key="demo-value" | | | | | | | |drink="water" | +------------+----------------+--------------+------------+--------+--------+----------------------+ ``` * Update a policy's target to all ```bash Command theme={null} cpln policy update demo-policy --set target=all --org demo-cpln-organization ``` ```text Output theme={null} +------------+----------------+--------------+------------+--------+--------+----------------------+ |NAME |DESCRIPTION |CREATED |TARGET KIND |MATCHES |ORIGIN |TAGS | |------------|----------------|--------------|------------|--------|--------|----------------------| |demo-policy |new description |4 minutes ago |org |all |default |demo-key="demo-value" | | | | | | | |drink="water" | +------------+----------------+--------------+------------+--------+--------+----------------------+ ``` *** # port-forward Source: https://docs.controlplane.com/cli-reference/commands/port-forward cpln port-forward — Establish secure local port forwarding to a running workload. Establish secure local access to your workload. Refer to the [cpln port-forward](/guides/cli/cpln-port-forward) guide for additional details. **Usage** ```none theme={null} cpln port-forward [OPTIONS] ``` ```text theme={null} Positionals: ref The name of the workload to forward traffic to. [string] [required] ports Port mappings in the format [LOCAL_PORT:]REMOTE_PORT (e.g. 8080:80). [array] [required] [default: []] Command options: --location A global virtual cloud location [string] --address Addresses to listen on (comma separated). Only accepts IP addresses or localhost as a value. When localhost is supplied, cpln will try to bind on both 127.0.0.1 and ::1 and will fail if neither of these addresses are available to bind. [default: "localhost"] --replica The name of the workload deployment replica Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] --version, -v Show version number [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Forward a single port from workload to local machine ```bash Command theme={null} cpln port-forward db-workload 5432:5432 --location aws-us-west-1 --org demo-org --gvc demo-gvc ``` ```text Output theme={null} Forwarding from 127.0.0.1:5432 -> 5432 ``` * Forward multiple ports from workload to local machine ```bash Command theme={null} cpln port-forward db-workload 8080:80 8443:443 --location aws-us-west-1 --org demo-org --gvc demo-gvc ``` ```text Output theme={null} Forwarding from 127.0.0.1:8080 -> 80 Forwarding from 127.0.0.1:8443 -> 443 ``` * Forward workload ports to multiple addresses ```bash Command theme={null} cpln port-forward db-workload 5432:5432 --address 0.0.0.0,127.0.0.1 --location aws-us-west-1 --org demo-org --gvc demo-gvc ``` ```text Output theme={null} Forwarding from 0.0.0.0:5432 -> 5432 Forwarding from 127.0.0.1:5432 -> 5432 ``` * Assign an ephemeral local port ```bash Command theme={null} cpln port-forward db-workload :80 --location aws-us-west-1 --org demo-org --gvc demo-gvc ``` ```text Output theme={null} Forwarding from 127.0.0.1:56421 -> 80 ``` * Assign the same local and remote port ```bash Command theme={null} cpln port-forward db-workload 80 --location aws-us-west-1 --org demo-org --gvc demo-gvc ``` ```text Output theme={null} Forwarding from 127.0.0.1:80 -> 80 ``` *** # profile Source: https://docs.controlplane.com/cli-reference/commands/profile cpln profile — Manage local CLI profiles for storing authentication and context settings. Manage local profiles For a detailed overview, please visit the [manage CLI profiles](/cli-reference/get-started/profiles) guide. ```none theme={null} cpln profile [SUBCOMMAND] ``` ```text theme={null} Commands: cpln profile delete Delete one or more referenced profiles cpln profile get [profiles...] Retrieve one or more profiles cpln profile login [ref] Create a profile named "default" and print a URL and a confirmation code to complete sign-in in any browser cpln profile set-default Set the referenced profile as the default cpln profile token [profile] Show the JWT token of the referenced profile cpln profile update Manage the referenced profile [aliases: create] Options: --version, -v Show version number [boolean] --help Show help [boolean] ``` *** #### profile delete Delete one or more referenced profiles **Usage** ```none theme={null} cpln profile delete [OPTIONS] ``` ```text theme={null} Positionals: profile One or more profile names to delete Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --version, -v Show version number [boolean] --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Delete one profile ```bash Command theme={null} cpln profile delete default ``` ```text Command theme={null} A table showing the profile details will return if the delete was successful. Otherwise, an empty table will be displayed. ``` * Delete multiple profiles ```bash Command theme={null} cpln profile delete demo-profile@example.com default ``` ```text Output theme={null} A table showing the profile details will return if the delete was successful. Otherwise, an empty table will be displayed. ``` *** #### profile get Retrieve one or more profiles **Usage** ```none theme={null} cpln profile get [profiles...] [OPTIONS] ``` An empty reference will return all references ```text theme={null} Positionals: profiles One or more profiles to show [array] [default: []] Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --version, -v Show version number [boolean] --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln profile get default ``` ```text Output theme={null} +--------+---------------------+--------+-------+-------------------------+----+----+-------+-----------+ |PROFILE |EMAIL |DEFAULT |ACTIVE |ENDPOINT |ORG |GVC |OUTPUT |TIMESTAMPS | |--------|---------------------|--------|-------|-------------------------|----|----|-------|-----------| |default |test-128@example.com |* |* |https://api.test.cpln.io | | |text |age | +--------+---------------------+--------+-------+-------------------------+----+----+-------+-----------+ ``` *** #### profile login Create a profile named "default" and print a URL and a confirmation code to complete sign-in in any browser **Usage** ```none theme={null} cpln profile login [ref] [OPTIONS] ``` ```text theme={null} Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Positionals: ref The resource reference. Usually it is the name of the resource. Options: --version, -v Show version number [boolean] --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln profile login ``` ```text Output theme={null} A new browser will open displaying the Control Plane login page. If the login was successful, the browser will indicate that it can be closed. The CLI will display the login results. Press Control-C to exit the CLI login. If the login failed, the browser and be closed and the CLI will return to the command prompt. Run \`cpln profile login\` again to restart the login. ``` *** #### profile set-default Set the referenced profile as the default **Usage** ```none theme={null} cpln profile set-default [OPTIONS] ``` ```text theme={null} Positionals: profile Profile name to be set as the default [required] Options: --version, -v Show version number [boolean] --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln profile set-default demo-profile ``` ```text Output theme={null} The referenced profile will be set as the default for all future executions of the CLI commands. If the set was successful, the command prompt will return. Otherwise, the error will be shown. ``` *** #### profile token Show the JWT token of the referenced profile **Usage** ```none theme={null} cpln profile token [profile] [OPTIONS] ``` An empty reference will return the token for the default profile ```text theme={null} Positionals: profile Profile to retrieve the token for Command options: --serve Serve token on a local server [boolean] --port Port for the local server [number] [default: 43200] --jwt View your jwt for the token server [boolean] --generateToken Generate a new jwt token for the token server [boolean] Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --version, -v Show version number [boolean] --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln profile token demo-profile ``` ```text Output theme={null} A valid JWT token for the referenced profile "demo-profile" will be shown. ``` ```text Token Server Usage theme={null} - 'cpln profile token --serve': Create a server (default port of 43200) instead of outputting the token directly. This command generates a profile specific token to authenticate against this token server. - 'cpln profile token --serve --port PORT': Sets the port of the token server. - 'cpln profile token --jwt': Outputs the token server token for the profile. - 'cpln profile token --generateToken': Regenerates a token for the profile, for the token server. An example use case is to integrate the server to a Postman collection. The collection can then make requests to the Control Plane API without needing to manually copy the token. After the server is running, by executing the 'cpln profile token --serve' command , the user can utilize the Pre-request script below in Postman: ------------------------ const postRequest = { url: "http://localhost:" + pm.environment.get("cplnTokenServerPort") + "/" + pm.environment.get("profile") + "/token", method: 'GET', header: { 'authorization': "Bearer " + pm.environment.get("cplnTokenServerToken") }, }; pm.sendRequest(postRequest, (err, response) => { pm.environment.set("token", response.text()); }) ------------------------ ``` *** #### profile update Manage the referenced profile **Usage** ```none theme={null} cpln profile update COMMAND_OPTIONS [OPTIONS] ``` ```text theme={null} Positionals: profile Name of the profile to update. If it does not exist, a new profile with that name will be created [required] Command options: --login Begin the login process using the referenced profile. Prints a URL and a confirmation code to complete sign-in in any browser --default Set the given profile as the default --org Set the given organization as the default organization of the referenced profile --gvc Set the given GVC as the default GVC of the referenced profile Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --version, -v Show version number [boolean] --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Create a new profile ```bash Command theme={null} cpln profile update new-profile ``` ```text Output theme={null} If the referenced profile name doesn't exist, a new one will be created. ``` * Perform an interactive login using a custom profile ```bash Command theme={null} cpln profile update new-profile --login ``` ```text Output theme={null} A browser will open displaying the Control Plane login page. If the login was successful, a new profile with the given name will be created (if it doesn't already exist) and the browser will indicate that it can be closed. This profile will be marked as the default and used by any future CLI command. The CLI will display the login results. Press Control-C to exit the CLI login. If the login failed, the CLI will return to the command prompt. Run the command again to restart the login flow. ``` * Set the referenced profile as the default profile ```bash Command theme={null} cpln profile update new-profile --default ``` ```text Output theme={null} If the update was successful, the referenced profile will be set as the default for all future cpln commands. ``` * Set a default organization and GVC on the referenced profile ```bash Command theme={null} cpln profile update new-profile --org demo-cpln-organization --gvc demo-gvc ``` ```text Output theme={null} The referenced organization and GVC will be set as the default on the referenced profile. Any future execution of a cpln command will use those values. ``` * Remove default organization and GVC on the referenced profile ```bash Command theme={null} cpln profile update new-profile --org "" --gvc "" ``` ```text Output theme={null} The specified default organization and GVC will be removed from the referenced profile. Future executions of any CPLN command will require the --org and the --gvc options. ``` *** # quota Source: https://docs.controlplane.com/cli-reference/commands/quota cpln quota — Display resource quotas and current usage for your organization. Show quotas For a detailed overview, please visit the [quota's](/reference/quota) reference page. ```none theme={null} cpln quota [SUBCOMMAND] ``` ```text theme={null} Commands: cpln quota edit Edit the referenced quota, as YAML, within an editor cpln quota get [ref...] Retrieve one or more referenced quotas cpln quota patch Update the referenced quota's metadata using an input file cpln quota permissions Show the grantable permissions for a quota object type cpln quota query Find all the quotas based on the given query cpln quota request Manage quota increase requests Options: --help Show help [boolean] ``` *** #### quota edit Edit the referenced quota, as YAML, within an editor **Usage** ```none theme={null} cpln quota edit [OPTIONS] ``` The default editor can be set by using the EDITOR environment variable. ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --replace, -r Replace instead of patch/merge [boolean] [default: false] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln quota edit b2a6bbc3-e3bb-4623-8689-9fcd71c1c86a --org demo-cpln-organization ``` ```text Output theme={null} Your default editor will be launched. Once any modifications are saved, the updates will be sent to the API. ``` *** #### quota get Retrieve one or more referenced quotas **Usage** ```none theme={null} cpln quota get [ref...] [OPTIONS] ``` An empty reference will return all references ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [default: []] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Get all quotas ```bash Command theme={null} cpln quota get --org demo-cpln-organization ``` ```text Output theme={null} +-------------------------------------+----------------------+-----------------------------------------+--------+--------+-----+----------+----------------------------------+ |ID |NAME |DESCRIPTION |ORIGIN |CURRENT |MAX |UNIT |DIMENSIONS | |-------------------------------------|----------------------|-----------------------------------------|--------|--------|-----|----------|----------------------------------| |189a7ef1-d6b5-4496-9bbc-eb933a64cb81 |agents |Max number of agents |builtin |4 |10 |count | | |27acde85-acee-4e4a-a8d7-a00cba61b9a4 |workloads-per-gvc |Max number of workloads in a gvc |builtin |0 |5 |count |gvcLink="demo-cpln-gvc" | |3319b55b-c307-4240-ba79-f1892c76f70e |memory-per-workload |Max memory per container |builtin |0 |1024 |MiB | | |3dd4d521-6c5f-44a9-90bd-ae7f25fe2fca |replicas-per-workload |Max replicas per workload |builtin |0 |5 |count | | |605d0bd8-1263-447c-a925-b5712bd7c5ae |cpu-per-workload |Max CPU per container |builtin |0 |1000 |millicore | | |6d68c00c-7019-4b68-81cd-990ce092ead3 |workloads-per-gvc |Max number of workloads in a gvc |builtin |0 |5 |count |gvcLink="test-gvc" | |86f80e69-317b-42ce-b334-e5c041c25268 |identities-per-gvc |Max number of workload identities in ... |builtin |4 |20 |count |gvcLink="demo-gvc-identity" | |96b373be-d7e1-4e5a-b2dd-db4de190c59d |workloads-per-gvc |Max number of workloads in a gvc |builtin |0 |5 |count |gvcLink="demo-gvc-get-streams" | |9ab9a7df-b6da-45c1-bcdd-91c16df94252 |workloads-per-gvc |Max number of workloads in a gvc |builtin |1 |20 |count |gvcLink="demo-gvc-workload" | |b2a6bbc3-e3bb-4623-8689-9fcd71c1c86a |gvcs |Max number of gvc's |builtin |10 |20 |count | | |b4e49406-5671-480e-919c-4ec313949698 |workloads-per-gvc |Max number of workloads in a gvc |builtin |0 |3 |count |gvcLink="quick-start-4" | |ce98ccc8-e4da-4dcb-a61d-4761f22b8eb2 |domains |Max number of domains |builtin |3 |20 |count | | |d2e6bffd-f479-4824-976e-d5037a43a27f |workloads-per-gvc |Max number of workloads in a gvc |builtin |0 |5 |count |gvcLink="demo-gvc-logs" | |e31e22c9-3a7e-4548-bb2c-9467ca820381 |cloudaccounts |Max number of cloudaccounts |builtin |5 |10 |count | | |f684909c-ae6b-4234-a722-fb3ecf2a8eaa |workloads-per-gvc |Max number of workloads in a gvc |builtin |0 |20 |count |gvcLink="demo-gvc-with-workloads" | +-------------------------------------+----------------------+-----------------------------------------+--------+--------+-----+----------+----------------------------------+ ``` * Get one quota ```bash Command theme={null} cpln quota get b2a6bbc3-e3bb-4623-8689-9fcd71c1c86a --org demo-cpln-organization ``` ```text Output theme={null} +-------------------------------------+-----+--------------------+--------+--------+----+------+-----------+ |ID |NAME |DESCRIPTION |ORIGIN |CURRENT |MAX |UNIT |DIMENSIONS | |-------------------------------------|-----|--------------------|--------|--------|----|------|-----------| |b2a6bbc3-e3bb-4623-8689-9fcd71c1c86a |gvcs |Max number of gvc's |builtin |10 |20 |count | | +-------------------------------------+-----+--------------------+--------+--------+----+------+-----------+ ``` * Get multiple quotas ```bash Command theme={null} cpln quota get b2a6bbc3-e3bb-4623-8689-9fcd71c1c86a ce98ccc8-e4da-4dcb-a61d-4761f22b8eb2 --org demo-cpln-organization ``` ```text Output theme={null} +-------------------------------------+--------+----------------------+--------+--------+----+------+-----------+ |ID |NAME |DESCRIPTION |ORIGIN |CURRENT |MAX |UNIT |DIMENSIONS | |-------------------------------------|--------|----------------------|--------|--------|----|------|-----------| |b2a6bbc3-e3bb-4623-8689-9fcd71c1c86a |gvcs |Max number of gvc's |builtin |10 |20 |count | | |ce98ccc8-e4da-4dcb-a61d-4761f22b8eb2 |domains |Max number of domains |builtin |3 |20 |count | | +-------------------------------------+--------+----------------------+--------+--------+----+------+-----------+ ``` *** #### quota patch Update the referenced quota's metadata using an input file **Usage** ```none theme={null} cpln quota patch --file FILE_NAME [OPTIONS] ``` Running this command with the option **`--file -`** sets the input to stdin. The input is required to be a valid JSON or YAML string (e.g., `{"description": "My new description"}` or `description: My new description`). To complete the update, press Control-D (or Control-Z if you are using Windows). ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --file, -f File to load the patch from. Use `--file -` to enable input from stdin. [required] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln quota patch b2a6bbc3-e3bb-4623-8689-9fcd71c1c86a --file update.json --org demo-cpln-organization ``` ```text Output theme={null} The changes within the JSON file will be performed against the referenced quota. ``` *** #### quota permissions Show the grantable permissions for a quota object type **Usage** ```none theme={null} cpln quota permissions [OPTIONS] ``` ```text theme={null} Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln quota permissions --org demo-cpln-organization ``` ```text Output theme={null} +-------+-----------------------+---------------------------+ |NAME |DESCRIPTION |IMPLIES | |-------|-----------------------|---------------------------| |create |Create new quotas | | |edit |Modify existing quotas |view | |manage |Full access |create, edit, manage, view | |view |Read-only access | | +-------+-----------------------+---------------------------+ ``` *** #### quota query Find all the quotas based on the given query **Usage** ```none theme={null} cpln quota query QUERY_OPTIONS [OPTIONS] ``` ```text theme={null} Query options: --match Query filter type [choices: "all", "none", "any"] [default: "all"] --property, --prop Property to query --tag Tag to query --rel Relation to query Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) Use the `--match` option to display **all**, **none**, or **any** of the results based on a given property, tag, or relation * Search for a quota based on a property named `description` ```bash Command theme={null} cpln quota query --match any --prop description="Max number of domains" --org demo-cpln-organization ``` ```text Output theme={null} +-------------------------------------+--------+----------------------+--------+--------+----+------+-----------+ |ID |NAME |DESCRIPTION |ORIGIN |CURRENT |MAX |UNIT |DIMENSIONS | |-------------------------------------|--------|----------------------|--------|--------|----|------|-----------| |ce98ccc8-e4da-4dcb-a61d-4761f22b8eb2 |domains |Max number of domains |builtin |3 |20 |count | | +-------------------------------------+--------+----------------------+--------+--------+----+------+-----------+ ``` *** #### quota request Manage quota increase requests ```none theme={null} cpln quota request [SUBCOMMAND] ``` ```text theme={null} Commands: cpln quota request create Submit a quota increase request cpln quota request get Get a quota increase request and its status cpln quota request list List the org's quota increase requests, optionally filtered by status Options: --help Show help [boolean] ``` #### quota request create Submit a quota increase request **Usage** ```none theme={null} cpln quota request create [OPTIONS] ``` ```text theme={null} Positionals: name The name of the quota to request an increase for (e.g., gvcs). [string] [required] Command options: --requested-max The new maximum value being requested for the quota. [number] [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### quota request get Get a quota increase request and its status **Usage** ```none theme={null} cpln quota request get [OPTIONS] ``` ```text theme={null} Positionals: id The ID of the quota increase request to retrieve (as returned by `cpln quota request create`). [string] [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### quota request list List the org's quota increase requests, optionally filtered by status **Usage** ```none theme={null} cpln quota request list [OPTIONS] ``` ```text theme={null} Command options: --status Only list requests with this status. [choices: "pending", "approved", "denied"] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** # rest Source: https://docs.controlplane.com/cli-reference/commands/rest cpln rest — Submit raw REST requests directly against the Control Plane API. Submit REST requests against the API ```none theme={null} cpln rest [SUBCOMMAND] ``` ```text theme={null} Commands: cpln rest create Submit a POST request followed by a GET request against the referenced path cpln rest delete Submit a DELETE request against the referenced path cpln rest edit Launches the default editor with the contents of the referenced path cpln rest get Submit a GET request against the referenced path cpln rest patch Submit a PATCH request against the referenced path cpln rest post Submit a POST request against the referenced path cpln rest put Submit a PUT request against the referenced path Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` *** #### rest create Submit a POST request followed by a GET request against the referenced path **Usage** ```none theme={null} cpln rest create --file FILE_NAME [OPTIONS] ``` If using the option **--file -**, press Control-D (or Control-Z if you are using Windows) to submit the request ```text theme={null} Positionals: path Resource path (e.g., /org/test/user/adam) [required] Command options: --file, -f File to load the request from. Use `--file -` to enable input from stdin. --header, -H HTTP headers in curl format: "x-header: value" Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln rest create /org --file create-org.json ``` ```text Output theme={null} The metadata contained within the JSON file will be performed against the referenced path as a POST request. If successful, a GET request will be performed on the resource and outputted to stdout. ``` *** #### rest delete Submit a DELETE request against the referenced path **Usage** ```none theme={null} cpln rest delete [OPTIONS] ``` ```text theme={null} Positionals: path Resource path (e.g., /org/test/user/adam) [required] Command options: --header, -H HTTP headers in curl format: "x-header: value" Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln rest delete /org/demo-cpln-organization/gvc/demo-gvc ``` ```text Output theme={null} The GVC with the reference \`/org/demo-cpln-organization/gvc/demo-gvc\` will be deleted. ``` *** #### rest edit Launches the default editor with the contents of the referenced path **Usage** ```none theme={null} cpln rest edit [OPTIONS] ``` The default editor can be set by using the EDITOR environment variable. ```text theme={null} Positionals: path Resource path (e.g., /org/test/user/adam) [required] Command options: --header, -H HTTP headers in curl format: "x-header: value" Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln rest edit /org/demo-cpln-organization ``` ```text Output theme={null} Your default editor will be launched. Once any modifications are saved, the updates will be sent to the API as a PATCH request. ``` *** #### rest get Submit a GET request against the referenced path **Usage** ```none theme={null} cpln rest get [OPTIONS] ``` An empty reference will return all references ```text theme={null} Positionals: path Resource path (e.g., /org/test/user/adam) [required] Command options: --header, -H HTTP headers in curl format: "x-header: value" Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln rest get /org/demo-cpln-organization ``` ```text Output theme={null} +-----------------------+------------------+----------------------------+----------------------+ |NAME |DESCRIPTION |LINK |TAGS | |-----------------------|------------------|----------------------------|----------------------| |demo-cpln-organization |Demo Organization |/org/demo-cpln-organization |demo-key="demo-value" | | | | |type="demo" | +-----------------------+------------------+----------------------------+----------------------+ ``` *** #### rest patch Submit a PATCH request against the referenced path **Usage** ```none theme={null} cpln rest patch --file FILE_NAME [OPTIONS] ``` If using the option **--file -**, press Control-D (or Control-Z if you are using Windows) to submit the request ```text theme={null} Positionals: path Resource path (e.g., /org/test/user/adam) [required] Command options: --file, -f File to load the request from. Use `--file -` to enable input from stdin. --header, -H HTTP headers in curl format: "x-header: value" Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln rest patch /org/demo-cpln-organization --file update-org.json ``` ```text Output theme={null} The metadata contained within the JSON file will be performed against the referenced path as a PATCH request. ``` *** #### rest post Submit a POST request against the referenced path **Usage** ```none theme={null} cpln rest post --file FILE_NAME [OPTIONS] ``` If using the option **--file -**, press Control-D (or Control-Z if you are using Windows) to submit the request ```text theme={null} Positionals: path Resource path (e.g., /org/test/user/adam) [required] Command options: --file, -f File to load the request from. Use `--file -` to enable input from stdin. --header, -H HTTP headers in curl format: "x-header: value" Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln rest post /org --file new-org.json ``` ```text Output theme={null} The metadata contained within the JSON file will be performed against the referenced path as a POST request. ``` *** #### rest put Submit a PUT request against the referenced path **Usage** ```none theme={null} cpln rest put [OPTIONS] ``` If using the option **--file -**, press Control-D (or Control-Z if you are using Windows) to submit the request ```text theme={null} Positionals: path Resource path (e.g., /org/test/user/adam) [required] Command options: --file, -f File to load the request from. Use `--file -` to enable input from stdin. --header, -H HTTP headers in curl format: "x-header: value" Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln rest put /org/demo-cpln-organization --file update-org.json ``` ```text Output theme={null} The metadata contained within the JSON file will be performed against the referenced path as a PUT request. ``` *** # sandbox Source: https://docs.controlplane.com/cli-reference/commands/sandbox cpln sandbox — Manage sandboxes and connect to running sandbox environments. Manage sandboxes ```none theme={null} cpln sandbox [SUBCOMMAND] ``` ```text theme={null} Commands: cpln sandbox connect Connect to a running sandbox Options: --help Show help [boolean] ``` *** #### sandbox connect Connect to a running sandbox **Usage** ```none theme={null} cpln sandbox connect [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --ide IDE or connection mode to launch. [choices: "vscode", "cursor", "ssh", "browser", "terminal"] [default: "vscode"] --port Preferred local SSH port. Defaults to an available port chosen automatically. [number] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Connect to a running sandbox using the default IDE (VS Code) ```bash Command theme={null} cpln sandbox connect demo-sandbox --org demo-cpln-organization --gvc demo-cpln-gvc ``` ```text Output theme={null} VS Code launched. Press Ctrl+C to stop the port-forward when done. Reconnect anytime with: code --remote ssh-remote+cpln-dev-demo-sandbox /workspace ``` * Open an SSH tunnel to the sandbox and connect from any terminal or IDE ```bash Command theme={null} cpln sandbox connect demo-sandbox --ide ssh --org demo-cpln-organization --gvc demo-cpln-gvc ``` ```text Output theme={null} SSH tunnel ready. Connect with "ssh cpln-dev-demo-sandbox", or use "cpln-dev-demo-sandbox" as a Remote-SSH host in your IDE. Press Ctrl+C to stop the port-forward when done. ``` * Open an SSH tunnel using a preferred local port ```bash Command theme={null} cpln sandbox connect demo-sandbox --ide ssh --port 2222 --org demo-cpln-organization --gvc demo-cpln-gvc ``` ```text Output theme={null} SSH tunnel ready. Connect with "ssh cpln-dev-demo-sandbox", or use "cpln-dev-demo-sandbox" as a Remote-SSH host in your IDE. Press Ctrl+C to stop the port-forward when done. ``` * Open the sandbox IDE in the browser ```bash Command theme={null} cpln sandbox connect demo-sandbox --ide browser --org demo-cpln-organization --gvc demo-cpln-gvc ``` ```text Output theme={null} Your sandbox password: If your browser did not open, visit: https://demo-sandbox-demo-cpln-gvc.cpln.app/_ide/ ``` *** # secret Source: https://docs.controlplane.com/cli-reference/commands/secret cpln secret — Manage secrets for securely storing sensitive data such as credentials and keys. Manage secrets For a detailed overview, please visit the [secret's](/reference/secret) reference page. ```none theme={null} cpln secret [SUBCOMMAND] ``` ```text theme={null} Commands: cpln secret access-report Show the access report for the referenced secret cpln secret audit [ref] Retrieve audit trail events for the referenced secret cpln secret clone Create a clone of the referenced secret [aliases: copy] cpln secret create-aws Create a new AWS secret cpln secret create-azure-connector Create a new Azure Connector secret cpln secret create-azure-sdk Create a new Azure SDK secret cpln secret create-dictionary Create a new dictionary secret [aliases: create-dict] cpln secret create-docker Create a new Docker secret cpln secret create-ecr Create a new ECR secret cpln secret create-gcp Create a new GCP secret cpln secret create-keypair Create a new KeyPair secret cpln secret create-nats Create a new nats account secret cpln secret create-opaque Create a new opaque secret cpln secret create-tls Create a new TLS secret cpln secret create-userpass Create a new Username/Password secret cpln secret delete Delete one or more referenced secrets cpln secret edit Edit the referenced secret, as YAML, within an editor cpln secret eventlog Show the event log of the referenced secret [aliases: log] cpln secret get [ref...] Retrieve one or more referenced secrets cpln secret patch Update the referenced secret's metadata using an input file cpln secret permissions Show the grantable permissions for a secret object type cpln secret query Find all the secrets based on the given query cpln secret reveal [ref] Show the secret in plaintext cpln secret tag Manage the tags belonging to one or more referenced secrets cpln secret update Update properties of the referenced secret Options: --help Show help [boolean] ``` *** #### secret access-report Show the access report for the referenced secret **Usage** ```none theme={null} cpln secret access-report [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln secret access-report demo-secret --org demo-cpln-organization ``` ```text Output theme={null} +-----------+--------------------------------------------------------+--------+ |PERMISSION |PRINCIPAL |GRANTED | |-----------|--------------------------------------------------------|--------| |create |/org/demo-cpln-organization/group/superusers |manage | |create |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |delete |/org/demo-cpln-organization/group/superusers |manage | |delete |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |edit |/org/demo-cpln-organization/group/superusers |manage | |edit |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |manage |/org/demo-cpln-organization/group/superusers |manage | |manage |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |reveal |/org/demo-cpln-organization/group/superusers |manage | |reveal |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |use |/org/demo-cpln-organization/group/superusers |manage | |use |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |view |/org/demo-cpln-organization/group/viewers |view | |view |/org/demo-cpln-organization/group/superusers |manage | |view |/org/demo-cpln-organization/serviceaccount/controlplane |manage | +-----------+--------------------------------------------------------+--------+ ``` *** #### secret audit Retrieve audit trail events for the referenced secret **Usage** ```none theme={null} cpln secret audit [ref] [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. Command options: --subject Filter by subject (serviceaccount name or user email) [string] --context Audit context name (the 'cpln' context displays Control Plane actions) [string] [default: "cpln"] --since Relative lookback window from now (e.g., 1h, 24h, 7d). Mutually exclusive with --from/--to [string] [default: "7d"] --from Start time for audit events (ISO 8601 format or relative duration, e.g., 2025-10-23T07:00:00Z, 7d, 1h). Cannot be used with --since [string] --to End time for audit events (ISO 8601 format or relative duration, e.g., 2025-10-23T07:00:00Z, 1d, 30m) [string] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### secret clone Create a clone of the referenced secret **Usage** ```none theme={null} cpln secret clone --name CLONE_NAME [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --name Set the name for the clone [required] --description Optional description, defaults to the name if not set --tag Attach tags (e.g., --tag drink=water) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln secret clone demo-secret --name demo-secret-clone --org demo-cpln-organization ``` ```text Output theme={null} +------------------+---------------------+---------+--------+--------------+ |NAME |DESCRIPTION |TYPE |CREATED |TAGS | |------------------|---------------------|---------|--------|--------------| |demo-secret-clone |Clone of demo-secret |userpass |Now |drink="water" | +------------------+---------------------+---------+--------+--------------+ ``` *** #### secret create-aws Create a new AWS secret **Usage** ```none theme={null} cpln secret create-aws --name SECRET_NAME --access-key AWS_ACCESS_KEY --secret-key AWS_SECRET_KEY [OPTIONS] ``` ```text theme={null} Command options: --name Name of the secret [required] --description, --desc Optional description, defaults to the name if not set --access-key AWS access key [required] --secret-key AWS secret key [required] --role-arn AWS role. Optional: Only if using "assumeRole" --external-id AWS IAM Role External Id --tag Attach tags (e.g., --tag drink=water) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### secret create-azure-connector Create a new Azure Connector secret **Usage** ```none theme={null} cpln secret create-azure-connector --name SECRET_NAME --url DEPLOYMENT_URL --code FUNCTION_APP_CODE [OPTIONS] ``` ```text theme={null} Command options: --name Name of the secret [required] --description, --desc Optional description, defaults to the name if not set --url URL of the azure connector function [required] --code Code of the azure connector function [required] --tag Attach tags (e.g., --tag drink=water) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### secret create-azure-sdk Create a new Azure SDK secret **Usage** ```none theme={null} cpln secret create-azure-sdk --name SECRET_NAME [OPTIONS] ``` ```text theme={null} Command options: --name Name of the secret [required] --description, --desc Optional description, defaults to the name if not set --file, -f Load the secret from a file. The file is usually created using the command `az ad sp create-for-rbac` [required] --tag Attach tags (e.g., --tag drink=water) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### secret create-dictionary Create a new dictionary secret **Usage** ```none theme={null} cpln secret create-dictionary --name SECRET_NAME --entry KEY=VALUE [OPTIONS] ``` ```text theme={null} Command options: --name Name of the secret [required] --description, --desc Optional description, defaults to the name if not set --entry Add an entry to the dictionary. Use the syntax: `key=value` [required] --tag Attach tags (e.g., --tag drink=water) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### secret create-docker Create a new Docker secret **Usage** ```none theme={null} cpln secret create-docker --name SECRET_NAME --file FILE_NAME ``` ```text theme={null} Command options: --name Name of the secret [required] --description, --desc Optional description, defaults to the name if not set --file, -f Load the docker config from a file (e.g., ~/.docker/config.json) [required] --tag Attach tags (e.g., --tag drink=water) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### secret create-ecr Create a new ECR secret **Usage** ```none theme={null} cpln secret create-ecr --name SECRET_NAME --access-key AWS_ACCESS_KEY --repo ECR_REPO [OPTIONS] ``` ```text theme={null} Command options: --name Name of the secret [required] --description, --desc Optional description, defaults to the name if not set --access-key AWS access key [required] --secret-key AWS secret key [required] --role-arn AWS role. Optional: Only if using "assumeRole" --repo ECR repository URI (e.g., AWS_ACCOUNT_ID.dkr.ecr.REGION.amazonaws.com/REPO_NAME) [required] --external-id AWS IAM Role External Id --tag Attach tags (e.g., --tag drink=water) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### secret create-gcp Create a new GCP secret **Usage** ```none theme={null} cpln secret create-gcp --name SECRET_NAME --file FILE_NAME [OPTIONS] ``` ```text theme={null} Command options: --name Name of the secret [required] --description, --desc Optional description, defaults to the name if not set --file, -f Load the secret from a file. The file is usually exported from the GCP console [required] --tag Attach tags (e.g., --tag drink=water) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### secret create-keypair Create a new KeyPair secret **Usage** ```none theme={null} cpln secret create-keypair --name SECRET_NAME --secret PATH_TO_SECRET_KEY [OPTIONS] ``` ```text theme={null} Command options: --name Name of the secret [required] --description, --desc Optional description, defaults to the name if not set --secret Path to the secret key file [required] --public Path to the public key file --passphrase Passpharse for the private key --tag Attach tags (e.g., --tag drink=water) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### secret create-nats Create a new nats account secret **Usage** ```none theme={null} cpln secret create-nats --name SECRET_NAME --account-id ACCOUNT_ID --private-key PRIVATE_KEY [OPTIONS] ``` ```text theme={null} Command options: --name Name of the secret [required] --description, --desc Optional description, defaults to the name if not set --account-id Account Id of the NATS account [required] --private-key Private Key to access the NATS account [required] --tag Attach tags (e.g., --tag drink=water) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### secret create-opaque Create a new opaque secret **Usage** ```none theme={null} cpln secret create-opaque --name SECRET_NAME --file FILE_NAME [OPTIONS] ``` ```text theme={null} Command options: --name Name of the secret [required] --description, --desc Optional description, defaults to the name if not set --file, -f Load the secret content from a file, use - for stdin [required] --encoding Set encoding. Available options: "base64", "plain" [default: "base64"] --tag Attach tags (e.g., --tag drink=water) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### secret create-tls Create a new TLS secret **Usage** ```none theme={null} cpln secret create-tls --name SECRET_NAME --key PATH_TO_KEY --cert PATH_TO_CERTIFICATE [OPTIONS] ``` ```text theme={null} Command options: --name Name of the secret [required] --description, --desc Optional description, defaults to the name if not set --key Path to the private key file in PEM format [required] --cert Path to the certificate file in PEM format [required] --chain Path to the certificate chain file in PEM format --tag Attach tags (e.g., --tag drink=water) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### secret create-userpass Create a new Username/Password secret **Usage** ```none theme={null} cpln secret create-userpass --name SECRET_NAME --username USERNAME --password PASSWORD [OPTIONS] ``` ```text theme={null} Command options: --name Name of the secret [required] --description, --desc Optional description, defaults to the name if not set --username Username [required] --password Password [required] --tag Attach tags (e.g., --tag drink=water) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln secret create-userpass --name demo-secret --username username --password password --desc demo-secret --tag drink=water --org demo-cpln-organization ``` ```text Output theme={null} +------------+------------+---------+--------+--------------+ |NAME |DESCRIPTION |TYPE |CREATED |TAGS | |------------|------------|---------|--------|--------------| |demo-secret |demo-secret |userpass |Now |drink="water" | +------------+------------+---------+--------+--------------+ ``` *** #### secret delete Delete one or more referenced secrets **Usage** ```none theme={null} cpln secret delete [OPTIONS] ``` ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [required] [default: []] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln secret delete demo-secret --org demo-cpln-organization ``` ```text Output theme={null} If the delete was successful, a new command prompt will be shown. ``` *** #### secret edit Edit the referenced secret, as YAML, within an editor **Usage** ```none theme={null} cpln secret edit [OPTIONS] ``` The default editor can be set by using the EDITOR environment variable. ```text theme={null} Command options: --replace, -r Replace instead of patch/merge [boolean] [default: "false"] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln secret edit demo-secret --org demo-cpln-organization ``` ```text Output theme={null} Your default editor will be launched. Once any modifications are saved, the updates will be sent to the API. ``` *** #### secret eventlog Show the event log of the referenced secret **Usage** ```none theme={null} cpln secret eventlog [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln secret eventlog demo-secret --org demo-cpln-organization ``` ```text Output theme={null} The event log for the referenced secret will be shown. ``` *** #### secret get Retrieve one or more referenced secrets **Usage** ```none theme={null} cpln secret get [ref...] [OPTIONS] ``` An empty reference will return all references ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [default: []] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Get all secrets ```bash Command theme={null} cpln secret get --org demo-cpln-organization ``` ```text Output theme={null} +------------------+------------------+----------------+--------------+--------------+ |NAME |DESCRIPTION |TYPE |CREATED |TAGS | |------------------|------------------|----------------|--------------|--------------| |azure-secret |azure-secret |azure-connector |4 minutes ago | | |demo-secret |demo-secret |userpass |4 minutes ago |drink="water" | |demo-secret-get-1 |demo-secret-get-1 |userpass |Now | | |demo-secret-get-2 |demo-secret-get-2 |userpass |Now | | +------------------+------------------+----------------+--------------+--------------+ ``` * Get one secret ```bash Command theme={null} cpln secret get demo-secret-get-1 --org demo-cpln-organization ``` ```text Output theme={null} +------------------+------------------+---------+--------+-----+ |NAME |DESCRIPTION |TYPE |CREATED |TAGS | |------------------|------------------|---------|--------|-----| |demo-secret-get-1 |demo-secret-get-1 |userpass |Now | | +------------------+------------------+---------+--------+-----+ ``` * Get multiple secrets ```bash Command theme={null} cpln secret get demo-secret-get-1 demo-secret-get-2 --org demo-cpln-organization ``` ```text Output theme={null} +------------------+------------------+---------+--------+-----+ |NAME |DESCRIPTION |TYPE |CREATED |TAGS | |------------------|------------------|---------|--------|-----| |demo-secret-get-1 |demo-secret-get-1 |userpass |Now | | |demo-secret-get-2 |demo-secret-get-2 |userpass |Now | | +------------------+------------------+---------+--------+-----+ ``` *** #### secret patch Update the referenced secret's metadata using an input file **Usage** ```none theme={null} cpln secret patch --file FILE_NAME [OPTIONS] ``` Running this command with the option **`--file -`** sets the input to stdin. The input is required to be a valid JSON or YAML string (e.g., `{"description": "My new description"}` or `description: My new description`). To complete the update, press Control-D (or Control-Z if you are using Windows). ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --file, -f File to load the patch from. Use `--file -` to enable input from stdin. [required] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln secret patch demo-secret --file update.json --org demo-cpln-organization ``` ```text Output theme={null} The changes within the JSON file will be performed against the referenced secret. ``` *** #### secret permissions Show the grantable permissions for a secret object type **Usage** ```none theme={null} cpln secret permissions [OPTIONS] ``` ```text theme={null} Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln secret permissions --org demo-cpln-organization ``` ```text Output theme={null} +-------+-----------------------------------------+------------------------------------------------+ |NAME |DESCRIPTION |IMPLIES | |-------|-----------------------------------------|------------------------------------------------| |create |Create new secrets | | |delete |Delete secrets | | |edit |Modify existing secrets |view, reveal | |manage |Full access |create, delete, edit, manage, reveal, use, view | |reveal |Reveal the plaintext of the secret |view | |use |Refer to this secret from other entit... |view | |view |Read-only access excluding plaintext | | +-------+-----------------------------------------+------------------------------------------------+ ``` *** #### secret query Find all the secrets based on the given query **Usage** ```none theme={null} cpln secret query QUERY_OPTIONS [OPTIONS] ``` ```text theme={null} Query options: --match Query filter type [choices: "all", "none", "any"] [default: "all"] --property, --prop Property to query --tag Tag to query --rel Relation to query Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) Use the `--match` option to display **all**, **none**, or **any** of the results based on a given property or tag * Search for a secret based on the name property ```bash Command theme={null} cpln secret query --match any --prop name=demo-secret --org demo-cpln-organization ``` ```text Output theme={null} +------------+------------+---------+--------------+--------------+ |NAME |DESCRIPTION |TYPE |CREATED |TAGS | |------------|------------|---------|--------------|--------------| |demo-secret |demo-secret |userpass |4 minutes ago |drink="water" | +------------+------------+---------+--------------+--------------+ ``` * Search for a secret based on the description property ```bash Command theme={null} cpln secret query --match any --prop description="demo-secret" --org demo-cpln-organization ``` ```text Output theme={null} +------------+------------+---------+--------------+--------------+ |NAME |DESCRIPTION |TYPE |CREATED |TAGS | |------------|------------|---------|--------------|--------------| |demo-secret |demo-secret |userpass |4 minutes ago |drink="water" | +------------+------------+---------+--------------+--------------+ ``` * Search for a secret based on a tag ```bash Command theme={null} cpln secret query --match any --tag drink=water --org demo-cpln-organization ``` ```text Output theme={null} +------------+------------+---------+--------------+--------------+ |NAME |DESCRIPTION |TYPE |CREATED |TAGS | |------------|------------|---------|--------------|--------------| |demo-secret |demo-secret |userpass |4 minutes ago |drink="water" | +------------+------------+---------+--------------+--------------+ ``` * Search for a secret based on a property and tag ```bash Command theme={null} cpln secret query --match any --prop description="demo-secret" --tag drink=water --org demo-cpln-organization ``` ```text Output theme={null} +------------+------------+---------+--------------+--------------+ |NAME |DESCRIPTION |TYPE |CREATED |TAGS | |------------|------------|---------|--------------|--------------| |demo-secret |demo-secret |userpass |4 minutes ago |drink="water" | +------------+------------+---------+--------------+--------------+ ``` *** #### secret reveal Show the secret in plaintext **Usage** ```none theme={null} cpln secret reveal [ref] [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names", "smart"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Reveal the secret of a UserPass secret ```bash Command theme={null} cpln secret reveal demo-secret --org demo-cpln-organization ``` ```text Output theme={null} +------------+------------+---------+--------------+--------------+ |NAME |DESCRIPTION |TYPE |CREATED |TAGS | |------------|------------|---------|--------------|--------------| |demo-secret |demo-secret |userpass |4 minutes ago |drink="water" | +------------+------------+---------+--------------+--------------+ ``` *** #### secret tag Manage the tags belonging to one or more referenced secrets **Usage** ```none theme={null} cpln secret tag --tag KEY=VALUE [OPTIONS] ``` Built-in tags, which begin with cpln/\*, can't be modified. ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [required] [default: []] Command options: --tag Attach tags (e.g., --tag drink=water) --remove-tag Remove tags (e.g., --remove-tag tagname) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Add a tag to a secret ```bash Command theme={null} cpln secret tag demo-secret-tag-1 --tag drink=water --org demo-cpln-organization ``` ```text Output theme={null} +------------------+------------------+---------+--------+--------------+ |NAME |DESCRIPTION |TYPE |CREATED |TAGS | |------------------|------------------|---------|--------|--------------| |demo-secret-tag-1 |demo-secret-tag-1 |userpass |Now |drink="water" | +------------------+------------------+---------+--------+--------------+ ``` * Update same tag (after running the example above) ```bash Command theme={null} cpln secret tag demo-secret-tag-1 --tag drink=coffee --org demo-cpln-organization ``` ```text Output theme={null} +------------------+------------------+---------+--------+---------------+ |NAME |DESCRIPTION |TYPE |CREATED |TAGS | |------------------|------------------|---------|--------|---------------| |demo-secret-tag-1 |demo-secret-tag-1 |userpass |Now |drink="coffee" | +------------------+------------------+---------+--------+---------------+ ``` * Remove tag (after running the example above) ```bash Command theme={null} cpln secret tag demo-secret-tag-1 --remove drink --org demo-cpln-organization ``` ```text Output theme={null} +------------------+------------------+---------+--------+-----+ |NAME |DESCRIPTION |TYPE |CREATED |TAGS | |------------------|------------------|---------|--------|-----| |demo-secret-tag-1 |demo-secret-tag-1 |userpass |Now | | +------------------+------------------+---------+--------+-----+ ``` * Add same tag to multiple secrets ```bash Command theme={null} cpln secret tag demo-secret-tag-1 demo-secret-tag-2 --tag multiple-secrets=true --org demo-cpln-organization ``` ```text Output theme={null} +------------------+------------------+---------+--------+------------------------+ |NAME |DESCRIPTION |TYPE |CREATED |TAGS | |------------------|------------------|---------|--------|------------------------| |demo-secret-tag-1 |demo-secret-tag-1 |userpass |Now |multiple-secrets="true" | |demo-secret-tag-2 |demo-secret-tag-2 |userpass |Now |multiple-secrets="true" | +------------------+------------------+---------+--------+------------------------+ ``` * Update same tag on multiple secrets (after running the example above) ```bash Command theme={null} cpln secret tag demo-secret-tag-1 demo-secret-tag-2 --tag multiple-secrets=demo-secret --org demo-cpln-organization ``` ```text Output theme={null} +------------------+------------------+---------+--------+-------------------------------+ |NAME |DESCRIPTION |TYPE |CREATED |TAGS | |------------------|------------------|---------|--------|-------------------------------| |demo-secret-tag-1 |demo-secret-tag-1 |userpass |Now |multiple-secrets="demo-secret" | |demo-secret-tag-2 |demo-secret-tag-2 |userpass |Now |multiple-secrets="demo-secret" | +------------------+------------------+---------+--------+-------------------------------+ ``` * Remove same tag from multiple secrets (after running the example above) ```bash Command theme={null} cpln secret tag demo-secret-tag-1 demo-secret-tag-2 --remove multiple-secrets --org demo-cpln-organization ``` ```text Output theme={null} +------------------+------------------+---------+--------+-----+ |NAME |DESCRIPTION |TYPE |CREATED |TAGS | |------------------|------------------|---------|--------|-----| |demo-secret-tag-1 |demo-secret-tag-1 |userpass |Now | | |demo-secret-tag-2 |demo-secret-tag-2 |userpass |Now | | +------------------+------------------+---------+--------+-----+ ``` *** #### secret update Update properties of the referenced secret **Usage** ```none theme={null} cpln secret update --set UPDATE_PROPERTY [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Update Properties: --set Update the following properties (e.g., --set description="Updated Description"): description string tags. string --unset Unset a property (same paths as --set, e.g., --unset description) Command options: --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * The `--set` option allows the modification of the following secret properties: * `description` * Description of the secret * Type: string * `tags` * Tags (key=value) associated with the secret * Type: string * `targetLinks` * Array of target links belonging to the secret * Type: string * `target` * The target of the secret * Type: string * Allowed values: 'all' * For the examples below, the initial secret is configured as: ``` +-------------------+-------------------+---------+--------+-----+ |NAME |DESCRIPTION |TYPE |CREATED |TAGS | |-------------------|-------------------|---------|--------|-----| |demo-secret-update |demo-secret-update |userpass |Now | | +-------------------+-------------------+---------+--------+-----+ ``` * Update a secret's description ```bash Command theme={null} cpln secret update demo-secret-update --set description="new description" --org demo-cpln-organization ``` ```text Output theme={null} +-------------------+----------------+---------+--------+-----+ |NAME |DESCRIPTION |TYPE |CREATED |TAGS | |-------------------|----------------|---------|--------|-----| |demo-secret-update |new description |userpass |Now | | +-------------------+----------------+---------+--------+-----+ ``` * Update a secret's tag ```bash Command theme={null} cpln secret update demo-secret-update --set tags.demo-key=demo-value --org demo-cpln-organization ``` ```text Output theme={null} +-------------------+----------------+---------+--------+----------------------+ |NAME |DESCRIPTION |TYPE |CREATED |TAGS | |-------------------|----------------|---------|--------|----------------------| |demo-secret-update |new description |userpass |Now |demo-key="demo-value" | +-------------------+----------------+---------+--------+----------------------+ ``` *** # serviceaccount Source: https://docs.controlplane.com/cli-reference/commands/serviceaccount cpln serviceaccount — Manage service accounts used for programmatic API access. Manage service accounts For a detailed overview, please visit the [service account's](/reference/serviceaccount) reference page. ```none theme={null} cpln serviceaccount [SUBCOMMAND] ``` ```text theme={null} Commands: cpln serviceaccount access-report Show the access report for the referenced service account cpln serviceaccount add-key Add a key to the reference service account cpln serviceaccount audit [ref] Retrieve audit trail events for the referenced service account cpln serviceaccount clone Create a clone of the referenced service account; this will only duplicate its spec. [aliases: copy] cpln serviceaccount create Create a new service account cpln serviceaccount delete Delete one or more referenced service accounts cpln serviceaccount edit Edit the referenced service account, as YAML, within an editor cpln serviceaccount eventlog Show the event log of the referenced service account [aliases: log] cpln serviceaccount get [ref...] Retrieve one or more referenced service accounts cpln serviceaccount patch Update the referenced service account's metadata using an input file cpln serviceaccount permissions Show the grantable permissions for a service account object type cpln serviceaccount query Find all the service accounts based on the given query cpln serviceaccount remove-key Remove a key from the reference service account cpln serviceaccount tag Manage the tags belonging to one or more referenced service accounts Options: --help Show help [boolean] ``` *** #### serviceaccount access-report Show the access report for the referenced service account **Usage** ```none theme={null} cpln serviceaccount access-report [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln serviceaccount access-report demo-service-account --org demo-cpln-organization ``` ```text Output theme={null} +-----------+--------------------------------------------------------+--------+ |PERMISSION |PRINCIPAL |GRANTED | |-----------|--------------------------------------------------------|--------| |addKey |/org/demo-cpln-organization/group/superusers |manage | |addKey |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |create |/org/demo-cpln-organization/group/superusers |manage | |create |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |delete |/org/demo-cpln-organization/group/superusers |manage | |delete |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |edit |/org/demo-cpln-organization/group/superusers |manage | |edit |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |manage |/org/demo-cpln-organization/group/superusers |manage | |manage |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |view |/org/demo-cpln-organization/group/superusers |manage | |view |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |view |/org/demo-cpln-organization/group/viewers |view | +-----------+--------------------------------------------------------+--------+ ``` *** #### serviceaccount add-key Add a key to the reference service account **Usage** ```none theme={null} cpln serviceaccount add-key --description KEY_DESCRIPTION [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --description, --desc Short description for the new key [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) The 'key' property returned from the add-key command will only be displayed once. There is no way to retrieve the key in the future. ```bash Command theme={null} cpln serviceaccount add-key demo-service-account --desc demo-key --org demo-cpln-organization ``` ```text Output theme={null} { "description": "demo-key", "created": "2026-08-12T13:35:26.556Z", "key": "sWRt5J1LPYU9nQYg.ZGVtby1jcGxuLW9yZ2FuaXphdGlvbi5kZW1vLXNlcnZpY2UtYWNjb3VudC5qaVZENFFMYzFBQXVScDB5a3YvTDliQ0V5ZURMWnRKVWNSaWE1N1kyVHY0PQ.FHIin0_tOeCyV1ZGLf88CI8xCcroEp10mxtKPUkPMJo" } ``` *** #### serviceaccount audit Retrieve audit trail events for the referenced service account **Usage** ```none theme={null} cpln serviceaccount audit [ref] [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. Command options: --subject Filter by subject (serviceaccount name or user email) [string] --context Audit context name (the 'cpln' context displays Control Plane actions) [string] [default: "cpln"] --since Relative lookback window from now (e.g., 1h, 24h, 7d). Mutually exclusive with --from/--to [string] [default: "7d"] --from Start time for audit events (ISO 8601 format or relative duration, e.g., 2025-10-23T07:00:00Z, 7d, 1h). Cannot be used with --since [string] --to End time for audit events (ISO 8601 format or relative duration, e.g., 2025-10-23T07:00:00Z, 1d, 30m) [string] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### serviceaccount clone Create a clone of the referenced service account; this will only duplicate its spec. **Usage** ```none theme={null} cpln serviceaccount clone --name CLONE_NAME [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --name Set the name for the clone [required] --description Optional description, defaults to the name if not set --tag Attach tags (e.g., --tag drink=water) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln serviceaccount clone demo-service-account --name demo-service-account-clone --org demo-cpln-organization ``` ```text Output theme={null} +---------------------------+------------------------------+--------+--------+-----+--------------+ |NAME |DESCRIPTION |CREATED |ORIGIN |KEYS |TAGS | |---------------------------|------------------------------|--------|--------|-----|--------------| |demo-service-account-clone |Clone of demo-service-account |Now |default | |drink="water" | +---------------------------+------------------------------+--------+--------+-----+--------------+ ``` *** #### serviceaccount create Create a new service account **Usage** ```none theme={null} cpln serviceaccount create --name SERVICE_ACCOUNT_NAME [OPTIONS] ``` ```text theme={null} Command options: --name Name of the new secret [required] --description, --desc Optional description, defaults to the name if not set --tag Attach tags (e.g., --tag drink=water) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln serviceaccount create --name demo-service-account --desc demo-service-account --tag drink=water --org demo-cpln-organization ``` ```text Output theme={null} +---------------------+---------------------+--------+--------+-----+--------------+ |NAME |DESCRIPTION |CREATED |ORIGIN |KEYS |TAGS | |---------------------|---------------------|--------|--------|-----|--------------| |demo-service-account |demo-service-account |Now |default | |drink="water" | +---------------------+---------------------+--------+--------+-----+--------------+ ``` *** #### serviceaccount delete Delete one or more referenced service accounts **Usage** ```none theme={null} cpln serviceaccount delete [OPTIONS] ``` Deleting a service account will remove all associated keys ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [required] [default: []] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln serviceaccount delete demo-service-account --org demo-cpln-organization ``` ```text Output theme={null} If the delete was successful, a new command prompt will be shown. ``` *** #### serviceaccount edit Edit the referenced service account, as YAML, within an editor **Usage** ```none theme={null} cpln serviceaccount edit [OPTIONS] ``` The default editor can be set by using the EDITOR environment variable. ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --replace, -r Replace instead of patch/merge [boolean] [default: false] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln serviceaccount edit demo-service-account --org demo-cpln-organization ``` ```text Output theme={null} Your default editor will be launched. Once any modifications are saved, the updates will be sent to the API. ``` *** #### serviceaccount eventlog Show the event log of the referenced service account **Usage** ```none theme={null} cpln serviceaccount eventlog [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln serviceaccount eventlog demo-service-account --org demo-cpln-organization ``` ```text Output theme={null} The event log for the referenced service account will be shown. ``` *** #### serviceaccount get Retrieve one or more referenced service accounts **Usage** ```none theme={null} cpln serviceaccount get [ref...] [OPTIONS] ``` An empty reference will return all references ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [default: []] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Get all service accounts ```bash Command theme={null} cpln serviceaccount get --org demo-cpln-organization ``` ```text Output theme={null} +---------------------------+-----------------------------------------+--------------+--------+-----------------+--------------+ |NAME |DESCRIPTION |CREATED |ORIGIN |KEYS |TAGS | |---------------------------|-----------------------------------------|--------------|--------|-----------------|--------------| |controlplane |Built-in serviceaccount used by Contr... |5 years ago |builtin | | | |cpln-user |cpln-user |2 years ago |default |sOajhentlsc226k8 | | |demo-service-account |demo-service-account |5 minutes ago |default |sWRt5J1LPYU9nQYg |drink="water" | |demo-service-account-get-1 |demo-service-account-get-1 |Now |default | | | |demo-service-account-get-2 |demo-service-account-get-2 |Now |default | | | +---------------------------+-----------------------------------------+--------------+--------+-----------------+--------------+ ``` * Get one service account ```bash Command theme={null} cpln serviceaccount get demo-service-account-get-1 --org demo-cpln-organization ``` ```text Output theme={null} +---------------------------+---------------------------+--------+--------+-----+-----+ |NAME |DESCRIPTION |CREATED |ORIGIN |KEYS |TAGS | |---------------------------|---------------------------|--------|--------|-----|-----| |demo-service-account-get-1 |demo-service-account-get-1 |Now |default | | | +---------------------------+---------------------------+--------+--------+-----+-----+ ``` * Get multiple service accounts ```bash Command theme={null} cpln serviceaccount get demo-service-account-get-1 demo-service-account-get-2 --org demo-cpln-organization ``` ```text Output theme={null} +---------------------------+---------------------------+--------+--------+-----+-----+ |NAME |DESCRIPTION |CREATED |ORIGIN |KEYS |TAGS | |---------------------------|---------------------------|--------|--------|-----|-----| |demo-service-account-get-1 |demo-service-account-get-1 |Now |default | | | |demo-service-account-get-2 |demo-service-account-get-2 |Now |default | | | +---------------------------+---------------------------+--------+--------+-----+-----+ ``` *** #### serviceaccount patch Update the referenced service account's metadata using an input file **Usage** ```none theme={null} cpln serviceaccount patch --file FILE_NAME [OPTIONS] ``` Running this command with the option **`--file -`** sets the input to stdin. The input is required to be a valid JSON or YAML string (e.g., `{"description": "My new description"}` or `description: My new description`). To complete the update, press Control-D (or Control-Z if you are using Windows). ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --file, -f File to load the patch from. Use `--file -` to enable input from stdin. [required] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln serviceaccount patch demo-service-account --file update.json --org demo-cpln-organization ``` ```text Output theme={null} The changes within the JSON file will be performed against the referenced service account. ``` *** #### serviceaccount permissions Show the grantable permissions for a service account object type **Usage** ```none theme={null} cpln serviceaccount permissions [OPTIONS] ``` ```text theme={null} Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln serviceaccount permissions --org demo-cpln-organization ``` ```text Output theme={null} +-------+---------------------------------------+-------------------------------------------+ |NAME |DESCRIPTION |IMPLIES | |-------|---------------------------------------|-------------------------------------------| |addKey |Add key to an existing service account | | |create |Create new service accounts | | |delete |Delete service accounts | | |edit |Modify existing resources |view | |manage |Full access |addKey, create, delete, edit, manage, view | |view |Read-only access | | +-------+---------------------------------------+-------------------------------------------+ ``` *** #### serviceaccount query Find all the service accounts based on the given query **Usage** ```none theme={null} cpln serviceaccount query QUERY_OPTIONS [OPTIONS] ``` ```text theme={null} Query options: --match Query filter type [choices: "all", "none", "any"] [default: "all"] --property, --prop Property to query --tag Tag to query --rel Relation to query Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) Use the `--match` option to display **all**, **none**, or **any** of the results based on a given property or tag * Search for a service account based on the name property ```bash Command theme={null} cpln serviceaccount query --match any --prop name=demo-service-account --org demo-cpln-organization ``` ```text Output theme={null} +---------------------+---------------------+--------------+--------+-----------------+--------------+ |NAME |DESCRIPTION |CREATED |ORIGIN |KEYS |TAGS | |---------------------|---------------------|--------------|--------|-----------------|--------------| |demo-service-account |demo-service-account |5 minutes ago |default |sWRt5J1LPYU9nQYg |drink="water" | +---------------------+---------------------+--------------+--------+-----------------+--------------+ ``` * Search for a service account based on the description property ```bash Command theme={null} cpln serviceaccount query --match any --prop description="demo-service-account" --org demo-cpln-organization ``` ```text Output theme={null} +---------------------+---------------------+--------------+--------+-----------------+--------------+ |NAME |DESCRIPTION |CREATED |ORIGIN |KEYS |TAGS | |---------------------|---------------------|--------------|--------|-----------------|--------------| |demo-service-account |demo-service-account |5 minutes ago |default |sWRt5J1LPYU9nQYg |drink="water" | +---------------------+---------------------+--------------+--------+-----------------+--------------+ ``` * Search for a service account based on a tag ```bash Command theme={null} cpln serviceaccount query --match any --tag drink=water --org demo-cpln-organization ``` ```text Output theme={null} +---------------------+---------------------+--------------+--------+-----------------+--------------+ |NAME |DESCRIPTION |CREATED |ORIGIN |KEYS |TAGS | |---------------------|---------------------|--------------|--------|-----------------|--------------| |demo-service-account |demo-service-account |5 minutes ago |default |sWRt5J1LPYU9nQYg |drink="water" | +---------------------+---------------------+--------------+--------+-----------------+--------------+ ``` * Search for a service account based on a property and tag ```bash Command theme={null} cpln serviceaccount query --match any --prop description="demo-service-account" --tag drink=water --org demo-cpln-organization ``` ```text Output theme={null} +---------------------+---------------------+--------------+--------+-----------------+--------------+ |NAME |DESCRIPTION |CREATED |ORIGIN |KEYS |TAGS | |---------------------|---------------------|--------------|--------|-----------------|--------------| |demo-service-account |demo-service-account |5 minutes ago |default |sWRt5J1LPYU9nQYg |drink="water" | +---------------------+---------------------+--------------+--------+-----------------+--------------+ ``` *** #### serviceaccount remove-key Remove a key from the reference service account **Usage** ```none theme={null} cpln serviceaccount remove-key --key KEY_TO_REMOVE [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --key Name of the key to remove. Can be repeated. [required] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * For the example below, the created service account key is: ``` +---------------------+---------------------+--------+--------+-----+--------------+ |NAME |DESCRIPTION |CREATED |ORIGIN |KEYS |TAGS | |---------------------|---------------------|--------|--------|-----|--------------| |demo-service-account |demo-service-account |Now |default | |drink="water" | +---------------------+---------------------+--------+--------+-----+--------------+ ``` * Remove key ```bash Command theme={null} cpln serviceaccount remove-key demo-service-account --key sRPtzpsUjJcFuWzA --org demo-cpln-organization ``` ```text Output theme={null} +---------------------+---------------------+--------------+--------+-----------------+--------------+ |NAME |DESCRIPTION |CREATED |ORIGIN |KEYS |TAGS | |---------------------|---------------------|--------------|--------|-----------------|--------------| |demo-service-account |demo-service-account |5 minutes ago |default |sWRt5J1LPYU9nQYg |drink="water" | +---------------------+---------------------+--------------+--------+-----------------+--------------+ ``` *** #### serviceaccount tag Manage the tags belonging to one or more referenced service accounts **Usage** ```none theme={null} cpln serviceaccount tag --tag KEY=VALUE [OPTIONS] ``` Built-in tags, which begin with cpln/\*, can't be modified. ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [required] [default: []] Command options: --tag Attach tags (e.g., --tag drink=water) --remove-tag Remove tags (e.g., --remove-tag tagname) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Add a tag to a service account ```bash Command theme={null} cpln serviceaccount tag demo-service-account-tag-1 --tag drink=water --org demo-cpln-organization ``` ```text Output theme={null} +---------------------------+---------------------------+--------+--------+-----+--------------+ |NAME |DESCRIPTION |CREATED |ORIGIN |KEYS |TAGS | |---------------------------|---------------------------|--------|--------|-----|--------------| |demo-service-account-tag-1 |demo-service-account-tag-1 |Now |default | |drink="water" | +---------------------------+---------------------------+--------+--------+-----+--------------+ ``` * Update same tag (after running the example above) ```bash Command theme={null} cpln serviceaccount tag demo-service-account-tag-1 --tag drink=coffee --org demo-cpln-organization ``` ```text Output theme={null} +---------------------------+---------------------------+--------+--------+-----+---------------+ |NAME |DESCRIPTION |CREATED |ORIGIN |KEYS |TAGS | |---------------------------|---------------------------|--------|--------|-----|---------------| |demo-service-account-tag-1 |demo-service-account-tag-1 |Now |default | |drink="coffee" | +---------------------------+---------------------------+--------+--------+-----+---------------+ ``` * Remove tag (after running the example above) ```bash Command theme={null} cpln serviceaccount tag demo-service-account-tag-1 --remove drink --org demo-cpln-organization ``` ```text Output theme={null} +---------------------------+---------------------------+--------+--------+-----+-----+ |NAME |DESCRIPTION |CREATED |ORIGIN |KEYS |TAGS | |---------------------------|---------------------------|--------|--------|-----|-----| |demo-service-account-tag-1 |demo-service-account-tag-1 |Now |default | | | +---------------------------+---------------------------+--------+--------+-----+-----+ ``` * Add same tag to multiple service accounts ```bash Command theme={null} cpln serviceaccount tag demo-service-account-tag-1 demo-service-account-tag-2 --tag multiple-serviceaccounts=true --org demo-cpln-organization ``` ```text Output theme={null} +---------------------------+---------------------------+--------+--------+-----+--------------------------------+ |NAME |DESCRIPTION |CREATED |ORIGIN |KEYS |TAGS | |---------------------------|---------------------------|--------|--------|-----|--------------------------------| |demo-service-account-tag-1 |demo-service-account-tag-1 |Now |default | |multiple-serviceaccounts="true" | |demo-service-account-tag-2 |demo-service-account-tag-2 |Now |default | |multiple-serviceaccounts="true" | +---------------------------+---------------------------+--------+--------+-----+--------------------------------+ ``` * Update same tag on multiple service accounts (after running the example above) ```bash Command theme={null} cpln serviceaccount tag demo-service-account-tag-1 demo-service-account-tag-2 --tag multiple-serviceaccounts=demo-serviceaccount --org demo-cpln-organization ``` ```text Output theme={null} +---------------------------+---------------------------+--------+--------+-----+-----------------------------------------------+ |NAME |DESCRIPTION |CREATED |ORIGIN |KEYS |TAGS | |---------------------------|---------------------------|--------|--------|-----|-----------------------------------------------| |demo-service-account-tag-1 |demo-service-account-tag-1 |Now |default | |multiple-serviceaccounts="demo-serviceaccount" | |demo-service-account-tag-2 |demo-service-account-tag-2 |Now |default | |multiple-serviceaccounts="demo-serviceaccount" | +---------------------------+---------------------------+--------+--------+-----+-----------------------------------------------+ ``` * Remove same tag from multiple service accounts (after running the example above) ```bash Command theme={null} cpln serviceaccount tag demo-service-account-tag-1 demo-service-account-tag-2 --remove multiple-serviceaccounts --org demo-cpln-organization ``` ```text Output theme={null} +---------------------------+---------------------------+--------+--------+-----+-----+ |NAME |DESCRIPTION |CREATED |ORIGIN |KEYS |TAGS | |---------------------------|---------------------------|--------|--------|-----|-----| |demo-service-account-tag-1 |demo-service-account-tag-1 |Now |default | | | |demo-service-account-tag-2 |demo-service-account-tag-2 |Now |default | | | +---------------------------+---------------------------+--------+--------+-----+-----+ ``` *** # stack Source: https://docs.controlplane.com/cli-reference/commands/stack cpln stack — Manage docker-compose files and deploy multi-service stacks to Control Plane. manage docker-compose files Refer to the [Compose Deploy guide](/guides/compose-deploy) for additional details. ```none theme={null} cpln stack [SUBCOMMAND] ``` ```text theme={null} Commands: cpln stack deploy deploy from a docker-compose file [aliases: up] cpln stack manifest Generate a CPLN apply file from a docker-compose file cpln stack rm Delete objects from a docker-compose file [aliases: down] Options: --help Show help [boolean] ``` *** #### stack deploy deploy from a docker-compose file Refer to the [compose deploy guide](/guides/compose-deploy) for more details. **Usage** ```none theme={null} cpln stack deploy [OPTIONS] ``` ```text theme={null} Command options: --directory, --dir Path to parent folder of docker-compose file --compose-file Name of the docker-compose file if alternative naming was used --build Build images [boolean] [default: true] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Assuming there is a docker compose project in the current directory ```bash Command theme={null} cpln stack deploy ``` ```text Output theme={null} Created /org/demo-cpln-organization/secret/demo-secret Created /org/demo-cpln-organization/gvc/demo-cpln-gvc/identity/demo-identity Created /org/demo-cpln-organization/gvc/demo-cpln-gvc/volumeset/demo-volume Created /org/demo-cpln-organization/gvc/demo-cpln-gvc/workload/demo-workload Created /org/demo-cpln-organization/policy/demo-policy ``` *** #### stack manifest Generate a CPLN spec file from a compose project. Outputs result to stdout. Note: this command will build and push images to CPLN **Usage** ```none theme={null} cpln stack manifest [OPTIONS] ``` ```text theme={null} Command options: --directory, --dir Path to parent folder of docker-compose file --compose-file Name of the docker-compose file if alternative naming was used --build Build images [boolean] [default: false] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Assuming there is a docker compose project in the current directory. ```bash Command theme={null} cpln stack manifest ``` ```yaml Output theme={null} kind: secret name: demo-secret description: demo-secret type: opaque data: encoding: plain payload: demo secret --- kind: identity name: demo-identity description: Identity to allow demo-workload to reveal secret --- kind: volumeset name: demo-volume description: demo-volume spec: fileSystemType: ext4 initialCapacity: 10 performanceClass: general-purpose-ssd --- kind: workload name: demo-workload description: demo-workload spec: type: standard containers: - name: demo-workload-container cpu: 250m memory: 53Mi ports: - number: 3000 image: demo-image:1.0 volumes: - path: /secrets/secret.txt uri: 'cpln://secret/demo-secret' recoveryPolicy: retain defaultOptions: capacityAI: false identityLink: /org/demo-cpln-organization/gvc/demo-cpln-gvc/identity/demo-identity firewallConfig: external: inboundAllowCIDR: - 0.0.0.0/0 outboundAllowCIDR: - 0.0.0.0/0 --- kind: policy name: demo-policy description: Policy for workloads to reveal demo-secret bindings: - permissions: - reveal principalLinks: - /org/demo-cpln-organization/gvc/demo-cpln-gvc/identity/demo-identity targetKind: secret targetLinks: - /org/demo-cpln-organization/secret/demo-secret ``` *** #### stack rm Delete all resources generated from a docker compose project. **Usage** ```none theme={null} cpln stack rm [OPTIONS] ``` ```text theme={null} Command options: --directory, --dir Path to parent folder of docker-compose file --compose-file Name of the docker-compose file if alternative naming was used Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Assuming there is a docker compose project in the current directory ```bash Command theme={null} cpln stack rm ``` ```text Output theme={null} Deleted /org/demo-cpln-organization/secret/demo-secret Deleted /org/demo-cpln-organization/gvc/demo-cpln-gvc/identity/demo-identity Deleted /org/demo-cpln-organization/gvc/demo-cpln-gvc/volumeset/demo-volume Deleted /org/demo-cpln-organization/gvc/demo-cpln-gvc/workload/demo-workload Deleted /org/demo-cpln-organization/policy/demo-policy ``` *** # task Source: https://docs.controlplane.com/cli-reference/commands/task cpln task — Manage pending tasks that require approval or manual intervention. Manage pending tasks ```none theme={null} cpln task [SUBCOMMAND] ``` ```text theme={null} Commands: cpln task complete Complete the referenced task cpln task delete Delete one or more referenced tasks cpln task get [ref...] Retrieve one or more referenced tasks cpln task get-mine [ref...] Retrieve one or more referenced tasks for the current or overridden profile [aliases: mine] cpln task permissions Show the grantable permissions for a task object type cpln task query Find all the tasks based on the given query Options: --help Show help [boolean] ``` *** #### task complete Complete the referenced task **Usage** ```none theme={null} cpln task complete --answer {accept|reject} [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --answer Accept or reject referenced task [required] [choices: "accept", "reject"] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### task delete Delete one or more referenced tasks **Usage** ```none theme={null} cpln task delete [OPTIONS] ``` ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [required] [default: []] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### task get Retrieve one or more referenced tasks **Usage** ```none theme={null} cpln task get [ref...] [OPTIONS] ``` An empty reference will return all references ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [default: []] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### task get-mine Retrieve one or more referenced tasks for the current or overridden profile **Usage** ```none theme={null} cpln task get-mine [ref...] [OPTIONS] ``` An empty reference will return all tasks for the current or overridden profile ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [default: []] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### task permissions Show the grantable permissions for a task object type **Usage** ```none theme={null} cpln task permissions [OPTIONS] ``` ```text theme={null} Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln task permissions --org demo-cpln-organization ``` ```text Output theme={null} +-------+-----------------------------+---------------------------+ |NAME |DESCRIPTION |IMPLIES | |-------|-----------------------------|---------------------------| |delete |Delete/Cancel existing tasks | | |edit |Modify existing tasks |view | |manage |Full access |delete, edit, manage, view | |view |Read-only access | | +-------+-----------------------------+---------------------------+ ``` *** #### task query Find all the tasks based on the given query **Usage** ```none theme={null} cpln task query QUERY_OPTIONS [OPTIONS] ``` ```text theme={null} Query options: --match Query filter type [choices: "all", "none", "any"] [default: "all"] --property, --prop Property to query --tag Tag to query --rel Relation to query Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** # upgrade Source: https://docs.controlplane.com/cli-reference/commands/upgrade cpln upgrade — Update the cpln CLI to the latest version in place. Upgrade the cpln CLI to the latest version **Usage** ```none theme={null} cpln upgrade [OPTIONS] ``` `cpln update` is an alias for `cpln upgrade`. The CLI checks npm for a newer release at most once a day and prints a notice when one is available. Set the `CPLN_SKIP_UPDATE_CHECK` environment variable to any non-empty value to skip that check and its notice; `cpln upgrade --check` still reports the installed and latest versions on demand. ```text theme={null} Upgrade options: --check, -c Report the installed and latest versions without upgrading [boolean] --force Upgrade even when already on the latest version [boolean] Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] --version, -v Show version number [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Check for a newer version without upgrading ```bash Command theme={null} cpln upgrade --check ``` ```text Output theme={null} Installed version: 3.13.0 Latest version: 3.14.0 Install method: npm A newer version is available: 3.14.0. Run 'cpln upgrade' to install it. ``` * Upgrade in place ```bash Command theme={null} cpln upgrade ``` ```text Output theme={null} ✓ cpln upgrade completed. ``` *** # user Source: https://docs.controlplane.com/cli-reference/commands/user cpln user — Manage users and their access within your organization. Manage users For a detailed overview, please visit the [user's](/reference/user) reference page. ```none theme={null} cpln user [SUBCOMMAND] ``` ```text theme={null} Commands: cpln user access-report Show the access report for the referenced user cpln user audit [ref] Retrieve audit trail events for the referenced user cpln user delete Delete one or more referenced users cpln user edit Edit the referenced user, as YAML, within an editor cpln user eventlog Show the event log of the referenced user [aliases: log] cpln user get [ref...] Retrieve one or more referenced users cpln user invite Invite users to the current or overridden organization cpln user patch Update the referenced user's metadata using an input file cpln user permissions Show the grantable permissions for a user object type cpln user query Find all the users based on the given query cpln user tag Manage the tags belonging to one or more referenced users cpln user update Update properties of the referenced user Options: --help Show help [boolean] ``` *** #### user access-report Show the access report for the referenced user **Usage** ```none theme={null} cpln user access-report [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln user access-report test-128@example.com --org demo-cpln-organization ``` ```text Output theme={null} +------------+--------------------------------------------------------+--------+ |PERMISSION |PRINCIPAL |GRANTED | |------------|--------------------------------------------------------|--------| |delete |/org/demo-cpln-organization/group/superusers |manage | |delete |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |edit |/org/demo-cpln-organization/group/superusers |manage | |edit |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |impersonate |/org/demo-cpln-organization/group/superusers |manage | |impersonate |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |invite |/org/demo-cpln-organization/group/superusers |manage | |invite |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |manage |/org/demo-cpln-organization/group/superusers |manage | |manage |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |view |/org/demo-cpln-organization/group/viewers |view | |view |/org/demo-cpln-organization/group/superusers |manage | |view |/org/demo-cpln-organization/serviceaccount/controlplane |manage | +------------+--------------------------------------------------------+--------+ ``` *** #### user audit Retrieve audit trail events for the referenced user **Usage** ```none theme={null} cpln user audit [ref] [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. Command options: --subject Filter by subject (serviceaccount name or user email) [string] --context Audit context name (the 'cpln' context displays Control Plane actions) [string] [default: "cpln"] --since Relative lookback window from now (e.g., 1h, 24h, 7d). Mutually exclusive with --from/--to [string] [default: "7d"] --from Start time for audit events (ISO 8601 format or relative duration, e.g., 2025-10-23T07:00:00Z, 7d, 1h). Cannot be used with --since [string] --to End time for audit events (ISO 8601 format or relative duration, e.g., 2025-10-23T07:00:00Z, 1d, 30m) [string] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### user delete Delete one or more referenced users **Usage** ```none theme={null} cpln user delete [OPTIONS] ``` Deleting a user will remove all associated resources (permissions, group memberships, etc.) ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [required] [default: []] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln user delete demo-user@controlplane.com --org demo-cpln-organization ``` ```text Output theme={null} If the delete was successful, a new command prompt will be shown. ``` *** #### user edit Edit the referenced user, as YAML, within an editor **Usage** ```none theme={null} cpln user edit [OPTIONS] ``` The default editor can be set by using the EDITOR environment variable. ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --replace, -r Replace instead of patch/merge [boolean] [default: false] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln user edit test-128@example.com --org demo-cpln-organization ``` ```text Output theme={null} Your default editor will be launched. Once any modifications are saved, the updates will be sent to the API. ``` *** #### user eventlog Show the event log of the referenced user **Usage** ```none theme={null} cpln user eventlog [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln user eventlog test-128@example.com --org demo-cpln-organization ``` ```text Output theme={null} The event log for the referenced user will be shown. ``` *** #### user get Retrieve one or more referenced users **Usage** ```none theme={null} cpln user get [ref...] [OPTIONS] ``` An empty reference will return all references ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [default: []] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Get all users ```bash Command theme={null} cpln user get --org demo-cpln-organization ``` ```text Output theme={null} +-----------------------------+---------------------+-----------------------+-------------+---------+------------------------------------------+ |ID |FULL NAME |EMAIL |CREATED |IDP |TAGS | |-----------------------------|---------------------|-----------------------|-------------|---------|------------------------------------------| |rMdL2BFDxEeYQPA5qRYGbLivhVS2 |Kyle Cupp |kyle@controlplane.com |2 years ago |firebase |firebase/sign_in_provider="microsoft.com" | |3mpcnkfyNhYXdjq8TbLefzXwyfu1 |Majid Abu Rmelah |majid@controlplane.com |7 months ago |firebase |firebase/sign_in_provider="microsoft.com" | |HkKCu6TcHIMB8ON8iXayuKw5oop2 |test-128@example.com |test-128@example.com |5 years ago |firebase |firebase/sign_in_provider="password" | +-----------------------------+---------------------+-----------------------+-------------+---------+------------------------------------------+ ``` * Get one user ```bash Command theme={null} cpln user get test-128@example.com --org demo-cpln-organization ``` ```text Output theme={null} +-----------------------------+---------------------+---------------------+------------+---------+-------------------------------------+ |ID |FULL NAME |EMAIL |CREATED |IDP |TAGS | |-----------------------------|---------------------|---------------------|------------|---------|-------------------------------------| |HkKCu6TcHIMB8ON8iXayuKw5oop2 |test-128@example.com |test-128@example.com |5 years ago |firebase |firebase/sign_in_provider="password" | +-----------------------------+---------------------+---------------------+------------+---------+-------------------------------------+ ``` *** #### user invite Invite users to the current or overridden organization **Usage** ```none theme={null} cpln user invite --email EMAIL_ADDRESS [--group GROUP_NAME] [OPTIONS] ``` ```text theme={null} Command options: --email Email address of the user to invite [required] --group Primary group to add the user to after they accept an invitation Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln user invite --email demo@controlplane.com --org demo-cpln-organization ``` ```text Output theme={null} If the invite request was successful, a JSON output with the task ID will be shown. ``` *** #### user patch Update the referenced user's metadata using an input file **Usage** ```none theme={null} cpln user patch --file FILE_NAME [OPTIONS] ``` Running this command with the option **`--file -`** sets the input to stdin. The input is required to be a valid JSON or YAML string (e.g., `{"description": "My new description"}` or `description: My new description`). To complete the update, press Control-D (or Control-Z if you are using Windows). ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --file, -f File to load the patch from. Use `--file -` to enable input from stdin. [required] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln user patch test-128@example.com --file update.json --org demo-cpln-organization ``` ```text Output theme={null} The changes within the JSON file will be performed against the referenced user. ``` *** #### user permissions Show the grantable permissions for a user object type **Usage** ```none theme={null} cpln user permissions [OPTIONS] ``` ```text theme={null} Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln user permissions --org demo-cpln-organization ``` ```text Output theme={null} +------------+-----------------------------+------------------------------------------------+ |NAME |DESCRIPTION |IMPLIES | |------------|-----------------------------|------------------------------------------------| |delete |Delete existing users | | |edit |Modify existing users |view | |impersonate |Lets you impersonate a user | | |invite |Can invite users to this org | | |manage |Full access |delete, edit, impersonate, invite, manage, view | |view |Read-only access | | +------------+-----------------------------+------------------------------------------------+ ``` *** #### user query Find all the users based on the given query **Usage** ```none theme={null} cpln user query QUERY_OPTIONS [OPTIONS] ``` ```text theme={null} Query options: --match Query filter type [choices: "all", "none", "any"] [default: "all"] --property, --prop Property to query --tag Tag to query --rel Relation to query Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) Use the `--match` option to display **all**, **none**, or **any** of the results based on a given property or tag * Search for a user based on a property ```bash Command theme={null} cpln user query --match any --prop email=test-128@example.com --org demo-cpln-organization ``` ```text Output theme={null} +-----------------------------+---------------------+---------------------+------------+---------+-------------------------------------+ |ID |FULL NAME |EMAIL |CREATED |IDP |TAGS | |-----------------------------|---------------------|---------------------|------------|---------|-------------------------------------| |HkKCu6TcHIMB8ON8iXayuKw5oop2 |test-128@example.com |test-128@example.com |5 years ago |firebase |firebase/sign_in_provider="password" | +-----------------------------+---------------------+---------------------+------------+---------+-------------------------------------+ ``` * Search for a user based on a tag ```bash Command theme={null} cpln user query --match any --tag firebase/sign_in_provider="password" --org demo-cpln-organization ``` ```text Output theme={null} +-----------------------------+---------------------+---------------------+------------+---------+-------------------------------------+ |ID |FULL NAME |EMAIL |CREATED |IDP |TAGS | |-----------------------------|---------------------|---------------------|------------|---------|-------------------------------------| |HkKCu6TcHIMB8ON8iXayuKw5oop2 |test-128@example.com |test-128@example.com |5 years ago |firebase |firebase/sign_in_provider="password" | +-----------------------------+---------------------+---------------------+------------+---------+-------------------------------------+ ``` * Search for a user based on a property and tag ```bash Command theme={null} cpln user query --match any --prop email=test-128@example.com --tag firebase/sign_in_provider="password" --org demo-cpln-organization ``` ```text Output theme={null} +-----------------------------+---------------------+---------------------+------------+---------+-------------------------------------+ |ID |FULL NAME |EMAIL |CREATED |IDP |TAGS | |-----------------------------|---------------------|---------------------|------------|---------|-------------------------------------| |HkKCu6TcHIMB8ON8iXayuKw5oop2 |test-128@example.com |test-128@example.com |5 years ago |firebase |firebase/sign_in_provider="password" | +-----------------------------+---------------------+---------------------+------------+---------+-------------------------------------+ ``` *** #### user tag Manage the tags belonging to one or more referenced users **Usage** ```none theme={null} cpln user tag --tag KEY=VALUE [OPTIONS] ``` Built-in tags, which begin with cpln/\*, can't be modified. ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [required] [default: []] Command options: --tag Attach tags (e.g., --tag drink=water) --remove-tag Remove tags (e.g., --remove-tag tagname) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Add a tag ```bash Command theme={null} cpln user tag test-128@example.com --tag drink=water --org demo-cpln-organization ``` ```text Output theme={null} +-----------------------------+---------------------+---------------------+------------+---------+-------------------------------------+ |ID |FULL NAME |EMAIL |CREATED |IDP |TAGS | |-----------------------------|---------------------|---------------------|------------|---------|-------------------------------------| |HkKCu6TcHIMB8ON8iXayuKw5oop2 |test-128@example.com |test-128@example.com |5 years ago |firebase |drink="water" | | | | | | |firebase/sign_in_provider="password" | +-----------------------------+---------------------+---------------------+------------+---------+-------------------------------------+ ``` * Update same tag (after running the example above) ```bash Command theme={null} cpln user tag test-128@example.com --tag drink=coffee --org demo-cpln-organization ``` ```text Output theme={null} +-----------------------------+---------------------+---------------------+------------+---------+-------------------------------------+ |ID |FULL NAME |EMAIL |CREATED |IDP |TAGS | |-----------------------------|---------------------|---------------------|------------|---------|-------------------------------------| |HkKCu6TcHIMB8ON8iXayuKw5oop2 |test-128@example.com |test-128@example.com |5 years ago |firebase |drink="coffee" | | | | | | |firebase/sign_in_provider="password" | +-----------------------------+---------------------+---------------------+------------+---------+-------------------------------------+ ``` * Remove tag (after running the example above) ```bash Command theme={null} cpln user tag test-128@example.com --remove drink --org demo-cpln-organization ``` ```text Output theme={null} +-----------------------------+---------------------+---------------------+------------+---------+-------------------------------------+ |ID |FULL NAME |EMAIL |CREATED |IDP |TAGS | |-----------------------------|---------------------|---------------------|------------|---------|-------------------------------------| |HkKCu6TcHIMB8ON8iXayuKw5oop2 |test-128@example.com |test-128@example.com |5 years ago |firebase |firebase/sign_in_provider="password" | +-----------------------------+---------------------+---------------------+------------+---------+-------------------------------------+ ``` *** #### user update Update properties of the referenced user **Usage** ```none theme={null} cpln user update --set UPDATE_PROPERTY [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Update Properties: --set Update the following properties (e.g., --set description="Updated Description"): tags. string --unset Unset a property (same paths as --set, e.g., --unset description) Command options: --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * The `--set` option allows the modification of the following user properties: * description * Description of the user * Type: string * tags * Tags (key=value) associated with the user * Type: string * For the examples below, the initial user is configured as: ```text theme={null} +-----------------------------+---------------------+---------------------+------------+---------+-------------------------------------+ |ID |FULL NAME |EMAIL |CREATED |IDP |TAGS | |-----------------------------|---------------------|---------------------|------------|---------|-------------------------------------| |HkKCu6TcHIMB8ON8iXayuKw5oop2 |test-128@example.com |test-128@example.com |5 years ago |firebase |firebase/sign_in_provider="password" | +-----------------------------+---------------------+---------------------+------------+---------+-------------------------------------+ ``` * Update a user's tag ```bash Command theme={null} cpln user update test-128@example.com --set tags.user-name="test-128" --org demo-cpln-organization ``` ```text Output theme={null} +-----------------------------+---------------------+---------------------+------------+---------+-------------------------------------+ |ID |FULL NAME |EMAIL |CREATED |IDP |TAGS | |-----------------------------|---------------------|---------------------|------------|---------|-------------------------------------| |HkKCu6TcHIMB8ON8iXayuKw5oop2 |test-128@example.com |test-128@example.com |5 years ago |firebase |firebase/sign_in_provider="password" | | | | | | |user-name="test-128" | +-----------------------------+---------------------+---------------------+------------+---------+-------------------------------------+ ``` *** # version Source: https://docs.controlplane.com/cli-reference/commands/version cpln version — Print the installed cpln version and build information. Print the installed cpln version **Usage** ```none theme={null} cpln version [OPTIONS] ``` `cpln version` and its short form `cpln -v` print the same information as `cpln --version`. ```text theme={null} Options: --help Show help [boolean] --version, -v Show version number [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln version ``` ```text Output theme={null} { "npm": "3.14.0", "build": "3.14.0", "version": "3.14.0", "epoch": 174, "timestamp": 1752710400000 } ``` *** # volumeset Source: https://docs.controlplane.com/cli-reference/commands/volumeset cpln volumeset — Manage persistent volume sets within a GVC. Supports traditional filesystems (ext4, xfs) for stateful workloads and shared filesystems for any workload type. Manage a persistent volumeset within a global virtual cloud For a detailed overview, please visit the [volumeset's](/reference/volumeset) reference page. ```none theme={null} cpln volumeset [SUBCOMMAND] ``` ```text theme={null} Commands: cpln volumeset access-report Show the access report for the referenced volume set cpln volumeset audit [ref] Retrieve audit trail events for the referenced volume set cpln volumeset create Create a new volume set cpln volumeset delete Delete one or more referenced volume sets cpln volumeset edit Edit the referenced volume set, as YAML, within an editor cpln volumeset eventlog Show the event log of the referenced volume set [aliases: log] cpln volumeset expand Expand the size of one or more volumes in the referenced volume set cpln volumeset get [ref...] Retrieve one or more referenced volume sets cpln volumeset patch Update the referenced volume set's metadata using an input file cpln volumeset permissions Show the grantable permissions for a volume set object type cpln volumeset query Find all the volume sets based on the given query cpln volumeset shrink Shrink the size of one or more volumes in the referenced volume set cpln volumeset snapshot Manage volume set snapshots cpln volumeset tag Manage the tags belonging to one or more referenced volume sets cpln volumeset update Update properties of the referenced volume set cpln volumeset volume Manage volume set volumes Options: --help Show help [boolean] ``` *** #### volumeset access-report Show the access report for the referenced volume set **Usage** ```none theme={null} cpln volumeset access-report [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### volumeset audit Retrieve audit trail events for the referenced volume set **Usage** ```none theme={null} cpln volumeset audit [ref] [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. Command options: --subject Filter by subject (serviceaccount name or user email) [string] --context Audit context name (the 'cpln' context displays Control Plane actions) [string] [default: "cpln"] --since Relative lookback window from now (e.g., 1h, 24h, 7d). Mutually exclusive with --from/--to [string] [default: "7d"] --from Start time for audit events (ISO 8601 format or relative duration, e.g., 2025-10-23T07:00:00Z, 7d, 1h). Cannot be used with --since [string] --to End time for audit events (ISO 8601 format or relative duration, e.g., 2025-10-23T07:00:00Z, 1d, 30m) [string] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### volumeset create Create a new volume set **Usage** ```none theme={null} cpln volumeset create --name NAME [OPTIONS] ``` ```text theme={null} Command options: --name Name of the new volume set [required] --description, --desc Optional description, defaults to the name if not set --performance-class Performance class of the volume set [choices: "general-purpose-ssd", "high-throughput-ssd", "shared"] [default: "general-purpose-ssd"] --file-system-type File system [choices: "xfs", "ext4", "shared"] [default: "xfs"] --initial-capacity Initial capacity in GB [number] [default: 10] --enable-autoscaling Enable Autoscaling [boolean] [default: false] --max-capacity Max capacity in GB, autoscaling needs to be enabled: --enable-autoscaling [number] [default: 10] --min-free-percentage Min free percentage, autoscaling needs to be enabled: --enable-autoscaling [number] [default: 1] --scaling-factor Scaling factor, autoscaling needs to be enabled: --enable-autoscaling [number] [default: 1.1] --enable-predictive-autoscaling Enable predictive scaling based on historical growth rates. Default: false. Autoscaling needs to be enabled: --enable-autoscaling [boolean] [default: false] --predictive-lookback-hours Hours of historical data to analyze. Default: 24. Max: 168 (1 week). Predictive autoscaling needs to be enabled: --enable-predictive-autoscaling [number] [default: 24] --predictive-projection-hours Hours into the future to project storage needs. Default: 6. Predictive autoscaling needs to be enabled: --enable-predictive-autoscaling [number] [default: 6] --predictive-min-data-points Minimum data points required for reliable growth rate calculation. Default: 10. Predictive autoscaling needs to be enabled: --enable-predictive-autoscaling [number] [default: 10] --predictive-min-growth-rate-gb-per-hour Minimum growth rate (GB/hour) to trigger predictive expansion. Default: 0.01. Predictive autoscaling needs to be enabled: --enable-predictive-autoscaling [number] [default: 0.01] --predictive-scaling-factor Scaling factor for predictive expansion. If not set, uses the parent autoscaling.scalingFactor. Use a lower value (e.g., 1.2) for gentler proactive scaling. Predictive autoscaling needs to be enabled: --enable-predictive-autoscaling [number] --create-final-snapshot Create Final Snapshot [boolean] [default: true] --retention-duration Retention Duration [default: "7d"] --schedule Snapshot Schedule (UTC) --tag Attach tags (e.g., --tag drink=water) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### volumeset delete Delete one or more referenced volume sets **Usage** ```none theme={null} cpln volumeset delete [OPTIONS] ``` ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [required] [default: []] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### volumeset edit Edit the referenced volume set, as YAML, within an editor **Usage** ```none theme={null} cpln volumeset edit [OPTIONS] ``` The default editor can be set by using the EDITOR environment variable. ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --replace, -r Replace instead of patch/merge [boolean] [default: false] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### volumeset eventlog Show the event log of the referenced volume set **Usage** ```none theme={null} cpln volumeset eventlog [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### volumeset expand Expand the size of one or more volumes in the referenced volume set **Usage** ```none theme={null} cpln volumeset expand [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --new-size The new storage capacity of the volume in GiB [number] [required] --timeout-seconds The maximum amount of time in seconds to wait for the volume to be expanded [number] [default: 600] --location A global virtual cloud location [string] --volume-index The index of the volume of which a snapshot should be taken [number] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### volumeset get Retrieve one or more referenced volume sets **Usage** ```none theme={null} cpln volumeset get [ref...] [OPTIONS] ``` An empty reference will return all references ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [default: []] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Command options: --all-gvcs Show volume sets from all the global virtual clouds within the current or overridden organization [boolean] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### volumeset patch Update the referenced volume set's metadata using an input file **Usage** ```none theme={null} cpln volumeset patch --file FILE_NAME [OPTIONS] ``` Running this command with the option **`--file -`** sets the input to stdin. The input is required to be a valid JSON or YAML string (e.g., `{"description": "My new description"}` or `description: My new description`). To complete the update, press Control-D (or Control-Z if you are using Windows). ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --file, -f File to load the patch from. Use `--file -` to enable input from stdin. [required] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### volumeset permissions Show the grantable permissions for a volume set object type **Usage** ```none theme={null} cpln volumeset permissions [OPTIONS] ``` ```text theme={null} Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### volumeset query Find all the volume sets based on the given query **Usage** ```none theme={null} cpln volumeset query QUERY_OPTIONS [OPTIONS] ``` ```text theme={null} Query options: --match Query filter type [choices: "all", "none", "any"] [default: "all"] --property, --prop Property to query --tag Tag to query --rel Relation to query Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### volumeset shrink Shrink the size of one or more volumes in the referenced volume set **Usage** ```none theme={null} cpln volumeset shrink [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --new-size The new storage capacity of the volume in GiB [number] [required] --timeout-seconds The maximum amount of time in seconds to wait for the volume to be expanded [number] [default: 600] --location A global virtual cloud location [string] --volume-index The index of the volume of which a snapshot should be taken [number] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### volumeset snapshot Manage volume set snapshots ```none theme={null} cpln volumeset snapshot [SUBCOMMAND] ``` ```text theme={null} Commands: cpln volumeset snapshot create Create one or more snapshots by a volume set reference cpln volumeset snapshot delete Delete one or more snapshots by a volume set reference cpln volumeset snapshot get Retrieve one or more snapshots by a volume set reference cpln volumeset snapshot restore Restore a snapshot to a volume Options: --help Show help [boolean] ``` #### volumeset snapshot create Create one or more snapshots by a volume set reference **Usage** ```none theme={null} cpln volumeset snapshot create [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --snapshot-name Name of the snapshot [string] [required] --location A global virtual cloud location [string] --volume-index The index of the volume of which a snapshot should be taken [number] --tag Attach tags (e.g., --tag drink=water) Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### volumeset snapshot delete Delete one or more snapshots by a volume set reference **Usage** ```none theme={null} cpln volumeset snapshot delete [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --snapshot-name Name of the snapshot [string] [required] --location A global virtual cloud location [string] --volume-index The index of the volume of which a snapshot should be taken [number] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### volumeset snapshot get Retrieve one or more snapshots by a volume set reference **Usage** ```none theme={null} cpln volumeset snapshot get [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --snapshot-name Name of the snapshot [string] --location A global virtual cloud location [string] --volume-index The index of the volume of which a snapshot should be taken [number] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### volumeset snapshot restore Restore a snapshot to a volume **Usage** ```none theme={null} cpln volumeset snapshot restore [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --snapshot-name Name of the snapshot [string] [required] --location A global virtual cloud location [string] [required] --volume-index The index of the volume of which a snapshot should be taken [number] [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### volumeset tag Manage the tags belonging to one or more referenced volume sets **Usage** ```none theme={null} cpln volumeset tag --tag KEY=VALUE [OPTIONS] ``` Built-in tags, which begin with cpln/\*, can't be modified. ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [required] [default: []] Command options: --tag Attach tags (e.g., --tag drink=water) --remove-tag Remove tags (e.g., --remove-tag tagname) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### volumeset update Update properties of the referenced volume set **Usage** ```none theme={null} cpln volumeset update --set UPDATE_PROPERTY [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Update Properties: --set Update the following properties (e.g., --set description="Updated Description"): description string tags. string spec.initialCapacity number spec.snapshots.createFinalSnapshot boolean spec.snapshots.retentionDuration string spec.snapshots.schedule string spec.autoscaling.maxCapacity number spec.autoscaling.minFreePercentage number spec.autoscaling.scalingFactor number spec.autoscaling.predictive.enabled boolean spec.autoscaling.predictive.lookbackHours number spec.autoscaling.predictive.projectionHours number spec.autoscaling.predictive.minDataPoints number spec.autoscaling.predictive.minGrowthRateGBPerHour number spec.autoscaling.predictive.scalingFactor number --unset Unset a property (same paths as --set, e.g., --unset description) Command options: --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### volumeset volume Manage volume set volumes ```none theme={null} cpln volumeset volume [SUBCOMMAND] ``` ```text theme={null} Commands: cpln volumeset volume delete Delete one or more volumes by a volume set reference cpln volumeset volume get Retrieve one or more volumes by a volume set reference Options: --help Show help [boolean] ``` #### volumeset volume delete Delete one or more volumes by a volume set reference **Usage** ```none theme={null} cpln volumeset volume delete [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --location A global virtual cloud location [string] --volume-index The index of the volume of which a snapshot should be taken [number] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln volumeset volume delete demo-volume-set --location aws-eu-central-1 --volume-index 0 ``` ```text Output theme={null} +-----------------+-------------+----------+ |LOCATION |VOLUME INDEX |LIFECYCLE | |-----------------|-------------|----------| |aws-eu-central-1 |0 |Deleting | +-----------------+-------------+----------+ ``` *** #### volumeset volume get Retrieve one or more volumes by a volume set reference **Usage** ```none theme={null} cpln volumeset volume get [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --location A global virtual cloud location [string] --volume-index The index of the volume of which a snapshot should be taken [number] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln volumeset volume get demo-volume-set ``` ```text Output theme={null} +-----------------+-------------+----------+ |LOCATION |VOLUME INDEX |LIFECYCLE | |-----------------|-------------|----------| |aws-eu-central-1 |0 |Bound | +-----------------+-------------+----------+ ``` *** # workload Source: https://docs.controlplane.com/cli-reference/commands/workload cpln workload — Manage workloads that run containerized applications within a GVC. Manage workloads within a global virtual cloud For a detailed overview, please visit the [workload's](/reference/workload) reference page. ```none theme={null} cpln workload [SUBCOMMAND] ``` ```text theme={null} Commands: cpln workload access-report Show the access report for the referenced workload cpln workload audit [ref] Retrieve audit trail events for the referenced workload cpln workload clone Create a clone of the referenced workload; this will only duplicate its spec. [aliases: copy] cpln workload connect Connect to a replica of the workload cpln workload create Create a new workload cpln workload cron Manage cron workloads cpln workload delete Delete one or more referenced workloads cpln workload edit Edit the referenced workload, as YAML, within an editor cpln workload eventlog Show the event log of the referenced workload [aliases: log] cpln workload exec Exec a command on a replica of the workload cpln workload force-redeployment Force redeployment of the workload(s) cpln workload get [ref...] Retrieve one or more referenced workloads cpln workload get-deployments Retrieve the current deployments of the referenced workload cpln workload get-replicas Get the replicas of the referenced workload in a given location [deprecated: This subcommand is deprecated, use 'replica get' instead.] cpln workload open Open the referenced workload's endpoint in your browser cpln workload patch Update the referenced workload's metadata using an input file cpln workload permissions Show the grantable permissions for a workload object type cpln workload query Find all the workloads based on the given query cpln workload replica Manage workload replicas cpln workload run Run a command with a workload instance cpln workload start Start the workload(s) cpln workload stop Stop the workload(s) cpln workload tag Manage the tags belonging to one or more referenced workloads cpln workload update Update properties of the referenced workload Options: --help Show help [boolean] ``` *** #### workload access-report Show the access report for the referenced workload **Usage** ```none theme={null} cpln workload access-report [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Display the user / service account / group permissions for the 'demo-workload' workload withing the 'demo-cpln-gvc' global virtual cloud within the 'demo-cpln-organization' organization ```bash Command theme={null} cpln workload access-report demo-workload --org demo-cpln-organization --gvc demo-gvc-workload ``` ```text Output theme={null} +----------------------+--------------------------------------------------------+--------+ |PERMISSION |PRINCIPAL |GRANTED | |----------------------|--------------------------------------------------------|--------| |configureLoadBalancer |/org/demo-cpln-organization/group/superusers |manage | |configureLoadBalancer |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |connect |/org/demo-cpln-organization/group/superusers |manage | |connect |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |create |/org/demo-cpln-organization/group/superusers |manage | |create |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |delete |/org/demo-cpln-organization/group/superusers |manage | |delete |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |edit |/org/demo-cpln-organization/group/superusers |manage | |edit |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |exec |/org/demo-cpln-organization/group/superusers |manage | |exec |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |exec.runCronWorkload |/org/demo-cpln-organization/group/superusers |manage | |exec.runCronWorkload |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |exec.stopReplica |/org/demo-cpln-organization/group/superusers |manage | |exec.stopReplica |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |manage |/org/demo-cpln-organization/group/superusers |manage | |manage |/org/demo-cpln-organization/serviceaccount/controlplane |manage | |view |/org/demo-cpln-organization/group/viewers |view | |view |/org/demo-cpln-organization/group/superusers |manage | |view |/org/demo-cpln-organization/serviceaccount/controlplane |manage | +----------------------+--------------------------------------------------------+--------+ ``` *** #### workload audit Retrieve audit trail events for the referenced workload **Usage** ```none theme={null} cpln workload audit [ref] [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. Command options: --subject Filter by subject (serviceaccount name or user email) [string] --context Audit context name (the 'cpln' context displays Control Plane actions) [string] [default: "cpln"] --since Relative lookback window from now (e.g., 1h, 24h, 7d). Mutually exclusive with --from/--to [string] [default: "7d"] --from Start time for audit events (ISO 8601 format or relative duration, e.g., 2025-10-23T07:00:00Z, 7d, 1h). Cannot be used with --since [string] --to End time for audit events (ISO 8601 format or relative duration, e.g., 2025-10-23T07:00:00Z, 1d, 30m) [string] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### workload clone Create a clone of the referenced workload; this will only duplicate its spec. **Usage** ```none theme={null} cpln workload clone --name CLONE_NAME [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --name Set the name for the clone [required] --description Optional description, defaults to the name if not set --tag Attach tags (e.g., --tag drink=water) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln workload clone demo-workload --name demo-workload-clone --org demo-cpln-organization --gvc demo-gvc-workload ``` ```text Output theme={null} +--------------------+-----------------------+------------------+------+--------+-------------------------------------------------------------------+--------------+ |NAME |DESCRIPTION |GVC |READY |CREATED |ENDPOINT |TAGS | |--------------------|-----------------------|------------------|------|--------|-------------------------------------------------------------------|--------------| |demo-workload-clone |Clone of demo-workload |demo-gvc-workload | |Now |https://demo-workload-clone-2x5qqmrp3zzt6.geta3p46gfrsp.t.cpln.app |drink="water" | +--------------------+-----------------------+------------------+------+--------+-------------------------------------------------------------------+--------------+ ``` *** #### workload connect Connect to a replica of the workload If multiple replicas are running, use the --replica flag to specify the replica. Use the subcommand [get-replicas](#workload-get-replicas) to view the current list of replicas. Refer to the [cpln workload connect guide](/guides/cli/workload/connect) for additional details. **Usage** ```none theme={null} cpln workload connect --location LOCATION [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --location A global virtual cloud location [string] --replica Replica of the deployment --container Container name of the workload --shell, -s Shell to open on replica [default: "bash"] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### workload create Create a new workload **Usage** ```none theme={null} cpln workload create --name WORKLOAD_NAME --image IMAGE_NAME [OPTIONS] ``` When creating a new workload and the image was pushed to your org's private repository, use the image name shortcut `//image/IMAGE_NAME`. For Example: `cpln workload create --name WORKLOAD_NAME --image //image/IMAGE_NAME` ```text theme={null} Command options: --name Name of the new workload [required] --description, --desc Optional description, defaults to the name if not set --type Workload type [choices: "serverless", "standard"] [default: "standard"] --image Name of the container image [required] --port Port to expose [number] [default: 8080] --env Environment variables in KEY=VALUE format --public Unconstrained ingress & egress for the workload [boolean] --identity Attach the named identity to the workload spec --enable-debug Enables debug response headers when the headers "x-cpln-debug: true" is in the request. [boolean] [default: false] --inherit-env Inherits the environment variables set at GVC level. [boolean] [default: false] --container-name Name of the container item --cpu Allocate CPU resources [default: "50m"] --memory, --mem Allocate Memory [default: "128Mi"] --volume Mount Object Store (S3, GCS, AzureBlob) buckets as file system. E.g. s3://backups@/mnt/storage --tag Attach tags (e.g., --tag drink=water) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * `name` * Type: string * Minimum Length: 3 * Maximum Length: 64 * Only lowercase characters * Must begin with a character * Allowed special characters: - * `image` * Type: string * `port` * Type: number * Default: 8080 * Minimum Value: 80 * Maximum Value: 65535 * `cpu` * Type: string * Default: 150m * `memory` * Type: string * Default: 128Mi **NOTE:** The ratio between CPU to Memory can be at most 1/8. ```bash Command theme={null} cpln workload create --name demo-workload --image gcr.io/knative-samples/helloworld-go --desc demo-workload --tag drink=water --org demo-cpln-organization --gvc demo-gvc-workload ``` ```text Output theme={null} +--------------+--------------+------------------+------+--------+-------------------------------------------------------------+--------------+ |NAME |DESCRIPTION |GVC |READY |CREATED |ENDPOINT |TAGS | |--------------|--------------|------------------|------|--------|-------------------------------------------------------------|--------------| |demo-workload |demo-workload |demo-gvc-workload | |Now |https://demo-workload-2x5qqmrp3zzt6.geta3p46gfrsp.t.cpln.app |drink="water" | +--------------+--------------+------------------+------+--------+-------------------------------------------------------------+--------------+ ``` *** #### workload cron Manage cron workloads ```none theme={null} cpln workload cron [SUBCOMMAND] ``` ```text theme={null} Commands: cpln workload cron get Get a list of job executions for the referenced workload cpln workload cron run Run a command with a cron workload instance. Use -- at the end to specify the command (e.g., cpln workload cron run --gvc my-gvc -- echo hello) cpln workload cron start Start a cron job cpln workload cron stop Stop a cron job Options: --help Show help [boolean] ``` #### workload cron get Get a list of job executions for the referenced workload **Usage** ```none theme={null} cpln workload cron get [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --location A global virtual cloud location [string] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln workload cron get demo-cron-workload ``` ```text Output theme={null} +-------------------+-----------------+-------------------------------------+-------+----------------------+----------------------+ |WORKLOAD |LOCATION |NAME OR COMMAND ID |STATUS |START TIME |COMPLETION TIME | |-------------------|-----------------|-------------------------------------|-------|----------------------|----------------------| |demo-cron-workload │aws-eu-central-1 |ce770d2b-2b7f-4415-b41e-961f161bcd86 |Active |3/30/2024, 3:29:47 AM |3/30/2024, 3:30:14 AM | +-------------------+-----------------+-------------------------------------+-------+----------------------+----------------------+ ``` *** #### workload cron run Run a command using a persistent cron workload runner. Refer to the [cpln workload cron run guide](/guides/cli/workload/cron-run) for additional details. The runner workload is created on first use and reused for subsequent runs, avoiding the overhead of creating and deleting a workload each time. `--interactive` and `--background` are mutually exclusive. **Usage** ```none theme={null} cpln workload cron run [OPTIONS] -- ``` ```text theme={null} Command options: --name Target (or create) a cron workload with this exact name; without it a generated cpln-run-* runner is used --tag Attach tags (e.g., --tag drink=water) --image Override image --interactive, -i Make the session interactive [boolean] [default: false] --background, -b Run in background without waiting for completion (fire and forget) [boolean] [default: false] --timeout, -t Maximum time to wait for job completion in seconds [number] [default: 600] --cpu Set allocated CPU for the main container --memory, --mem Set allocated memory for the main container --env Environment variables in KEY=VALUE format --shell, -s Shell to use, only valid when interactive flag is true [default: "bash"] --location Location to run the command --container Which container to run the command in --identity Attach the named identity to the runner workload -- Command to execute. Must be at the end of the command (e.g., -- echo hello world) Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Default (wait for completion) theme={null} cpln workload cron run --org demo-cpln-organization --gvc demo-gvc -- echo "Hello from Control Plane!" ``` ```bash Background (fire and forget) theme={null} cpln workload cron run --org demo-cpln-organization --gvc demo-gvc --background -- npm run db:migrate ``` ```bash Interactive session theme={null} cpln workload cron run --org demo-cpln-organization --gvc demo-gvc --interactive -- bash ``` *** #### workload cron start Start a cron job **Usage** ```none theme={null} cpln workload cron start [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --location A global virtual cloud location [string] --container-name Name of the container [string] --env One or more environment variables in KEY=VALUE format [string] --command Override the entrypoint [string] --arg One or more custom command line argument that will be sent to the container [string] --active-deadline-seconds Maximum duration in seconds for the cron job to complete. Used by the deadline reconciler to detect orphaned commands. [number] --file, -f File to load and use for the command. Use `--file -` to enable input from stdin. Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Basic Usage ```bash Command theme={null} cpln workload cron start demo-cron-workload ``` ```text Output theme={null} +-------------------+-----------------+-------------------------------------+-------+----------------------+----------------+ |WORKLOAD |LOCATION |NAME OR COMMAND ID |STATUS |START TIME |COMPLETION TIME | |-------------------|-----------------|-------------------------------------|-------|----------------------|----------------| |demo-cron-workload │aws-eu-central-1 |ce770d2b-2b7f-4415-b41e-961f161bcd86 |Active |3/30/2024, 3:29:47 AM | | +-------------------+-----------------+-------------------------------------+-------+----------------------+----------------+ ``` * Override Container Through Arguments ```bash Command theme={null} cpln workload cron start demo-cron-workload --container-name demo --command bash --arg echo --arg hello --arg world --env APP_MODE=dev --location aws-eu-central-1 ``` ```text Output theme={null} +-------------------+-----------------+-------------------------------------+-------+----------------------+----------------+ |WORKLOAD |LOCATION |NAME OR COMMAND ID |STATUS |START TIME |COMPLETION TIME | |-------------------|-----------------|-------------------------------------|-------|----------------------|----------------| |demo-cron-workload │aws-eu-central-1 |2afa5cbc-049d-455b-96bc-db4cf459def3 |Active |3/30/2024, 3:29:47 AM | | +-------------------+-----------------+-------------------------------------+-------+----------------------+----------------+ ``` * Override Multiple Containers Through a File **Example File** ```yaml YAML theme={null} name: demo env: - name: APP_MODE value: dev command: bash args: - echo - hello - world --- name: demo env: - name: APP_MODE value: prod command: bash args: - ls ``` **Usage** ```bash Command theme={null} cpln workload cron start demo-cron-workload --file overrideContainerFile.yaml --location aws-eu-central-1 ``` ```text Output theme={null} +-------------------+-----------------+-------------------------------------+-------+----------------------+----------------+ |WORKLOAD |LOCATION |NAME OR COMMAND ID |STATUS |START TIME |COMPLETION TIME | |-------------------|-----------------|-------------------------------------|-------|----------------------|----------------| |demo-cron-workload │aws-eu-central-1 |beac030b-8e54-46c1-acbe-aa19e1682258 |Active |3/30/2024, 3:29:47 AM | | +-------------------+-----------------+-------------------------------------+-------+----------------------+----------------+ ``` *** #### workload cron stop Stop a cron job **Usage** ```none theme={null} cpln workload cron stop [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --replica-name Name of the replica [string] [required] --location A global virtual cloud location [string] [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### workload delete Delete one or more referenced workloads **Usage** ```none theme={null} cpln workload delete [OPTIONS] ``` Deleting a workload will remove all associated resources (containers, etc.) ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [required] [default: []] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Delete a workload ```bash Command theme={null} cpln cpln workload delete demo-workload-1 --org demo-cpln-organization --gvc demo-gvc-workload ``` ```text Output theme={null} If the delete was successful, a new command prompt will be shown. ``` * Delete multiple workloads ```bash Command theme={null} cpln workload delete demo-workload-1 demo-workload-2 --org demo-cpln-organization --gvc demo-gvc-workload ``` ```text Output theme={null} If the delete was successful, a new command prompt will be shown. ``` *** #### workload edit Edit the referenced workload, as YAML, within an editor **Usage** ```none theme={null} cpln workload edit [OPTIONS] ``` The default editor can be set by using the EDITOR environment variable. ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --replace, -r Replace instead of patch/merge [boolean] [default: false] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln workload edit demo-workload --org demo-cpln-organization --gvc demo-gvc-workload ``` ```text Output theme={null} Your default editor will be launched. Once any modifications are saved, the updates will be sent to the API. ``` *** #### workload eventlog Show the event log of the referenced workload **Usage** ```none theme={null} cpln workload eventlog [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln workload eventlog demo-workload --org demo-cpln-organization --gvc demo-gvc-workload ``` ```text Output theme={null} The event log for the referenced workload will be shown. ``` *** #### workload exec Exec a command on a replica of the workload. Refer to the [cpln workload exec guide](/guides/cli/workload/exec) for additional details. **Usage** ```none theme={null} cpln workload exec --location LOCATION -- COMMAND ARG1 ARG2 [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --location A global virtual cloud location [string] --replica Replica of the deployment --container Container name of the workload --stdin, -i Pass stdin to the container [boolean] [default: false] --tty, -t Stdin is a TTY [boolean] [default: false] --quiet, -q Only print output from the remote session [boolean] [default: false] -- Command to execute on replica Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Execute a command ```bash theme={null} cpln workload exec index --location aws-eu-central-1 -- ls /etc | grep group ``` *** #### workload force-redeployment Force redeployment of the referenced workload(s). **Usage** ```none theme={null} cpln workload force-redeployment [OPTIONS] ``` ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [required] [default: []] Command options: --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### workload get Retrieve one or more referenced workloads **Usage** ```none theme={null} cpln workload get [ref...] [OPTIONS] ``` An empty reference will return all references ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [default: []] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Command options: --all-gvcs Show workloads from all the global virtual clouds within the current or overridden organization [boolean] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Get all workloads ```bash Command theme={null} cpln workload get --org demo-cpln-organization --gvc demo-gvc-workload ``` ```text Output theme={null} +--------------------+--------------------+------------------+------+--------------+-------------------------------------------------------------------+--------------+ |NAME |DESCRIPTION |GVC |READY |CREATED |ENDPOINT |TAGS | |--------------------|--------------------|------------------|------|--------------|-------------------------------------------------------------------|--------------| |demo-workload-get-2 |demo-workload-get-2 |demo-gvc-workload |false |Now |https://demo-workload-get-2-2x5qqmrp3zzt6.geta3p46gfrsp.t.cpln.app | | |demo-workload-get-1 |demo-workload-get-1 |demo-gvc-workload |false |Now |https://demo-workload-get-1-2x5qqmrp3zzt6.geta3p46gfrsp.t.cpln.app | | |demo-workload |demo-workload |demo-gvc-workload |true |5 minutes ago |https://demo-workload-2x5qqmrp3zzt6.geta3p46gfrsp.t.cpln.app |drink="water" | +--------------------+--------------------+------------------+------+--------------+-------------------------------------------------------------------+--------------+ ``` * Get one workload ```bash Command theme={null} cpln workload get demo-workload-get-1 --org demo-cpln-organization --gvc demo-gvc-workload ``` ```text Output theme={null} +--------------------+--------------------+------------------+------+--------+-------------------------------------------------------------------+-----+ |NAME |DESCRIPTION |GVC |READY |CREATED |ENDPOINT |TAGS | |--------------------|--------------------|------------------|------|--------|-------------------------------------------------------------------|-----| |demo-workload-get-1 |demo-workload-get-1 |demo-gvc-workload |false |Now |https://demo-workload-get-1-2x5qqmrp3zzt6.geta3p46gfrsp.t.cpln.app | | +--------------------+--------------------+------------------+------+--------+-------------------------------------------------------------------+-----+ ``` * Get multiple workloads ```bash Command theme={null} cpln workload get demo-workload-get-1 demo-workload-get-2 --org demo-cpln-organization --gvc demo-gvc-workload ``` ```text Output theme={null} +--------------------+--------------------+------------------+------+--------+-------------------------------------------------------------------+-----+ |NAME |DESCRIPTION |GVC |READY |CREATED |ENDPOINT |TAGS | |--------------------|--------------------|------------------|------|--------|-------------------------------------------------------------------|-----| |demo-workload-get-1 |demo-workload-get-1 |demo-gvc-workload |false |Now |https://demo-workload-get-1-2x5qqmrp3zzt6.geta3p46gfrsp.t.cpln.app | | |demo-workload-get-2 |demo-workload-get-2 |demo-gvc-workload |false |Now |https://demo-workload-get-2-2x5qqmrp3zzt6.geta3p46gfrsp.t.cpln.app | | +--------------------+--------------------+------------------+------+--------+-------------------------------------------------------------------+-----+ ``` *** #### workload get-deployments Retrieve the current deployments of the referenced workload **Usage** ```none theme={null} cpln workload get-deployments [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln workload get-deployments demo-workload-get-deployments --gvc demo-gvc-get-deployments --org demo-cpln-organization ``` ```text Output theme={null} +-----------------+--------------+---------------------------------------------------------------------------------+ |NAME/LOCATION |MODIFIED |STATUS | |-----------------|--------------|---------------------------------------------------------------------------------| |aws-eu-central-1 |0 minutes ago |endpoint="https://demo-workload-get-deployments-dta8cb6daad8a.aws-eu-central-... | | | |expectedDeploymentVersion=2 | | | |internal={"podStatus":{},"podsValidZone":true,"timestamp":"2024-02-29T22:04:0... | | | |lastProcessedVersion=2 | | | |message="" | | | |ready=true | | | |message="" | | | |ready=true | | | |remote="https://a4e2dc319c383ac09ceeed0475a71f33.remote.cpln.io" | | | |versions=[{"message":"Replica(s) Initializing...","ready":true,"containers":{... | | | |versions=[{"message":"Replica(s) Initializing...","ready":true,"containers":{... | +-----------------+--------------+---------------------------------------------------------------------------------+ ``` *** #### workload get-replicas Get the replicas of the referenced workload in a given location **Usage** ```none theme={null} cpln workload get-replicas --location LOCATION [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --location A global virtual cloud location [string] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln workload get-replicas demo-workload --location aws-eu-central-1 --gvc demo-gvc --org demo-cpln-organization ``` ```text Output theme={null} +-----------------------------------------------+ |REPLICA | |-----------------------------------------------| |demo-workload-00118-deployment-655f87bffb-nmxxr| +-----------------------------------------------+ ``` *** #### workload open Open the referenced workload's endpoint in your browser **Usage** ```none theme={null} cpln workload open [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Open the endpoint of the referenced workload ```bash Command theme={null} cpln workload open demo-workload --gvc demo-gvc --org $2 ``` ```text Output theme={null} The endpoint for this workload will be opened in the default browser ``` *** #### workload patch Update the referenced workload's metadata using an input file **Usage** ```none theme={null} cpln workload patch --file FILE_NAME [OPTIONS] ``` Running this command with the option **`--file -`** sets the input to stdin. The input is required to be a valid JSON or YAML string (e.g., `{"description": "My new description"}` or `description: My new description`). To complete the update, press Control-D (or Control-Z if you are using Windows). ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --file, -f File to load the patch from. Use `--file -` to enable input from stdin. [required] --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln workload patch demo-workload --file workload-update.json --org $2 ``` ```text Output theme={null} The changes within the JSON file will be performed against the referenced workload. ``` To update the workload options (i.e., Capacity AI, Scaling Strategy) using the CLI, use the example below to pipe the updated JSON or YAML text to the patch command: ```bash theme={null} echo '{"spec": {"defaultOptions": {"autoscaling": {"metric": "cpu", "target": 90}, "capacityAI": false}}}' | cpln workload patch WORKLOAD_NAME --f - --org ORG_NAME --gvc GVC_NAME ``` *** #### workload permissions Show the grantable permissions for a workload object type **Usage** ```none theme={null} cpln workload permissions [OPTIONS] ``` ```text theme={null} Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln workload permissions --org demo-cpln-organization --gvc demo-gvc-workload ``` ```text Output theme={null} +----------------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------------+ |NAME |DESCRIPTION |IMPLIES | |----------------------|-----------------------------------------|-----------------------------------------------------------------------------------------------------------------| |configureLoadBalancer |Principal can configure LB | | |connect |Connect to replica (open an interacti... | | |create |Create new workloads | | |delete |Delete existing workloads | | |edit |Modify existing workloads |view | |exec |Execute commands |exec.runCronWorkload, exec.stopReplica | |exec.runCronWorkload |Force a cron-workload to run | | |exec.stopReplica |Force a replica to be stopped | | |manage |Full access |configureLoadBalancer, connect, create, delete, edit, exec, exec.runCronWorkload, exec.stopReplica, manage, view | |view |Read-only access | | +----------------------+-----------------------------------------+-----------------------------------------------------------------------------------------------------------------+ ``` *** #### workload query Find all the workloads based on the given query **Usage** ```none theme={null} cpln workload query QUERY_OPTIONS [OPTIONS] ``` ```text theme={null} Query options: --match Query filter type [choices: "all", "none", "any"] [default: "all"] --property, --prop Property to query --tag Tag to query --rel Relation to query Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) Use the `--match` option to display **all**, **none**, or **any** of the results based on a given property or tag * Search for a workload based on the name property ```bash Command theme={null} cpln workload query --match any --prop name=demo-workload --org demo-cpln-organization --gvc demo-gvc-workload ``` ```text Output theme={null} +--------------------+--------------------+------------------+------+--------------+-------------------------------------------------------------------+--------------+ |NAME |DESCRIPTION |GVC |READY |CREATED |ENDPOINT |TAGS | |--------------------|--------------------|------------------|------|--------------|-------------------------------------------------------------------|--------------| |demo-workload-get-2 |demo-workload-get-2 |demo-gvc-workload | |Now |https://demo-workload-get-2-2x5qqmrp3zzt6.geta3p46gfrsp.t.cpln.app | | |demo-workload-get-1 |demo-workload-get-1 |demo-gvc-workload | |Now |https://demo-workload-get-1-2x5qqmrp3zzt6.geta3p46gfrsp.t.cpln.app | | |demo-workload |demo-workload |demo-gvc-workload | |5 minutes ago |https://demo-workload-2x5qqmrp3zzt6.geta3p46gfrsp.t.cpln.app |drink="water" | +--------------------+--------------------+------------------+------+--------------+-------------------------------------------------------------------+--------------+ ``` * Search for a workload based on the description property ```bash Command theme={null} cpln workload query --match any --prop description="demo-workload" --org demo-cpln-organization --gvc demo-gvc-workload ``` ```text Output theme={null} +--------------------+--------------------+------------------+------+--------------+-------------------------------------------------------------------+--------------+ |NAME |DESCRIPTION |GVC |READY |CREATED |ENDPOINT |TAGS | |--------------------|--------------------|------------------|------|--------------|-------------------------------------------------------------------|--------------| |demo-workload-get-2 |demo-workload-get-2 |demo-gvc-workload | |Now |https://demo-workload-get-2-2x5qqmrp3zzt6.geta3p46gfrsp.t.cpln.app | | |demo-workload-get-1 |demo-workload-get-1 |demo-gvc-workload | |Now |https://demo-workload-get-1-2x5qqmrp3zzt6.geta3p46gfrsp.t.cpln.app | | |demo-workload |demo-workload |demo-gvc-workload | |5 minutes ago |https://demo-workload-2x5qqmrp3zzt6.geta3p46gfrsp.t.cpln.app |drink="water" | +--------------------+--------------------+------------------+------+--------------+-------------------------------------------------------------------+--------------+ ``` * Search for a workload based on a tag ```bash Command theme={null} cpln workload query --match any --tag drink=water --org demo-cpln-organization --gvc demo-gvc-workload ``` ```text Output theme={null} +--------------------+--------------------+------------------+------+--------------+-------------------------------------------------------------------+--------------+ |NAME |DESCRIPTION |GVC |READY |CREATED |ENDPOINT |TAGS | |--------------------|--------------------|------------------|------|--------------|-------------------------------------------------------------------|--------------| |demo-workload-get-2 |demo-workload-get-2 |demo-gvc-workload | |Now |https://demo-workload-get-2-2x5qqmrp3zzt6.geta3p46gfrsp.t.cpln.app | | |demo-workload-get-1 |demo-workload-get-1 |demo-gvc-workload | |Now |https://demo-workload-get-1-2x5qqmrp3zzt6.geta3p46gfrsp.t.cpln.app | | |demo-workload |demo-workload |demo-gvc-workload | |6 minutes ago |https://demo-workload-2x5qqmrp3zzt6.geta3p46gfrsp.t.cpln.app |drink="water" | +--------------------+--------------------+------------------+------+--------------+-------------------------------------------------------------------+--------------+ ``` * Search for a workload based on a property and tag ```bash Command theme={null} cpln workload query --match any --prop description="demo-workload" --tag drink=water --org demo-cpln-organization --gvc demo-gvc-workload ``` ```text Output theme={null} +--------------------+--------------------+------------------+------+--------------+-------------------------------------------------------------------+--------------+ |NAME |DESCRIPTION |GVC |READY |CREATED |ENDPOINT |TAGS | |--------------------|--------------------|------------------|------|--------------|-------------------------------------------------------------------|--------------| |demo-workload-get-2 |demo-workload-get-2 |demo-gvc-workload | |Now |https://demo-workload-get-2-2x5qqmrp3zzt6.geta3p46gfrsp.t.cpln.app | | |demo-workload-get-1 |demo-workload-get-1 |demo-gvc-workload | |Now |https://demo-workload-get-1-2x5qqmrp3zzt6.geta3p46gfrsp.t.cpln.app | | |demo-workload |demo-workload |demo-gvc-workload | |6 minutes ago |https://demo-workload-2x5qqmrp3zzt6.geta3p46gfrsp.t.cpln.app |drink="water" | +--------------------+--------------------+------------------+------+--------------+-------------------------------------------------------------------+--------------+ ``` *** #### workload replica Manage workload replicas ```none theme={null} cpln workload replica [SUBCOMMAND] ``` ```text theme={null} Commands: cpln workload replica get Get the replica of the referenced workload in a given location cpln workload replica stop Stop the replica of the referenced workload in a given location Options: --help Show help [boolean] ``` #### workload replica get Get the replica of the referenced workload in a given location **Usage** ```none theme={null} cpln workload replica get [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --replica-name Name of the replica [string] --location A global virtual cloud location [string] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) *** #### workload replica stop Stop the replica of the referenced workload in a given location **Usage** ```none theme={null} cpln workload replica stop [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Command options: --replica-name Name of the replica [string] [required] --location A global virtual cloud location [string] [required] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash Command theme={null} cpln workload replica stop demo-workload --replica-name demo-workload-0 --location aws-eu-central-1 ``` ```text Output theme={null} +-----------------+-----------------+-------------------+---------+----------------------+----------------+ |WORKLOAD |LOCATION |REPLICA |STATUS |START TIME |COMPLETION TIME | |-----------------|-----------------|-------------------|---------|----------------------|----------------| |demo-workload |aws-eu-central-1 |demo-workload-0 |Stopping |3/30/2024, 4:02:12 AM | | +-----------------+-----------------+-------------------+---------+----------------------+----------------+ ``` *** #### workload run Run a command with a workload instance. Refer to the [cpln workload run guide](/guides/cli/workload/run) for additional details. **Recommendation:** Consider using [`cpln workload cron run`](#workload-cron-run) instead — it's significantly faster and the preferred way to run commands going forward. It uses a persistent cron workload runner that is reused across executions, eliminating the overhead of creating and tearing down a workload each time. Most basic usage is `cpln workload run -- ls -al`. This will create a new workload to execute the command, it uses an ubuntu base image. Other examples are: If you have a workload you want to reuse: `cpln workload run --clone WORKLOAD_NAME -- ls -al` `-- `: Must always be at the end. Optional flags: `--rm`: To clean up afterwards. `-i`: To connect to the session. `--cpu` and `--mem`: To override the default resources. `--location`: To override the location. (By default the first location fetched from your gvc). **Usage** ```none theme={null} cpln workload run -- COMMAND [OPTIONS] ``` ```text theme={null} Command options: --clone Clone a workload --tag Attach tags (e.g., --tag drink=water) --image Override image --interactive, -i Make the session interactive [boolean] [default: false] --remove, --rm Deletes the workload after the command is run [boolean] [default: false] --cpu Set allocated CPU for the main container --memory, --mem Set allocated memory for the main container --env Environment variables in KEY=VALUE format --command, -c Container command --arg, -a Container args --shell, -s Shell to use, only valid when interactive flag is true [default: "bash"] --location Location to run the command --container Which container to run the command in, only used when "clone" option is used Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash theme={null} cpln workload run --org demo-cpln-organization --gvc demo-gvc -- ls -al ``` *** #### workload start Start the workload(s) **Usage** ```none theme={null} cpln workload start [OPTIONS] ``` ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [required] [default: []] Command options: --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash theme={null} cpln workload start demo-workload --org demo-cpln-organization --gvc demo-gvc ``` *** #### workload stop Stop the workload(s) **Usage** ```none theme={null} cpln workload stop [OPTIONS] ``` ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [required] [default: []] Command options: --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) ```bash theme={null} cpln workload stop demo-workload --org demo-cpln-organization --gvc demo-gvc ``` *** #### workload tag Manage the tags belonging to one or more referenced workloads **Usage** ```none theme={null} cpln workload tag --tag KEY=VALUE [OPTIONS] ``` Built-in tags, which begin with cpln/\*, can't be modified. ```text theme={null} Positionals: ref One or more resource references. Usually it is the name of the resource. [array] [required] [default: []] Command options: --tag Attach tags (e.g., --tag drink=water) --remove-tag Remove tags (e.g., --remove-tag tagname) --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Add a tag to a workload ```bash Command theme={null} cpln workload tag demo-workload-tag-1 --tag drink=water --org demo-cpln-organization --gvc demo-gvc-workload ``` ```text Output theme={null} +--------------------+--------------------+------------------+------+--------+-------------------------------------------------------------------+--------------+ |NAME |DESCRIPTION |GVC |READY |CREATED |ENDPOINT |TAGS | |--------------------|--------------------|------------------|------|--------|-------------------------------------------------------------------|--------------| |demo-workload-tag-1 |demo-workload-tag-1 |demo-gvc-workload | |Now |https://demo-workload-tag-1-2x5qqmrp3zzt6.geta3p46gfrsp.t.cpln.app |drink="water" | +--------------------+--------------------+------------------+------+--------+-------------------------------------------------------------------+--------------+ ``` * Update same tag (after running the example above) ```bash Command theme={null} cpln workload tag demo-workload-tag-1 --tag drink=coffee --org demo-cpln-organization --gvc demo-gvc-workload ``` ```text Output theme={null} +--------------------+--------------------+------------------+------+--------+-------------------------------------------------------------------+---------------+ |NAME |DESCRIPTION |GVC |READY |CREATED |ENDPOINT |TAGS | |--------------------|--------------------|------------------|------|--------|-------------------------------------------------------------------|---------------| |demo-workload-tag-1 |demo-workload-tag-1 |demo-gvc-workload | |Now |https://demo-workload-tag-1-2x5qqmrp3zzt6.geta3p46gfrsp.t.cpln.app |drink="coffee" | +--------------------+--------------------+------------------+------+--------+-------------------------------------------------------------------+---------------+ ``` * Remove tag (after running the example above) ```bash Command theme={null} cpln workload tag demo-workload-tag-1 --remove drink --org demo-cpln-organization --gvc demo-gvc-workload ``` ```text Output theme={null} +--------------------+--------------------+------------------+------+--------+-------------------------------------------------------------------+-----+ |NAME |DESCRIPTION |GVC |READY |CREATED |ENDPOINT |TAGS | |--------------------|--------------------|------------------|------|--------|-------------------------------------------------------------------|-----| |demo-workload-tag-1 |demo-workload-tag-1 |demo-gvc-workload | |Now |https://demo-workload-tag-1-2x5qqmrp3zzt6.geta3p46gfrsp.t.cpln.app | | +--------------------+--------------------+------------------+------+--------+-------------------------------------------------------------------+-----+ ``` * Add same tag to multiple workloads ```bash Command theme={null} cpln workload tag demo-workload-tag-1 demo-workload-tag-2 --tag multiple-workloads=true --org demo-cpln-organization --gvc demo-gvc-workload ``` ```text Output theme={null} +--------------------+--------------------+------------------+------+--------+-------------------------------------------------------------------+--------------------------+ |NAME |DESCRIPTION |GVC |READY |CREATED |ENDPOINT |TAGS | |--------------------|--------------------|------------------|------|--------|-------------------------------------------------------------------|--------------------------| |demo-workload-tag-1 |demo-workload-tag-1 |demo-gvc-workload | |Now |https://demo-workload-tag-1-2x5qqmrp3zzt6.geta3p46gfrsp.t.cpln.app |multiple-workloads="true" | |demo-workload-tag-2 |demo-workload-tag-2 |demo-gvc-workload | |Now |https://demo-workload-tag-2-2x5qqmrp3zzt6.geta3p46gfrsp.t.cpln.app |multiple-workloads="true" | +--------------------+--------------------+------------------+------+--------+-------------------------------------------------------------------+--------------------------+ ``` * Update same tag on multiple workloads (after running the example above) ```bash Command theme={null} cpln workload tag demo-workload-tag-1 demo-workload-tag-2 --tag multiple-workloads=demo-workload --org demo-cpln-organization --gvc demo-gvc-workload ``` ```text Output theme={null} +--------------------+--------------------+------------------+------+--------+-------------------------------------------------------------------+-----------------------------------+ |NAME |DESCRIPTION |GVC |READY |CREATED |ENDPOINT |TAGS | |--------------------|--------------------|------------------|------|--------|-------------------------------------------------------------------|-----------------------------------| |demo-workload-tag-1 |demo-workload-tag-1 |demo-gvc-workload | |Now |https://demo-workload-tag-1-2x5qqmrp3zzt6.geta3p46gfrsp.t.cpln.app |multiple-workloads="demo-workload" | |demo-workload-tag-2 |demo-workload-tag-2 |demo-gvc-workload | |Now |https://demo-workload-tag-2-2x5qqmrp3zzt6.geta3p46gfrsp.t.cpln.app |multiple-workloads="demo-workload" | +--------------------+--------------------+------------------+------+--------+-------------------------------------------------------------------+-----------------------------------+ ``` * Remove same tag from multiple workload (after running the example above) ```bash Command theme={null} cpln workload tag demo-workload-tag-1 demo-workload-tag-2 --remove multiple-workloads --org demo-cpln-organization --gvc demo-gvc-workload ``` ```text Output theme={null} +--------------------+--------------------+------------------+------+--------+-------------------------------------------------------------------+-----+ |NAME |DESCRIPTION |GVC |READY |CREATED |ENDPOINT |TAGS | |--------------------|--------------------|------------------|------|--------|-------------------------------------------------------------------|-----| |demo-workload-tag-1 |demo-workload-tag-1 |demo-gvc-workload | |Now |https://demo-workload-tag-1-2x5qqmrp3zzt6.geta3p46gfrsp.t.cpln.app | | |demo-workload-tag-2 |demo-workload-tag-2 |demo-gvc-workload | |Now |https://demo-workload-tag-2-2x5qqmrp3zzt6.geta3p46gfrsp.t.cpln.app | | +--------------------+--------------------+------------------+------+--------+-------------------------------------------------------------------+-----+ ``` *** #### workload update Update properties of the referenced workload **Usage** ```none theme={null} cpln workload update --set UPDATE_PROPERTY [OPTIONS] ``` ```text theme={null} Positionals: ref The resource reference. Usually it is the name of the resource. [required] Update Properties: --set Update the following properties (e.g., --set description="Updated Description"): description string tags. string spec.identityLink string spec.containers..image string spec.containers..workingDir string spec.containers..metrics.port string spec.containers..metrics.path string spec.containers..cpu string spec.containers..memory string spec.containers..command string spec.containers..args string[] spec.containers..env..value string spec.containers..env. string spec.containers..inheritEnv boolean spec.firewallConfig.external.inboundAllowCIDR string[] spec.firewallConfig.external.outboundAllowHostname string[] spec.firewallConfig.external.outboundAllowCIDR string[] spec.firewallConfig.internal.inboundAllowType { 'none' | 'same-gvc' | 'same-org' | 'workload-list' } spec.firewallConfig.internal.inboundAllowWorkload string[] spec.defaultOptions.autoscaling.metric { 'concurrency' | 'cpu' | 'rps' } spec.defaultOptions.autoscaling.target number spec.defaultOptions.autoscaling.minScale number spec.defaultOptions.autoscaling.maxScale number spec.defaultOptions.autoscaling.scaleToZeroDelay number spec.defaultOptions.autoscaling.maxConcurrency number spec.defaultOptions.timeoutSeconds number spec.defaultOptions.capacityAI boolean spec.defaultOptions.debug boolean spec.defaultOptions.suspend boolean spec.job.schedule string spec.job.concurrencyPolicy { 'Forbid' | 'Replace' } spec.job.historyLimit number spec.job.restartPolicy { 'OnFailure' | 'Never' } spec.job.activeDeadlineSeconds number spec.loadBalancer.direct.enabled boolean spec.loadBalancer.direct.ipSet string spec.loadBalancer.geoLocation.enabled boolean spec.loadBalancer.geoLocation.headers.asn string spec.loadBalancer.geoLocation.headers.city string spec.loadBalancer.geoLocation.headers.country string spec.loadBalancer.geoLocation.headers.region string spec.requestRetryPolicy.attempts number spec.requestRetryPolicy.retryOn string[] spec.extras string spec.extras.affinity string spec.extras.tolerations string spec.extras.topologySpreadConstraints string Arrays can be appended to, replaced, or removed using the `+=`, `=`, or `-=` operators, respectively. --unset Unset a property (same paths as --set, e.g., --unset description) Command options: --dry-run Validate the request and print the resource that would be written, without writing it. Defaults the output format to yaml-slim [boolean] Context options: --profile Override the profile. The default profile can be set using the `cpln profile set-default` command or by setting the CPLN_PROFILE environment variable --org Override the organization. The default organization can be set in your profile by using the `cpln profile update` command --gvc Override the global virtual cloud. The default global virtual cloud can be set in your profile by using the `cpln profile update` command Format options: --output, -o Set the output format [choices: "text", "json", "yaml", "json-slim", "yaml-slim", "tf", "crd", "names"] --color Colorize the output [boolean] [default: true] --ts Timestamp format if the output format is text (e.g., --output=text) [choices: "iso", "local", "age"] --max Maximum number of records to show. A value less than 1 will return all records. [number] [default: 50] Request options: --token Override the authorization token --endpoint Override the API URL --insecure, -k Ignore TLS errors [boolean] Debug options: --verbose, -v Enable verbose output to stderr [boolean] [default: false] --debug, -d Show all HTTP communications [boolean] [default: false] Options: --help Show help [boolean] ``` [Common Options Reference](/cli-reference/using-cli/common-options) * Array items are addressed by their `name`, not by index — for example `spec.containers\..inheritEnv` (such as `spec.containers.main.inheritEnv=true`). Index selectors like `spec.containers[0]` are not supported. * The `--set` option allows the modification of the following workload properties: * \`description1 * Description of the workload * Type: string * `tags` * Tags (key=value) associated with the workload * Type: string * `spec.identityLink` * The identity of the workload * Type: string * `spec.containers\..image` * The name of the container image to be pulled * Type: string * `spec.containers\..port` * The port to serve traffic over * Type: number * `spec.containers\..cpu` * The amount of cpu units available to the workload * Default: 150m * Type: string * `spec.containers\..memory` * The amount of memory available to the workload * Default: 128Mi * Type: string * `spec.containers\..env\..value` * The environment variables to be available to the workload * Type: string * `spec.firewallConfig.external.outboundAllowHostname` * The list of hostnames that the workload is allowed to call * Type: string\[] * `spec.firewallConfig.external.outboundAllowCIDR` * The list of CIDR values that the workload is allowed to call * Type: string\[] * `spec.firewallConfig.external.inboundAllowCIDR` * The list of CIDR values that are allowed to call the workload * Type: string\[] * Tip: Use the value `["0.0.0.0/0"]` to allow all inbound requests * `spec.firewallConfig.internal.inboundAllowType` * The allowed inbound types to a workload * Type: string * Allowed values: 'none' | 'same-gvc' | 'same-org' | 'workload-list' * `spec.firewallConfig.internal.inboundAllowWorkload` * If the inbound allow type is 'workload-list', this property will be active and contain the names of other workloads that can communicate with this workload * Type: string\[] * Allowed values: Other workload names within the same global virtual cloud **NOTE:** The ratio between CPU to Memory can be at most 1/8. * For the examples below, the initial workload is configured as: ```text theme={null} +---------------------+---------------------+------------------+------+--------+--------------------------------------------------------------------+-----+ |NAME |DESCRIPTION |GVC |READY |CREATED |ENDPOINT |TAGS | |---------------------|---------------------|------------------|------|--------|--------------------------------------------------------------------|-----| |demo-workload-update |demo-workload-update |demo-gvc-workload | |Now |https://demo-workload-update-2x5qqmrp3zzt6.geta3p46gfrsp.t.cpln.app | | +---------------------+---------------------+------------------+------+--------+--------------------------------------------------------------------+-----+ ``` * Update an workload's description ```bash Command theme={null} cpln workload update demo-workload-update --set description="new description" --org demo-cpln-organization --gvc demo-gvc-workload ``` ```text Output theme={null} +---------------------+----------------+------------------+------+--------+--------------------------------------------------------------------+-----+ |NAME |DESCRIPTION |GVC |READY |CREATED |ENDPOINT |TAGS | |---------------------|----------------|------------------|------|--------|--------------------------------------------------------------------|-----| |demo-workload-update |new description |demo-gvc-workload | |Now |https://demo-workload-update-2x5qqmrp3zzt6.geta3p46gfrsp.t.cpln.app | | +---------------------+----------------+------------------+------+--------+--------------------------------------------------------------------+-----+ ``` * Update an workload's tags ```bash Command theme={null} cpln workload update demo-workload-update --set tags.demo-key=demo-value --org demo-cpln-organization --gvc demo-gvc-workload ``` ```text Output theme={null} +---------------------+----------------+------------------+------+--------+--------------------------------------------------------------------+----------------------+ |NAME |DESCRIPTION |GVC |READY |CREATED |ENDPOINT |TAGS | |---------------------|----------------|------------------|------|--------|--------------------------------------------------------------------|----------------------| |demo-workload-update |new description |demo-gvc-workload | |Now |https://demo-workload-update-2x5qqmrp3zzt6.geta3p46gfrsp.t.cpln.app |demo-key="demo-value" | +---------------------+----------------+------------------+------+--------+--------------------------------------------------------------------+----------------------+ ``` * Update a workload's Image **NOTE:** Use the convention `//image/IMAGE:TAG` to reference an image residing within the current org's private repository. ```bash Command theme={null} cpln workload update demo-workload-update --set spec.containers.helloworld-go.image=gcr.io/knative-samples/helloworld-go:latest --org demo-cpln-organization --gvc demo-gvc-workload ``` ```text Output theme={null} +---------------------+----------------+------------------+------+--------+--------------------------------------------------------------------+----------------------+ |NAME |DESCRIPTION |GVC |READY |CREATED |ENDPOINT |TAGS | |---------------------|----------------|------------------|------|--------|--------------------------------------------------------------------|----------------------| |demo-workload-update |new description |demo-gvc-workload | |Now |https://demo-workload-update-2x5qqmrp3zzt6.geta3p46gfrsp.t.cpln.app |demo-key="demo-value" | +---------------------+----------------+------------------+------+--------+--------------------------------------------------------------------+----------------------+ ``` *** # Authentication Source: https://docs.controlplane.com/cli-reference/get-started/authentication Authenticate with the Control Plane CLI using interactive login or token-based flows. The CLI supports multiple authentication methods for different environments and use cases. ## Interactive login The default authentication method opens your browser to sign in with a confirmation code. ```bash theme={null} cpln login ``` This command: 1. Prints a six-digit confirmation code and opens the login page in your browser 2. Waits while you sign in and enter the code 3. Creates a `default` profile with your credentials ```text theme={null} First, copy your confirmation code: 960619 Opening your browser to log in. Browser didn't open? Use the URL below: https://auth.cpln.io/cli-login?id=A1b2C3d4E5f6G7h8I9j0KlMnOpQrStUvWxYz Waiting for login to complete... (code expires in ~10 minutes) ``` After successful authentication, the CLI is ready to use with your default profile. ## Unattended authentication For automation where no person is available to complete a sign-in (CI/CD pipelines, scripts, long-lived containers), use a service account token. 1. Click **Service Accounts** in the left menu and click **New** (or use the **Create** dropdown in the upper right corner) 2. Enter a unique name and optional description 3. Click **Next (Tags)**, add any optional tags, then click **Create** ```bash theme={null} cpln serviceaccount create --name pipeline-automation --description "CI/CD automation" ``` See the [Create a Service Account](/guides/create-service-account) guide for details. 1. Open the service account you created 2. Click the **Keys** link 3. Enter a key description and click **Add** 4. Copy and download the generated key securely ```bash theme={null} cpln serviceaccount add-key pipeline-automation --description github-workflow ``` Save the returned key securely. Use the token directly or create a profile: ```bash theme={null} cpln profile create --token --org --gvc ``` When using `--token`, always include `--gvc` if your commands require a default GVC context. Set as default (optional): ```bash theme={null} cpln profile set-default ``` Pass the token with each command: ```bash theme={null} cpln gvc get --token --org ``` Set the `CPLN_TOKEN` environment variable: ```bash theme={null} export CPLN_TOKEN= cpln gvc get --org ``` For detailed service account token setup, see the [Browser-less CLI Login](/guides/browser-less-cli-login) guide. ## Token management ### View your current token Display the token for a profile: ```bash theme={null} cpln profile token ``` ### Override token for a single command Use a different token without changing your profile: ```bash theme={null} cpln gvc get --token ``` ### Token precedence The CLI uses tokens in this order: 1. `--token` flag (highest priority) 2. `CPLN_TOKEN` environment variable 3. Profile token (default) ## Authentication for CI/CD For automated environments: 1. Create a service account with appropriate permissions 2. Generate a key and store it securely (e.g., `CPLN_TOKEN` in secrets manager) 3. Create a profile in your CI/CD pipeline ```bash theme={null} cpln profile update ci --org --default ``` See [CI/CD usage](/cli-reference/ci-cd-development/ci-cd) for complete automation setup. ## Troubleshooting The confirmation code is valid for about 10 minutes. If it expires before you finish signing in, the CLI stops waiting and reports that the session expired. Run `cpln login` again to get a fresh URL and code. The browser does not have to run on the same machine as the CLI. Copy the printed URL to a browser on any device, sign in, and enter the confirmation code — the waiting CLI picks up the credentials automatically. If no person is available to complete a sign-in at all, such as in a CI/CD pipeline, use a service account token instead. A 403 error typically means one of the following: * **Wrong org**: You're trying to access an org you don't have access to, or there's a typo in the org name. Verify with `cpln profile get`. * **Missing token**: No token is provided. Ensure `CPLN_TOKEN` is set or you're using a profile with a valid token. * **Insufficient permissions**: Your token doesn't have the required permissions. Generate a new key or re-login to refresh your token: ```bash theme={null} cpln login ``` Or for service accounts: ```bash theme={null} cpln serviceaccount add-key --description ``` For more troubleshooting help, see the [Troubleshooting](/cli-reference/using-cli/troubleshooting) page. ## Next steps Manage multiple authentication contexts Automate with service accounts # Images Source: https://docs.controlplane.com/cli-reference/get-started/images Build, push, pull, and manage container images with the Control Plane CLI. The CLI provides powerful commands for working with container images. Build locally, push to your org's private registry, pull from external registries, and copy images between organizations. ## Quick Reference | Command | Description | | ------------------------- | ---------------------------------------------------------- | | `cpln image build` | Build images locally or remotely, and optionally push them | | `cpln image docker-login` | Authenticate Docker to your org's registry | | `cpln image get` | List or view images in your org | | `cpln image copy` | Copy images between organizations | | `cpln image delete` | Delete images from your registry | ## Build and Push Images The most common workflow is building and pushing a local application: ```bash theme={null} cpln image build --name my-app:v1 --push ``` This command: 1. Builds your image using the Dockerfile in the current directory 2. Tags it for your org's private registry 3. Pushes it to `your-org.registry.cpln.io/my-app:v1` No Docker on this machine or CI runner? Add `--remote` to build remotely — the image is built and pushed for you: `cpln image build --name my-app:v1 --remote`. ### Build Options ```bash theme={null} # Build from current directory cpln image build --name my-app:v1 --push # Specify a Dockerfile cpln image build --name my-app:v1 --dockerfile ./docker/Dockerfile.prod --push # Build from a different directory cpln image build --name my-app:v1 --push --dir ./my-project # Build for a different platform cpln image build --name my-app:v1 --push --platform linux/arm64 # Build without cache cpln image build --name my-app:v1 --push --no-cache # Pass a value the Dockerfile reads with ARG cpln image build --name my-app:v1 --push --build-arg NODE_VERSION=22 # Mount a secret for one RUN step, without baking it into the image cpln image build --name my-app:v1 --push --secret id=npmtoken,src=cpln://secret/npm-token ``` | Flag | Description | | ------------------ | --------------------------------------------------------------------- | | `--dockerfile` | Path to Dockerfile (default: `./Dockerfile`) | | `--dir` | Build context directory (default: current directory) | | `--push` | Push the image to your org's private registry after building | | `--no-cache` | Build without using cache | | `--platform`, `-p` | Target platform (default: `linux/amd64`) | | `--build-arg` | Set a Dockerfile `ARG` value. See [Build Arguments](#build-arguments) | | `--secret` | Expose a secret to a `RUN` step. See [Build Secrets](#build-secrets) | When `--dir` is specified, the Dockerfile in that directory is used by default. Use `--dockerfile` to override this behavior. ```bash theme={null} cpln image build --name my-app:v1 --push ``` **Buildpack options:** | Flag | Description | | -------------------------- | ----------------------------------------------------------------------- | | `--builder`, `-B` | CNB-compatible builder image (default: `heroku/builder:24_linux-amd64`) | | `--buildpack`, `-b` | Additional buildpack to use (can be specified multiple times) | | `--dir` | Build context directory (default: current directory) | | `--push` | Push the image to your org's private registry after building | | `--no-cache` | Build without using cache | | `--env`, `-e` | Environment variable for the build (can be specified multiple times) | | `--env-file` | File containing environment variables (can be specified multiple times) | | `--trust-builder` | Trust the builder image (skip security prompts) | | `--trust-extra-buildpacks` | Trust additional buildpacks | | `--platform`, `-p` | Target platform (default: `linux/amd64`) | **Examples:** ```bash theme={null} # Use a different builder cpln image build --name my-app:v1 --push -B gcr.io/buildpacks/builder:google-22 # Add a specific buildpack (e.g., for Rust) cpln image build --name my-rust-app:v1 --push -b docker.io/paketocommunity/rust # Pass build-time environment variables cpln image build --name my-app:v1 --push -e NODE_ENV=production -e LOG_LEVEL=info # Use an env file cpln image build --name my-app:v1 --push --env-file .env.build # Build for a different platform cpln image build --name my-app:v1 --push --platform linux/arm64 # Trust builder and extra buildpacks (useful in CI/CD) cpln image build --name my-app:v1 --push --trust-builder --trust-extra-buildpacks ``` **Common builders:** | Builder | Description | | ------------------------------------- | ----------------------------------------------------------- | | `heroku/builder:24` | Default. Supports Node.js, Python, Go, Java, Ruby, and more | | `gcr.io/buildpacks/builder:google-22` | Google Cloud buildpacks | | `paketobuildpacks/builder-jammy-base` | Paketo community buildpacks (includes .NET, Rust) | For language-specific requirements and conventions, see the [Buildpacks Guide](/guides/buildpacks). Build remotely instead of through a local Docker daemon. The CLI uploads the build folder, Control Plane detects how to build it, and the image is pushed to your org's private registry, so `--push` is not used. ```bash theme={null} # Build the current folder cpln image build --name my-app:v1 --remote # Build a different folder cpln image build --name my-app:v1 --remote --dir ./my-project # Build a repository instead of a local folder cpln image build --name my-app:v1 --remote --repo https://github.com/my-org/my-app --branch main # Start the build and return immediately cpln image build --name my-app:v1 --remote --detach # Pass a build argument and mount a secret from your org cpln image build --name my-app:v1 --remote \ --build-arg NODE_VERSION=22 \ --secret id=npmtoken,src=cpln://secret/npm-token ``` | Flag | Description | | ------------- | -------------------------------------------------------------------------------- | | `--remote` | Build remotely and push the resulting image | | `--dir` | Folder to upload (default: current directory) | | `--repo` | HTTPS URL of a GitHub or GitLab repository to build instead of a folder | | `--branch` | Branch to build (requires `--repo`, defaults to the repository's default branch) | | `--detach` | Return as soon as the build starts instead of following it | | `--no-cache` | Ignore cached layers and re-upload the whole folder | | `--build-arg` | Set a Dockerfile `ARG` value. See [Build Arguments](#build-arguments) | | `--secret` | Mount a secret from your org. See [Build Secrets](#build-secrets) | **What gets uploaded:** the folder is filtered by `.dockerignore`, or by `.gitignore` when there is no `.dockerignore`, and is limited to 500 MB and 20,000 files. Common junk such as `.git`, `node_modules`, `__pycache__`, and `.venv` is excluded either way. Later builds of the same image upload only the files that changed. Symlinks travel as links, so one pointing outside the build folder fails the build. A remote build detects how to build the source itself, so the local build flags do not apply. `--dockerfile`, `--builder`, `--buildpack`, `--env`, `--env-file`, `--trust-builder`, `--trust-extra-buildpacks`, `--platform`, and `--push` are rejected together with `--remote`, and the image is built for `linux/amd64`. `--build-arg` and `--secret` do apply. A secret must name one in your org with `src=cpln://secret/`. Build logs stream until the image is pushed. Pressing `Ctrl+C` stops watching but leaves the build running remotely; check the result with `cpln image get my-app:v1`. Building a private repository requires the org's connection to GitHub or GitLab. The first build that needs it opens a browser to authorize the connection and then resumes on its own. In a non-interactive session, the CLI prints the connect URL and exits so you can authorize it and re-run the build. ### Build Arguments `--build-arg` sets a value the Dockerfile reads with `ARG`. It takes docker's own two forms: a `NAME=value` pair, or a bare `NAME` whose value comes from your environment. ```bash theme={null} # Set the value directly cpln image build --name my-app:v1 --push --build-arg NODE_VERSION=22 # Take the value from the environment export GIT_SHA=$(git rev-parse --short HEAD) cpln image build --name my-app:v1 --push --build-arg GIT_SHA # Repeat the option for each argument cpln image build --name my-app:v1 --push \ --build-arg NODE_VERSION=22 \ --build-arg GIT_SHA ``` The Dockerfile declares each one it uses: ```dockerfile theme={null} ARG NODE_VERSION=22 FROM node:${NODE_VERSION}-alpine ARG GIT_SHA LABEL org.opencontainers.image.revision=$GIT_SHA ``` **Common uses** | Use case | Example | | ----------------------------- | ---------------------------------------------------------- | | Pin a base image version | `--build-arg NODE_VERSION=22` | | Stamp provenance into a label | `--build-arg GIT_SHA` | | Switch a build variant | `--build-arg BUILD_TARGET=production` | | Select a package mirror | `--build-arg NPM_REGISTRY=https://nexus.internal/repo/npm` | | Force a cache bust | `--build-arg CACHEBUST=$(date +%s)` | A build argument is not a secret. Its value becomes part of the image's build and cache keys, and anyone who can pull the image can recover it. Use [`--secret`](#build-secrets) for tokens, keys, and passwords. A bare `NAME` that is unset in your environment sends nothing, so the Dockerfile's own `ARG` default applies. A build with no Dockerfile prints a warning naming the arguments it could not consume and continues, since buildpack builds have no `ARG` mechanism. ### Build Secrets `--secret` exposes a credential to a single `RUN` step. Unlike a build argument, the value is mounted as a file for the length of that step only: it never lands in the image, its layers, or its build cache. The option takes docker's own form, `id=[,src=]`, and `src` additionally accepts a secret in your org. ```bash theme={null} # A secret in your org cpln image build --name my-app:v1 --push \ --secret id=npmtoken,src=cpln://secret/npm-token # One field of a dictionary or structured secret cpln image build --name my-app:v1 --push \ --secret id=dbpass,src=cpln://secret/db-creds.password # A file on this machine, the way docker reads one cpln image build --name my-app:v1 --push \ --secret id=npmtoken,src=./npm-token.txt # Repeat the option for each secret cpln image build --name my-app:v1 --push \ --secret id=npmtoken,src=cpln://secret/npm-token \ --secret id=certkey,src=cpln://secret/client-tls.key ``` The Dockerfile reads it by the same `id`, at `/run/secrets/`: ```dockerfile theme={null} # syntax=docker/dockerfile:1 FROM node:22-alpine COPY package*.json ./ RUN --mount=type=secret,id=npmtoken,required=true \ NPM_TOKEN="$(cat /run/secrets/npmtoken)" npm ci ``` **Sources `src` accepts** | Source | Meaning | | ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- | | `cpln://secret/` | An [opaque secret](/guides/create-secret/opaque) in your org, mounted whole | | `cpln://secret/.` | One field of a structured secret, such as a [dictionary](/guides/create-secret/dictionary) entry or a [userpass](/guides/create-secret/userpass) password | | `` | A file on this machine, exactly as docker reads it | | omitted, or `env=` | An environment variable on this machine, exactly as docker reads it | A `--remote` build accepts only the `cpln://secret/...` sources. The build service resolves them itself with your credentials, so the value never leaves the platform. A file path or an environment variable is rejected. Add `required=true` to the mount. Without it, a missing secret mounts nothing and the build carries on with an empty credential, which usually surfaces much later as a confusing authentication error. Build secrets require Docker Buildx for local builds, and a Dockerfile in either mode. Buildpack builds cannot consume secrets, so the build is rejected before it starts. ## Use Images in Workloads Reference your pushed images when creating workloads: ```bash theme={null} cpln workload create --name my-app --gvc my-gvc \ --image //image/my-app:v1 --port 8080 --public ``` ### Image Reference Formats | Format | Description | | -------------------------------- | ------------------------------------ | | `//image/IMAGE:TAG` | Image in your org's registry | | `ORG.registry.cpln.io/IMAGE:TAG` | Image in another org's registry | | `nginx:latest` | Public image from Docker Hub | | `gcr.io/project/IMAGE:TAG` | Image from Google Container Registry | ## Authenticate Docker For direct Docker operations, authenticate to your org's registry: ```bash theme={null} cpln image docker-login ``` Then use standard Docker commands: ```bash theme={null} docker pull your-org.registry.cpln.io/my-app:v1 docker push your-org.registry.cpln.io/my-app:v1 ``` ## List and Manage Images ```bash theme={null} # List all images in your org cpln image get # Get details for a specific image cpln image get my-app:v1 # Delete an image cpln image delete my-app:v1 ``` ## Copy Images Between Orgs Copy an image to another organization: ```bash theme={null} cpln image copy my-app:v1 --to-org destination-org ``` Copy with a different name: ```bash theme={null} cpln image copy my-app:v1 --to-org destination-org --to-name renamed-app:v1 ``` For cross-org copies with different credentials, use `--to-profile`. See the [Copy Images guide](/guides/copy-image). ## CI/CD Authentication For automated pipelines, set `CPLN_TOKEN` in your CI/CD platform's secrets (e.g., GitLab CI/CD variables, GitHub secrets) and use the CLI directly: ```bash theme={null} cpln image build --name my-app:$CI_COMMIT_SHA --push ``` The CLI automatically uses `CPLN_TOKEN` when available. On runners without a Docker daemon, swap `--push` for `--remote` and the image is built and pushed remotely instead: ```bash theme={null} cpln image build --name my-app:$CI_COMMIT_SHA --remote ``` For direct Docker access, authenticate with a service account: ```bash theme={null} echo $CPLN_TOKEN | docker login your-org.registry.cpln.io -u '' --password-stdin ``` See [CI/CD Usage](/cli-reference/ci-cd-development/ci-cd) for complete automation setup. ## Troubleshooting Start Docker, or build without it: ```bash theme={null} cpln image build --name my-app:v1 --remote ``` A remote build uploads the whole build folder. Exclude what the build does not need by adding the large paths to `.dockerignore`, then re-run the build. Docker Buildx is not installed. Install it: ```bash theme={null} curl -sSL "https://github.com/docker/buildx/releases/download/v0.29.1/buildx-v0.29.1.linux-amd64" \ | install -m 0755 -D /dev/stdin ~/.docker/cli-plugins/docker-buildx ``` Re-run `cpln image docker-login` to refresh credentials and double check that you don't have typos in the org name. Buildpack builds have no secret mechanism, so the option is rejected before the build starts. Add a Dockerfile to the build context, or drop `--secret`. Local builds mount secrets through Buildx, which is not installed. Install the plugin as shown above, or build remotely with `--remote`. The `id` in `--secret id=` and the `id` in `RUN --mount=type=secret,id=` must match. When they do not, BuildKit mounts nothing and the build continues silently. Add `required=true` to the mount so the build fails at that step instead. A structured secret holds several fields, so the reference must select one: ```bash theme={null} --secret id=dbpass,src=cpln://secret/db-creds.password ``` The message lists the fields the secret carries. An [opaque secret](/guides/create-secret/opaque) needs no field, since it holds a single value. Verify you have push permission on images. Check your policies or refresh your service account token. Optimize your Dockerfile: * Use multi-stage builds * Start from smaller base images * Remove unnecessary files ## Learn More Language-specific conventions for building without Dockerfiles Detailed guide for building and pushing images Configure workloads to pull from private registries Full command documentation # Profiles Source: https://docs.controlplane.com/cli-reference/get-started/profiles Manage authentication and default context across multiple organizations and environments. Profiles store authentication credentials and default context (organization and GVC) for the Control Plane CLI. They enable you to switch between different environments and accounts without re-authenticating. ## Why use profiles Separate profiles for each org (dev, staging, production) Switch between user and service accounts easily Store tokens in a profile instead of passing them with every command Create dedicated profiles for automation ## Working with profiles ### List all profiles ```bash theme={null} cpln profile get ``` ### View a specific profile ```bash theme={null} cpln profile get ``` ### Create a new profile ```bash theme={null} cpln login ``` This creates a profile, authenticates via browser, and sets the profile as the default. The profile name is optional. If omitted, the CLI creates or updates the `default` profile. ```bash theme={null} cpln profile create --token --org ``` Set default GVC and mark as default profile: ```bash theme={null} cpln profile create --token --org --gvc --default ``` Use `--default` to set the profile as the default immediately. This is equivalent to running `cpln profile set-default ` after creation. When using `--token` with a service account key, the profile will not have a default GVC unless you explicitly set it with `--gvc`. Always include `--gvc ` when updating an existing profile if your workflow requires a default GVC. ### Set default profile ```bash theme={null} cpln profile set-default ``` All subsequent commands will use this profile unless overridden with `--profile`. You can also use `--default` with `cpln profile create` or `cpln profile update` to set the profile as default in a single command. ### Update a profile Update the organization, GVC, or default status for a profile and set it as the default: ```bash theme={null} cpln profile update --org --gvc --default ``` ### Clear default context Remove the default organization or GVC from a profile by passing an empty string: ```bash Remove default org theme={null} cpln profile update --org "" ``` ```bash Remove default GVC theme={null} cpln profile update --gvc "" ``` Removing the default organization from a profile also removes the default GVC, since the GVC is tied to a specific organization. ### Delete a profile ```bash theme={null} cpln profile delete ``` Deleting a profile removes stored credentials. You'll need to re-authenticate to access that account. ## Profile properties Each profile stores authentication and configuration settings that apply to all CLI commands. ### Context properties | Property | Flag | Description | | ------------ | ------- | ---------------------------------------------- | | Organization | `--org` | Default org for all commands | | GVC | `--gvc` | Default GVC for workload and identity commands | ### Output properties | Property | Flag | Description | Default | | ------------- | ---------- | ------------------------------------------------ | ------- | | Output format | `--output` | `text`, `json`, `yaml`, `json-slim`, `yaml-slim` | `text` | | Color | `--color` | Enable colored output (`true`/`false`) | `true` | | Timestamps | `--ts` | Timestamp format: `iso`, `local`, `age` | `age` | | Max records | `--max` | Maximum records to display | `50` | ### Request properties | Property | Flag | Description | Default | | -------- | ------------ | -------------------------------------- | --------------------- | | Token | `--token` | Authentication token | Set via login | | Endpoint | `--endpoint` | API endpoint URL | `https://api.cpln.io` | | Insecure | `--insecure` | Skip TLS verification (`true`/`false`) | `false` | Update any property with: ```bash theme={null} cpln profile update -- ``` ```bash Set output format theme={null} cpln profile update my-profile --output json ``` ```bash Set multiple properties theme={null} cpln profile update my-profile --org my-org --gvc my-gvc --output yaml --color false ``` ## Override context ### Override profile context Override defaults for a single command: ```bash Override profile theme={null} cpln --profile ``` ```bash Override org theme={null} cpln --org ``` ```bash Override GVC theme={null} cpln --gvc ``` ```bash Override multiple theme={null} cpln --profile --org --gvc ``` ## Environment variable overrides Set these environment variables to override profile defaults: * `CPLN_PROFILE` - The profile name that will become the default * `CPLN_TOKEN` - Authentication token * `CPLN_ORG` - Default organization * `CPLN_GVC` - Default GVC ```bash Set profile theme={null} export CPLN_PROFILE=production ``` ```bash Set token theme={null} export CPLN_TOKEN=your-token-here ``` ```bash Set org and GVC theme={null} export CPLN_ORG=my-org export CPLN_GVC=my-gvc ``` ## View profile token Display the token for a profile (useful for debugging or CI/CD setup): ```bash theme={null} cpln profile token ``` Tokens are sensitive credentials. Do not share them or commit them to version control. ## Profile best practices Name profiles by environment or purpose: * `dev`, `staging`, `production` * `org1-prod`, `org2-staging` * `ci`, `local-dev` Reduce the need for `--org` and `--gvc` flags by setting defaults: ```bash theme={null} cpln profile update --org --gvc ``` Always check your active profile before running destructive commands: ```bash theme={null} cpln profile get ``` Look for the profile marked as default with a star `*`. First, set your environment variables: ```bash theme={null} export CPLN_TOKEN= export CPLN_ORG= export CPLN_GVC= ``` Then create a profile and set it as default: ```bash theme={null} cpln profile create ci --default ``` The CLI automatically uses the environment variables for authentication and context. See [CI/CD usage](/cli-reference/ci-cd-development/ci-cd) for details. ## Common pitfalls **Wrong context**: Always verify the active profile before running commands that create or modify resources. Use `cpln profile get` and check the active button. **Service account tokens and GVC**: When creating or updating a profile with `--token` using a service account key, the default GVC on the profile is removed. Always include `--gvc ` on profile update to avoid "GVC required" errors. **Multi-org developers**: Explicitly pass `--org` and `--gvc` when switching contexts in scripts to avoid unintended changes. **CI runners**: Store tokens securely in your CI/CD platform's secret manager. Never hardcode tokens in scripts or configuration files. ## Next steps Learn about all shared CLI flags Configure profiles for automation # Quickstart Source: https://docs.controlplane.com/cli-reference/get-started/quickstart Get started with the Control Plane CLI in minutes. Follow these steps to install, authenticate, and start using the Control Plane CLI. Choose your preferred installation method: Install via npm if you have Node.js **version 18** or later installed. ```bash theme={null} npm install -g @controlplane/cli ``` Requires [Node.js](https://nodejs.org/en/download/) version 18+ (an active LTS release is recommended). For detailed instructions, see [Installation](/cli-reference/installation). Install via Homebrew on macOS or Linux. ```bash theme={null} brew tap controlplane-com/cpln && brew install cpln ``` If you don't have Homebrew installed, follow the [installation instructions](https://brew.sh/). For detailed instructions, see [Installation](/cli-reference/installation). Download from the [Installation page](/cli-reference/installation#binary). Confirm the CLI is installed and accessible: ```bash theme={null} cpln --version ``` You should see the CLI version number. Log in to Control Plane: ```bash theme={null} cpln login ``` The CLI prints a login URL and a six-digit confirmation code, then waits. Open the URL in a browser on any device, sign in, and enter the code. The CLI stores your credentials in a default profile. Use the built-in help to discover commands: ```bash theme={null} cpln --help cpln --help ``` Start with read-only commands like `cpln gvc get` or `cpln workload get`. ## Try your first commands Once authenticated, try these safe, read-only commands: ```bash List GVCs theme={null} cpln gvc get ``` ```bash List workloads in a GVC theme={null} cpln workload get --gvc ``` ```bash List all configured profiles theme={null} cpln profile get ``` ## Deploy your first app Build and deploy a local application to Control Plane: Create a Global Virtual Cloud to host your workload: ```bash theme={null} cpln gvc create --name my-app-gvc --location aws-us-east-1 ``` From your project directory, build and push to your org's private registry: ```bash theme={null} cpln image build --name my-app:v1 --push ``` No Dockerfile? The CLI can auto-detect your language and build with buildpacks. Deploy your image as a workload: ```bash theme={null} cpln workload create --name my-app --gvc my-app-gvc \ --image //image/my-app:v1 --port 8080 --public ``` Replace `8080` with the port your application listens on. Use `--public` to make it accessible from the internet. View your workload status: ```bash theme={null} cpln workload get my-app --gvc my-app-gvc ``` Once ready, access your app at the endpoint shown in the output. For more image operations, see the [Images guide](/cli-reference/get-started/images). ## Next steps Build, push, and manage container images Manage authentication and context Learn about shared CLI flags Use the CLI in automation # Installation Source: https://docs.controlplane.com/cli-reference/installation Install the Control Plane CLI on macOS, Linux, or Windows. The Control Plane CLI (`cpln`) is available via npm, Homebrew, or direct binary download for all major operating systems. ## Choose your installation method Install via npm if you have Node.js **version 16** or later installed. ```bash Install theme={null} npm install -g @controlplane/cli ``` ```bash Update theme={null} npm update -g @controlplane/cli ``` Requires [Node.js](https://nodejs.org/en/download/) version 16+. Install via Homebrew on macOS or Linux. ```bash Install theme={null} brew tap controlplane-com/cpln && brew install cpln ``` ```bash Update theme={null} brew upgrade cpln ``` If you don't have Homebrew installed, follow the [installation instructions](https://brew.sh/). Download and install the binary package for your operating system. Download for Apple Silicon Download for Intel Macs Download for Linux (x64) or for Windows (WSL2) Download for Windows 10/11 ```bash theme={null} curl -LO https://storage.googleapis.com/artifacts.cpln-build.appspot.com/binaries/cpln/2754171261-e11832d5/cpln-macos-arm64.dmg ``` ```bash theme={null} curl -LO https://storage.googleapis.com/artifacts.cpln-build.appspot.com/binaries/cpln/2754171261-e11832d5/cpln-macos-x64.dmg ``` ```bash curl theme={null} curl -LO https://storage.googleapis.com/artifacts.cpln-build.appspot.com/binaries/cpln/2754171261-e11832d5/cpln-linux.tgz ``` ```bash wget theme={null} wget https://storage.googleapis.com/artifacts.cpln-build.appspot.com/binaries/cpln/2754171261-e11832d5/cpln-linux.tgz ``` ```powershell theme={null} Invoke-WebRequest -Uri "https://storage.googleapis.com/artifacts.cpln-build.appspot.com/binaries/cpln/2754171261-e11832d5/cpln-win.zip" -OutFile "cpln-win.zip" ``` ```bash curl theme={null} curl -LO https://storage.googleapis.com/artifacts.cpln-build.appspot.com/binaries/cpln/2754171261-e11832d5/cpln-linux.tgz ``` ```bash wget theme={null} wget https://storage.googleapis.com/artifacts.cpln-build.appspot.com/binaries/cpln/2754171261-e11832d5/cpln-linux.tgz ``` When using WSL2, download the Linux binary, not the Windows binary. ```bash theme={null} hdiutil attach cpln-macos-arm64.dmg && \ cp -R /Volumes/cpln-macos-arm64/cpln /Volumes/cpln-macos-arm64/docker-credential-cpln . && \ hdiutil detach /Volumes/cpln-macos-arm64 && \ sudo mv cpln docker-credential-cpln /usr/local/bin/ ``` You'll be prompted to enter your password. The `sudo` command requires administrator privileges to move files to `/usr/local/bin`. ```bash theme={null} hdiutil attach cpln-macos-x64.dmg && \ cp -R /Volumes/cpln-macos-x64/cpln /Volumes/cpln-macos-x64/docker-credential-cpln . && \ hdiutil detach /Volumes/cpln-macos-x64 && \ sudo mv cpln docker-credential-cpln /usr/local/bin/ ``` You'll be prompted to enter your password. The `sudo` command requires administrator privileges to move files to `/usr/local/bin`. ```bash theme={null} tar -xvf cpln-linux.tgz && \ sudo mv cpln docker-credential-cpln /usr/local/bin/ ``` You'll be prompted to enter your password. The `sudo` command requires administrator privileges to move files to `/usr/local/bin`. 1. Right-click `cpln-win.zip` and select **Extract All** 2. Choose a permanent location (e.g., `C:\Program Files\cpln`) 3. Add to PATH: * Press `Win + R`, type `sysdm.cpl`, and press Enter * Click the **Advanced** tab → **Environment Variables** * Under "User variables", select **Path** and click **Edit** * Click **New** and add the directory containing `cpln.exe` (e.g., `C:\Program Files\cpln`) * Click **OK** to save all dialogs 4. Open a **new** Command Prompt or PowerShell window to use `cpln` ```bash theme={null} tar -xvf cpln-linux.tgz && \ sudo mv cpln docker-credential-cpln /usr/local/bin/ ``` You'll be prompted to enter your password. The `sudo` command requires administrator privileges to move files to `/usr/local/bin`. **Logging in from WSL2:** No browser is needed inside WSL2. `cpln login` prints a login URL and a confirmation code, so you can paste the URL into your Windows browser, sign in, and enter the code. The CLI running in WSL2 picks up the credentials on its own. | OS | File | SHA256 | | ------------- | -------------------- | ---------------------------------------------------------------- | | macOS-arm64 | cpln-macos-arm64.dmg | 58dffee7a0b3d78a848449ad39b5f1c7e991229020d4e8c525543b7e7fbd20c1 | | macOS-x64 | cpln-macos-x64.dmg | 861bff72825f6594fa6e156b96454aed4dc1ef158058437bdd3d263d5d3c23eb | | Linux | cpln-linux.tgz | e83eb8573706c8adbfbf93e0e6907c0969853de1399aa85fa502a83350399378 | | Windows 10/11 | cpln-win.zip | 992962dd2cb7f5e2c77bd539bf1166004227079f0a7321e9c126b4f3742ecd79 | The binary package includes two executables: * `cpln` - The Control Plane CLI * `docker-credential-cpln` - Enables Docker authentication to your org's private image registry ## Verify installation After installing with any method, confirm the CLI is installed and accessible: ```bash theme={null} cpln --version ``` You should see the version number printed to the console. If you see `command not found`, ensure the installation directory is in your `PATH`. `cpln version` and `cpln -v` are aliases for `cpln --version`. ## Keep the CLI up to date Run `cpln upgrade` to upgrade the CLI to the latest version. ## Enable shell completion (optional) Enable tab completion for `cpln` commands, subcommands, and flags in your terminal: ```bash theme={null} cpln misc install-completion ``` Restart your terminal after installation. For non-interactive setup and supported shells, see [Shell Completion](/cli-reference/using-cli/shell-completion). ## Next steps Get started with the CLI in minutes Learn how to authenticate # CLI Reference Source: https://docs.controlplane.com/cli-reference/overview Comprehensive guide to the Control Plane CLI for managing infrastructure, workloads, and resources. The Control Plane CLI (`cpln`) provides a powerful command-line interface for managing organizations, GVCs, workloads, and all supporting resources. Nearly everything you can do through the Console UI, API, or Terraform can also be done through the CLI. The CLI also provides powerful operations not available elsewhere, such as port forwarding, executing commands in containers, and converting Kubernetes manifests. Working with an AI assistant? The [Control Plane AI Plugin](/ai/plugin) teaches Claude Code, Codex, and Antigravity CLI how to drive `cpln` correctly — including the resource command map, hallucination traps, and the multi-step workflows that the CLI doesn't bundle into single commands. ## Get started Install via npm, Homebrew, or binary download Get up and running in minutes Log in with interactive or token-based flows Manage multiple environments and contexts ## Quick reference ```bash theme={null} npm install -g @controlplane/cli ``` ```bash theme={null} brew tap controlplane-com/cpln && brew install cpln ``` Download from the [Installation page](/cli-reference/installation). ```bash theme={null} cpln --version ``` ```bash theme={null} cpln login ``` The CLI prints a login URL and a confirmation code to complete sign-in from any browser. For unattended environments, see [Authentication](/cli-reference/get-started/authentication). ```bash theme={null} cpln --help cpln --help ``` Browse all commands in the **Commands** section. ## Using the CLI Use built-in help to discover commands and flags Shared flags across all CLI commands Control command output with JSON, YAML, and more Common issues and solutions Enable tab completion for commands and flags Deploy apps, manage secrets, debug workloads, and more ## CI/CD & automation Automate deployments with service accounts Run the CLI inside Docker containers ## CLI-only features These operations are unique to the CLI and provide capabilities not available in the Console or API: GitOps-style resource management from YAML/JSON files Convert Kubernetes manifests to Control Plane format Deploy Helm charts directly to Control Plane Deploy Docker Compose applications to Control Plane Manage the Kubernetes operator for Control Plane Forward local ports to workloads for debugging Copy files to and from workload containers Execute commands inside running containers Open an interactive shell in a workload container Push images to Control Plane registry Pull images from Control Plane registry Copy images between registries or orgs Dive into all CLI-only features in the **Guides** section for detailed walkthroughs and examples. ## Common workflows Learn by example with these common CLI workflows: These examples assume you have a [profile configured](/cli-reference/get-started/profiles) with a default org set. Build and push your local application to Control Plane's image registry: ```bash bash theme={null} # Create a GVC cpln gvc create --name my-app-gvc --location aws-us-east-1 # Build and push your image cpln image build --name my-app:v1.0.0 --push # Create a workload using your image cpln workload create \ --name my-app \ --gvc my-app-gvc \ --image //image/my-app:v1.0.0 \ --port 8080 \ --public # View workload status cpln workload get my-app --gvc my-app-gvc ``` The `--port 8080` must match the port your application exposes in its Dockerfile. Change this value to match your application's exposed port. Deploy using a public image from Docker Hub or another registry: ```bash bash theme={null} # Create a GVC cpln gvc create --name my-app-gvc --location aws-us-east-1 # Create a workload with a public image cpln workload create \ --name my-app \ --gvc my-app-gvc \ --image nginx:latest \ --port 80 \ --public # View workload status cpln workload get my-app --gvc my-app-gvc ``` See [Create Workload](/cli-reference/commands/workload#workload-create) for detailed steps. ```bash bash theme={null} # Export existing resources cpln gvc get my-gvc --output yaml-slim > gvc.yaml cpln workload get my-app --gvc my-gvc --output yaml-slim > workload.yaml # Edit files, then apply cpln apply --file gvc.yaml cpln apply --file workload.yaml --gvc my-gvc ``` See [cpln apply](/guides/cpln-apply) for GitOps workflows. Create a secret, grant a workload identity access to it via a policy, and inject it as an environment variable. ```bash bash theme={null} # Create a file containing the secret value echo "hello-world" > my-db-password.txt # Create an opaque secret from the file cpln secret create-opaque --name db-password \ --encoding plain \ --file my-db-password.txt # Create a GVC cpln gvc create --name my-gvc --location aws-us-east-1 # Create an identity for your workload cpln identity create --name my-app-identity --gvc my-gvc # Create a policy targeting the secret cpln policy create --name my-app-secrets-policy \ --target-kind secret \ --resource db-password # Add a binding to grant the identity reveal permission cpln policy add-binding my-app-secrets-policy \ --identity //gvc/my-gvc/identity/my-app-identity \ --permission reveal # Create a workload with the identity attached and inject the secret as an env var cpln workload create \ --name my-app \ --gvc my-gvc \ --image nginx:latest \ --port 80 \ --identity my-app-identity \ --env DB_PASSWORD=cpln://secret/db-password.payload \ --public # Check workload status (and wait for the workload to become ready) cpln workload get my-app --gvc my-gvc # Verify the secret is available in the container cpln workload exec my-app --gvc my-gvc -- env | grep DB_PASSWORD # Expected output from the cpln workload exec command Defaulting to location 'aws-us-east-1' because no location was specified in the command. Defaulting to replica 'my-app-00001-deployment-6d67547b5d-586d9' because no replica was specified in the command. Defaulting to container 'nginx' because no container was specified in the command. DB_PASSWORD=hello-world ``` See [Policy](/reference/policy) for access control. ```bash bash theme={null} # Build and push cpln image build --name my-app:v1.0.0 --push # Or build remotely, without local Docker cpln image build --name my-app:v1.0.0 --remote # Update workload to use new image (replace # with the acutal container name from your workload) cpln workload update my-app --gvc my-gvc \ --set spec.containers..image=//image/my-app:v1.0.0 ``` See [Push Image](/guides/push-image) for details. ```bash theme={null} # View logs cpln logs '{gvc="my-gvc", workload="my-app"}' # Execute commands in a container cpln workload exec my-app --gvc my-gvc -- ls -a # Forward a port for local testing cpln port-forward my-app 8080:8080 --gvc my-gvc # Copy files from a workload (assuming your workload is using nginx:latest image) cpln cp my-app:usr/share/nginx/html/index.html ./index.html --gvc my-gvc ``` See [Workload Exec](/guides/cli/workload/exec) and [Port Forward](/guides/cli/cpln-port-forward). ## Command categories The CLI organizes commands by resource type. Explore the **Commands** section for detailed reference on each command. ### Infrastructure Global Virtual Clouds Available locations Organizations ### Workloads & compute Manage workloads Managed Kubernetes Persistent volumes ### Images & containers Container images Docker Compose stacks ### Security & access Secrets management Access policies Workload identities ### Users & accounts User accounts User groups Service accounts ### Networking & domains Custom domains IP sets for firewall rules ### Infrastructure as code Create/update from files Delete from files Convert K8s to Control Plane ### Operations & utilities View logs Forward ports to workloads Copy files to/from workloads Manage Helm releases ### Cloud & integrations Cloud provider accounts Control Plane agents Audit contexts ### System & admin CLI profiles Authentication Resource quotas Pending tasks Miscellaneous utilities Direct REST API access ## Additional resources REST API documentation Core platform concepts Infrastructure as code with Terraform ## Get help Use `cpln --help` to explore commands Solutions to common issues # Release Notes Source: https://docs.controlplane.com/cli-reference/release-notes Version history and changelog for the Control Plane CLI (cpln), including new features, improvements, and bug fixes. This page will be updated regularly. Check back often for the latest features, updates, and changes. ## Latest Versions * CLI * Date: August 12, 2026 * Version Info * npm: **3.16.0** * [Installation Instructions](/cli-reference/installation) ## CLI v3.16.0 Release * Date: August 12, 2026 * Version Info * npm: 3.16.0 * **Minor Changes** * Add `--build-arg` to [cpln image build](/cli-reference/commands/image#image-build) to set build-time variables, as `--build-arg NAME=value`, or `--build-arg NAME` to take the value from your environment. Repeat the option for each arg. It applies to local and `--remote` builds alike, and a build with no Dockerfile warns that the args were not consumed, since buildpack builds ignore them. * Add `--secret` to [cpln image build](/cli-reference/commands/image#image-build) to expose a secret to a Dockerfile build, in the form `--secret id=[,src=]`. `src` takes a local file path, or `cpln://secret/[.]` to name a secret in your org, which the CLI reveals for the build alone and never bakes into the image. A `--remote` build accepts only `cpln://secret/...` sources. Buildpack builds cannot consume secrets, so the option requires a Dockerfile. * Add `--dry-run` to the commands that write a resource: `create`, `update`, `patch`, `edit`, `clone`, `tag`, [cpln apply](/cli-reference/commands/apply), and the subcommands that modify a resource, such as `gvc add-location`, `group add-member`, `policy add-binding`, and `workload start`. The platform validates the request and returns the resource that would be written, and nothing is stored. Output defaults to `yaml-slim`, so the result reads as a manifest; pass `--output` for another format. On `cpln apply`, `--dry-run` cannot be combined with `--ready`, since nothing is created to wait for. * Upgrade a standalone macOS install in place with [cpln upgrade](/cli-reference/commands/upgrade), which now installs the signed and notarized disk image for Intel and Apple silicon instead of printing manual instructions. * Recognize bun and yarn berry installs in [cpln upgrade](/cli-reference/commands/upgrade): a global bun install upgrades with `bun add -g`, and a project dependency installed by yarn berry is no longer read as a global install. The new-version notice now closes with the step that fits the install, pointing a project dependency at its `package.json` instead of at `cpln upgrade`. * **Patch Changes** * Stop [cpln upgrade](/cli-reference/commands/upgrade) before it downloads a release when the install directory cannot be written to, and name the way out for the platform: re-run with `sudo`, or from a terminal opened with "Run as administrator" on Windows. * Clear the scratch files a binary upgrade can leave in the install directory on Windows, where the executable in use cannot be deleted and is moved aside instead. ## CLI v3.15.0 Release * Date: July 27, 2026 * Version Info * npm: 3.15.0 * **Minor Changes** * Add `--remote` to [cpln image build](/cli-reference/commands/image#image-build) to build remotely instead of through local Docker. The CLI uploads the `--dir` folder, Control Plane detects how to build the source, and the image is pushed to the org's private registry, so neither a Docker daemon nor `--push` is needed. Build logs stream to the terminal, and interrupting the command leaves the build running remotely. * Build straight from a repository with `cpln image build --remote --repo `, optionally pinned to a `--branch`. GitHub and GitLab are supported; the first build of a private repository opens a browser once to connect the org's git provider. * Add `--detach` to [cpln image build](/cli-reference/commands/image#image-build) to start a remote build and return without following it. The image appears in `cpln image get` once the build pushes it. * Open the browser automatically during [cpln login](/cli-reference/commands/login), while still printing the sign-in URL as a fallback for headless environments. * **Patch Changes** * Keep digit-shaped values as typed in [cpln image build](/cli-reference/commands/image#image-build) and [cpln cp](/cli-reference/commands/cp), so an image tag, branch, directory, or container name such as `1.10` no longer loses its trailing zero. ## CLI v3.14.1 Release * Date: July 20, 2026 * Version Info * npm: 3.14.1 * **Patch Changes** * Fix [cpln workload run](/cli-reference/commands/workload#workload-run) failing on a healthy deployment that is slow to become ready — a transient platform message is no longer read as a stalled deployment. Pass `--verbose` to see the deployment status while it waits. ## CLI v3.14.0 Release * Date: July 20, 2026 * Version Info * npm: 3.14.0 * **Minor Changes** * Add `cpln metrics query ` to run a PromQL query against your organization's metrics. Runs an instant query by default; pass `--from`, `--to`, `--since`, and `--step` for a range query, or `--time` to evaluate at a specific moment. * Add `cpln upgrade` (alias `cpln update`) to update the CLI in place. It detects whether the CLI was installed via npm, Homebrew, or a standalone binary and runs the matching upgrade. Pass `--check` to compare the installed and latest versions without upgrading, or `--force` to reinstall the latest even when already up to date. * Add a `cpln version` command that prints the installed CLI version — the same output as `cpln --version` — and add `-v` as a short alias for `--version`. * Accept relative durations in the `--from` and `--to` flags of [cpln logs](/cli-reference/commands/logs), in addition to absolute ISO 8601 timestamps. For example, `--from 7d`, `--from now-30m`, or `--to 1h`. * Add `--name` to [cpln workload cron run](/cli-reference/commands/workload#workload-cron-run) to run against a runner workload with an exact name, creating it if needed, instead of the generated `cpln-run-*` runner. A stable name can be referenced elsewhere, such as a called service's internal firewall allowlist. * Honor `envFrom` in the [K8s Converter](/guides/cli/cpln-convert#environment-variables), expanding each ConfigMap and Secret key into an environment variable. * Infer `firewallConfig.internal.inboundAllowType: same-gvc` for converted workloads backed by a Kubernetes [Service](/guides/cli/cpln-convert#firewall-and-public-exposure). * Add the [`cpln.io/workload-type`, `cpln.io/inherit-env`, and `cpln.io/internal-inbound-allow-type` annotations](/guides/cli/cpln-convert#conversion-annotations) to control conversion. * Convert `ReadWriteMany`/`ReadOnlyMany` PVCs to [shared volumesets](/guides/cli/cpln-convert#how-volumesets-are-configured), mountable by any workload type. Existing workloads from an older `ReadWriteMany`/`ReadOnlyMany` conversion keep their dedicated storage, since the file system type and workload type are immutable; the shared conversion applies to newly created workloads. * Hint at the `cpln.io/workload-type` annotation when `cpln apply --k8s` fails with `Workload type may not be changed`. * **Patch Changes** * Fail [cpln workload run](/cli-reference/commands/workload#workload-run) fast when a deployment stalls before becoming ready — for example, when it is paused because a referenced secret cannot be revealed — aborting with the platform's reason instead of waiting for the readiness poll to time out. Terminal sessions now also exit non-zero on a transport or backend session error instead of reporting success. * Retry [cpln image build --push](/cli-reference/commands/image#image-build) and [cpln image copy](/cli-reference/commands/image#image-copy) on transient proxy and connection-closed errors before giving up. Permanent failures such as bad credentials or a missing image still fail immediately. * Fix base64 secret `data` not being decoded when merged with `stringData`. * Surface K8s conversion warnings on `cpln convert -o yaml` and on failed `cpln apply --k8s`. ## CLI v3.13.0 Release * Date: July 14, 2026 * Version Info * npm: 3.13.0 * **Minor Changes** * Switch [cpln login](/cli-reference/commands/login) to a device-code flow. The command prints a sign-in URL and a confirmation code to enter in any browser, so logging in works from SSH sessions, containers, and other headless environments. * Add `request` to [cpln quota](/cli-reference/commands/quota) to manage quota increase requests: `cpln quota request create --requested-max ` submits one, `cpln quota request get ` shows its status, and `cpln quota request list [--status pending|approved|denied]` reviews them. Requests within your account's boundary are approved automatically; the rest await administrator approval. * **Patch Changes** * Fix [cpln sandbox connect](/cli-reference/commands/sandbox#sandbox-connect) SSH authentication against de-rooted [sandbox](/guides/sandbox/getting-started) images by connecting as `devuser` instead of `root`. ## CLI v3.12.0 Release * Date: July 3, 2026 * Version Info * npm: 3.12.0 * **Minor Changes** * Add `cpln sandbox connect` command to connect to a running [sandbox](/guides/sandbox/getting-started) over an SSH tunnel using VS Code, Cursor, SSH, the browser IDE, or a terminal (`--ide vscode|cursor|ssh|browser|terminal`). * **Patch Changes** * Restore the terminal's stdin state after interactive exec sessions end. ## CLI v3.11.0 Release * Date: June 12, 2026 * Version Info * npm: 3.11.0 * **Minor Changes** * Add `health` subcommand to [cpln mk8s](/cli-reference/commands/mk8s) to show the health status of an mk8s cluster. * Determine workload readiness from the workload health object instead of deployments. * **Patch Changes** * Fix [cpln login](/cli-reference/commands/login) popup authentication being blocked by Chrome's Cross-Origin-Opener-Policy (COOP). * Fix `--publish` option handling in the [cpln agent up](/cli-reference/commands/agent#agent-up) command. * Fix [cpln port-forward](/cli-reference/commands/port-forward) crash when the IPv6 loopback address cannot be bound. * Upgrade dependencies to resolve known security vulnerabilities and the `uuid` deprecation warning. ## CLI v3.10.2 Release * Date: May 6, 2026 * Version Info * npm: 3.10.2 * **Patch Changes** * Fix --arg and --header dropping option-shaped values ## CLI v3.10.1 Release * Date: April 3, 2026 * Version Info * npm: 3.10.1 * **Patch Changes** * Upgraded dependencies to resolve known security vulnerabilities. * Add pagination to logs command to fetch beyond server limit. * Fix ConfigMap and Secret name collision in k8s-converter. ## CLI v3.10.0 Release * Date: March 16, 2026 * Version Info * npm: 3.10.0 * **Minor Changes** * Add `--identity` flag to [cpln workload cron run](/cli-reference/commands/workload#workload-cron-run) subcommand, when given a new cron workload with that identity will be created or an existing one that has it will be used. * Add predictive autoscaling to volumeset [create](/cli-reference/commands/volumeset#volumeset-create) and [update](/cli-reference/commands/volumeset#volumeset-update) commands. * Add [cpln volumeset shrink](/cli-reference/commands/volumeset#volumeset-shrink) command. * Add `schedule-name` and `active-deadline-seconds` args to [cpln workload cron start](/cli-reference/commands/workload#workload-cron-start) command. ## CLI v3.9.1 Release * Date: February 11, 2026 * Version Info * npm: 3.9.1 * **Patch Changes** * Revert [cpln workload run](/cli-reference/commands/workload#workload-run) to original standard workload-based execution * Move cron-based run logic to new [cpln workload cron run](/cli-reference/commands/workload#workload-cron-run) subcommand ## CLI v3.9.0 Release * Date: February 6, 2026 * Version Info * npm: 3.9.0 * **Minor Changes** * Add `audit` subcommand to resource commands (e.g., `cpln workload audit`, `cpln gvc audit`) to retrieve [audit trail](/core/audittrail) events. * Refactor shell completion system to remove `tabtab` dependency. * Refactor [cpln workload run](/cli-reference/commands/workload#workload-run) to use a persistent suspended cron workload. * Add `--unset` flag to the update command for removing properties and named array items. * Add `requestRetryPolicy` and `extras` paths to [cpln workload update](/cli-reference/commands/workload#workload-update) with JSON support. * Add `--timeoutSeconds` option to [cpln volumeset expand](/cli-reference/commands/volumeset) command. * Add pagination warning when more records are available beyond the default limit, guiding users to use `--max 0` to fetch all records. * Add context-aware error message when the active profile points to a non-existent profile configuration. * Add Domain conversion to [K8s Converter](/cli-reference/commands/convert). * Change default workload type to `standard` in create and convert commands. Before v3.9.0, the K8s Converter produced a mix of `serverless` and `standard` workloads; from v3.9.0 on, every converted Deployment is `standard` unless a stateful condition applies. The workload type is immutable, so re-running `cpln apply --k8s` against workloads created by an older CLI can fail with `Workload type may not be changed` (405). Pin the type in the K8s manifest with the [`cpln.io/workload-type` annotation](/guides/cli/cpln-convert#conversion-annotations) to keep it stable across CLI versions. * Deprecate `--remove` flag in favor of `--remove-tag` for tag commands. * Show `Now` instead of `0 minutes ago` for recent timestamps. * Fix incorrect `--rel` flag description in query options. * **Patch Changes** * Fix Helm release status not being marked as failed when resource link resolution fails, which could leave releases stuck in `pending-install` or `pending-upgrade` status. * Fix error formatting to display network errors (e.g., certificate failures) instead of showing an empty object. * Fix numeric environment variable values breaking [cpln helm install](/cli-reference/commands/helm#helm-install). * Fix tilde (`~`) expansion in user-provided file paths for commands like [cpln agent up](/cli-reference/commands/agent#agent-up). * Fix [cpln login](/cli-reference/commands/login) blank page in binary distributions. * Add fallback to legacy `docker build` when `docker buildx` is unavailable in [cpln image build](/cli-reference/commands/image#image-build). ## CLI v3.8.2 Release * Date: January 16, 2026 * Version Info * npm: 3.8.2 * **Patch Changes** * Fixed binary startup issues affecting cpln and docker-credential-cpln on macOS and other platforms. ## CLI v3.8.1 Release * Date: January 15, 2026 * Version Info * npm: 3.8.1 * **Patch Changes** * Fixed an issue where the cpln binary would fail to start on macOS and other platforms. ## CLI v3.8.0 Release * Date: January 15, 2026 * Version Info * npm: 3.8.0 * **Minor Changes** * Add `buildpack` / `b`, `env`, `envFile`, `trust-builder` and `trust-extra-buildpacks` args to [cpln image build](/cli-reference/commands/image#image-build) command for building an image with [Cloud Native Buildpacks](https://buildpacks.io). * Add `--platform` arg to [cpln image build](/cli-reference/commands/image#image-build) command. * Auto-detect Dockerfile from --dir directory in [cpln image build](/cli-reference/commands/image#image-build) command. * Fix package vulnerabilities. ## CLI v3.7.5 Release * Date: December 10, 2025 * Version Info * npm: 3.7.5 * **Patch Changes** * Enforce history limit post‑success and prune to configured depth in [cpln helm](/cli-reference/commands/helm) command. ## CLI v3.7.4 Release * Date: December 4, 2025 * Version Info * npm: 3.7.4 * **Patch Changes** * Update buildpacks builder to build using platform `linux/amd64`. ## CLI v3.7.3 Release * Date: December 4, 2025 * Version Info * npm: 3.7.3 * **Patch Changes** * Update buildpacks to `0.39.0`. ## CLI v3.7.2 Release * Date: November 10, 2025 * Version Info * npm: 3.7.2 * **Patch Changes** * [cpln image build](/cli-reference/commands/image#image-build) now uses `docker buildx build`. ## CLI v3.7.1 Release * Date: October 22, 2025 * Version Info * npm: 3.7.1 * **Patch Changes** * Update agent default image. * Read `x-cpln` stanza in docker-compose to override and extend workload spec. * Assume port protocol from port number in [cpln convert](/cli-reference/commands/convert) command. ## CLI v3.7.0 Release * Date: September 12, 2025 * Version Info * npm: 3.7.0 * **Major Changes** * A chart path is now required when running any of the following commands: * [cpln helm template](/cli-reference/commands/helm#helm-template) * [cpln helm install](/cli-reference/commands/helm#helm-install) * [cpln helm upgrade](/cli-reference/commands/helm#helm-upgrade) * **Minor Changes** * Add `--repo` flag to install/upgrade helm charts from OCI registries or Helm repositories. Refer to the [cpln helm](/guides/cpln-helm#install-from-remote-repositories) guide for more info. * Add `--install` flag to [cpln helm upgrade](/cli-reference/commands/helm#helm-upgrade) to automatically create and install a release with the given name if not present. * **Patch Changes** * Enforce validation so non-CPLN resources cause install or upgrade to fail. * Fix Helm notes rendering issue. * Update default image for [cpln workload run](/cli-reference/commands/workload#workload-run) command to ubuntu:latest. * Allow bare keys in `.env` to default to empty values in [cpln stack](/cli-reference/commands/stack) command. ## CLI v3.6.1 Release * Date: August 20, 2025 * Version Info * npm: 3.6.1 * **Patch Changes** * Fix resource not found during helm release migration to version 4. ## CLI v3.6.0 Release * Date: August 20, 2025 * Version Info * npm: 3.6.0 * **Minor Changes** * Add `--dependency-update` to [cpln helm](/cli-reference/commands/helm) commands. * **Patch Changes** * Fix typos and grammar mistakes. * Fix the displayed release name on error if user attempts to override a resource of a different release in [cpln helm](/cli-reference/commands/helm). * Fix [cpln helm](/cli-reference/commands/helm) tag handling to persist only tags specified with --tag option and drop removed/unwanted ones. * Return a non-zero code if a backend error occurred in [cpln workload exec](/cli-reference/commands/workload#workload-exec) or [cpln workload connect](/cli-reference/commands/workload#workload-connect) commands. * Fix a [cpln port-forward](/cli-reference/commands/port-forward) local server error for packaged binaries. * Fix [K8s Converter](/cli-reference/commands/convert) Logging service 400 status on submit with refresh token. * Add public exposure detection to workload firewall in K8s [K8s Converter](/cli-reference/commands/convert). * Fix command array parsing to correctly assign extra items to args in [K8s Converter](/cli-reference/commands/convert). * Prevent `subPath` to be appended to mount path during [K8s conversion](/cli-reference/commands/convert). ## CLI v3.5.6 Release * Date: July 11, 2025 * Version Info * npm: 3.5.6 * **Patch Changes** * Validate org name after parsing service account token during profile update. * Decode docker secret data if it is in base64 format. * Handle securityContext in K8s converter. * Decode dictionary secret values if the data is base64. * Fix helm list max limit being applied before processing revisions. * Show the helm release name not the secret name if the resource doesn't belong to the release being installed. * Add cpln workload cron stop to be an alias of replica stop command. ## CLI v3.5.5 Release * Date: June 6, 2025 * Version Info * npm: 3.5.5 * **Patch Changes** * Fix issue where the [cpln port-forward](/cli-reference/commands/port-forward) command session disconnects after 1 hour. ## CLI v3.5.4 Release * Date: June 5, 2025 * Version Info * npm: 3.5.4 * **Patch Changes** * Fix issue where the workload's ready status was not displayed correctly when running the [cpln workload get](/cli-reference/commands/workload#workload-get) command. ## CLI v3.5.3 Release * Date: May 23, 2025 * Version Info * npm: 3.5.3 * **Patch Changes** * Fix issue where Helm release was uninstalled on [cpln helm upgrade](/cli-reference/commands/helm#helm-upgrade) when history limit was reached. ## CLI v3.5.2 Release * Date: May 9, 2025 * Version Info * npm: 3.5.2 * **Patch Changes** * Add --state-tag argument to [cpln helm install](/cli-reference/commands/helm#helm-install) & [cpln helm upgrade](/cli-reference/commands/helm#helm-upgrade). * Fix tags in helm release resources during [cpln helm install](/cli-reference/commands/helm#helm-install) & [cpln helm upgrade](/cli-reference/commands/helm#helm-upgrade). ## CLI v3.5.1 Release * Date: May 5, 2025 * Version Info * npm: 3.5.1 * **Patch Changes** * Hot fix for [cpln image build](/cli-reference/commands/image#image-build) command. ## CLI v3.5.0 Release * Date: May 5, 2025 * Version Info * npm: 3.5.0 * **Minor Changes** * Add YAML document separator for CRD list items. * Separate helm release deployments into individual secrets. * Prevent Helm release upgrades via [cpln helm install](/cli-reference/commands/helm#helm-install); use [cpln helm upgrade](/cli-reference/commands/helm#helm-upgrade) instead. * Add a limit to the release name length. * Add [cpln helm get all](/cli-reference/commands/helm#helm-get-all) subcommand. * Add [cpln helm get notes](/cli-reference/commands/helm#helm-get-notes) subcommand. ## CLI v3.4.5 Release * Date: April 14, 2025 * Version Info * npm: 3.4.5 * **Patch Changes** * Add K8s CRD as an output option. (`--output crd`) * Fix agent default image. * Map health probe port to the number defined by the container port name in the [cpln convert](/cli-reference/commands/convert) command. * Limit concurrent streams for the [cpln cp](/cli-reference/commands/cp) command. * Measure upload/download speed instantaneously in the [cpln cp](/cli-reference/commands/cp) command. * Allow rollback to deployed deployment in the [cpln helm rollback](/cli-reference/commands/helm#helm-rollback) command. * Remove unnecessary apostrophe at the end of the applied template link in the [cpln helm](/cli-reference/commands/helm) command. * Avoid applying a resource with its GVC property in the [cpln helm](/cli-reference/commands/helm) command. * Remove the requirement for the 'type' property within a K8s Secret in the [cpln convert](/cli-reference/commands/convert) command. * Ensure unique names for PVCs defiend inside a StatefulSet workload in the [cpln convert](/cli-reference/commands/convert) command. ## CLI v3.4.4 Release * Date: March 28, 2025 * Version Info * npm: 3.4.4 * **Patch Changes** * Add [cpln operator](/cli-reference/commands/operator) command. * Fix broken agent image issue affecting [cpln agent up](/cli-reference/commands/agent#agent-up) command. * Disable remote port validation in the [cpln port-forward](/cli-reference/commands/port-forward) command. * Convert CPU and memory requests when no limits are specified in the Kubernetes converter. * Add `CPLN_SKIP_UPDATE_CHECK` environment variable to optionally bypass automatic update checks. * Fix typo for the defaulted location in the [cpln connect](/cli-reference/commands/workload#workload-connect) / [cpln exec](/cli-reference/commands/workload#workload-exec) commands. * Change [cpln helm](/cli-reference/commands/helm) output to always display full links. ## CLI v3.4.3 Release * Date: March 17, 2025 * Version Info * npm: 3.4.3 * **Patch Changes** * Update the [cpln port-forward](/cli-reference/commands/port-forward) command to correctly handle refresh tokens. ## CLI v3.4.2 Release * Date: March 14, 2025 * Version Info * npm: 3.4.2 * **Patch Changes** * Submit a ticket to record a one-time audit trail entry for the [cpln port-forward](/cli-reference/commands/port-forward) command. * Automatically connect to a different replica for each session when no specific replica is provided to the [cpln port-forward](/cli-reference/commands/port-forward) command. ## CLI v3.4.1 Release * Date: February 28, 2025 * Version Info * npm: 3.4.1 * **Patch Changes** * Improve [cpln port-forward](/cli-reference/commands/port-forward) command. ## CLI v3.4.0 Release * Date: February 25, 2025 * Version Info * npm: 3.4.0 * **Minor Changes** * Add [cpln port-forward](/cli-reference/commands/port-forward) command. ## CLI v3.3.2 Release * Date: February 17, 2025 * Version Info * npm: 3.3.2 * **Patch Changes** * Add `--build` option for [stack](/cli-reference/commands/stack) commands. ## CLI v3.3.1 Release * Date: January 23, 2025 * Version Info * npm: 3.3.1 * **Patch Changes** * Convert user email characters to lowercase when inviting users. * Fix referencing existing secrets within workloads in Helm. ## CLI v3.3.0 Release * Date: January 1, 2025 * Version Info * npm: 3.3.0 * **Minor Changes** * Add `loadBalancer` to cpln workload update command. * Add `shared` to volume set create command. * Add ipset command. * In cpln helm, avoid applying GVC related resources if GVC does not exist. * In cpln helm, avoid updating unchanged resources. * In cpln helm, introduce `cpln/secret:` tag when applying a Helm chart to update workloads that reference secrets. * Call the K8s converter logging service. * Print and highlight ignored properties of K8s objects when doing `cpln convert` with the `--verbose` option. * Disable nearest year rounding in table format. ## CLI v3.2.1 Release * Date: December 2, 2024 * Version Info * npm: 3.2.1 * **Patch Changes** * Fix yaml output for cpln convert. * Handle tar errors and warnings. * Fix cpln cp for Windows. * Inject more cpln context to helm template. * Convert individual K8s Jobs. ## CLI v3.2.0 Release * Date: November 1, 2024 * Version Info * npm: 3.2.0 * **Minor Changes** * Add 'names' as a new output option. * Enhance [cpln cp](/cli-reference/commands/cp) reliability and performance. * Add progress indicator for [cpln cp](/cli-reference/commands/cp). * Fix priority resource deletion order in [cpln helm uninstall](/cli-reference/commands/helm#helm-uninstall). * Add `--all` option to [cpln helm get values](/cli-reference/commands/helm#helm-get-values). ## CLI v3.1.1 Release * Date: August 26, 2024 * Version Info * npm: 3.1.1 * **Patch Changes:** * Fix a bug causing invalid target links in policies when converting K8s files using the [cpln convert](/cli-reference/commands/convert). * Add tags that are passed to [cpln helm install](/cli-reference/commands/helm#helm-install) to the state as well. ## CLI v3.1.0 Release * Date: August 19, 2024 * Version Info * npm: 3.1.0 * **Major Changes:** * Refactor of [cpln workload exec](/cli-reference/commands/workload#workload-exec) command:The [cpln workload exec](/cli-reference/commands/workload#workload-exec) command has been updated to align with the functionality of [kubectl exec](https://kubernetes.io/docs/reference/kubectl/generated/kubectl_exec/).For more details, refer to the [cpln workload exec guide](/guides/cli/workload/exec). * **Stdin and TTY Handling:** Previously, stdin and TTY were enabled by default. Now, these must be explicitly set: * Use the `-i` option to send stdin to the workload. * Use the `-t` option to treat stdin as TTY. * **Minor Changes:** * Improve [cpln convert](/cli-reference/commands/convert) command to convert ConfigMaps, Persistent Volume Claims and pull secrets. * Add `--encoding` option to [cpln secret create-opaque](/cli-reference/commands/secret#secret-create-opaque) command. * Fix path syntax for [cpln mk8s kubeconfig](/cli-reference/commands/mk8s#mk8s-kubeconfig) command on Windows. * Use the first location of a GVC if the `--location` option is not specified for [cpln workload exec](/cli-reference/commands/workload#workload-exec), [cpln workload connect](/cli-reference/commands/workload#workload-connect) and [cpln cp](/cli-reference/commands/cp) commands. Specifying a location is no longer required. ## CLI v3.0.0 Release * Date: July 22, 2024 * Version Info * npm: 3.0.0 * Notes: * Fix cpln cp. * Allow identity name for policy binding. ## CLI v2.4.0 Release * Date: July 15, 2024 * Version Info * npm: 2.4.0 * Notes: * Use cpln://reference to reference fieldRef for K8s Converter. * Add BYOK location support. * Allow setting profile's default org and gvc to empty. * Optimize the performance of the `cpln helm list` command. * Add `cpln cp` command. * Directly stream stdin to the workload replica when using the `cpln workload exec` and `cpln workload run commands`. ## CLI v2.3.1 Release * Date: June 9, 2024 * Version Info * npm: 2.3.1 * Notes: * Fix stdin null issue on macOS. ## CLI v2.3.0 Release * Date: June 7, 2024 * Version Info * npm: 2.3.0 * Notes: * Query by GVC if --gvc option is specified. * Fix stack capture of command & args. * Retry requests upon hitting rate limits. * Remove maxContentLength limit. * Handle standard input (stdin) in workload exec. * Enhance error handling for Helm deployment API call failures. * Add --no-cache argument to image build command. ## CLI v2.2.2 Release * Date: May 28, 2024 * Version Info * npm: 2.2.2 * Notes: * Notify users of a new CLI version once a day. * Prevent a helm release from managing a resource that belongs to a different release. ## CLI v2.2.1 Release * Date: May 15, 2024 * Version Info * npm: 2.2.1 * Notes: * Add retry to internal API requests. * Helm: Skip resource deletion if specified. ## CLI v2.2.0 Release * Date: May 15, 2024 * Version Info * npm: 2.2.0 * Notes: * Additional k8s converter functionality. ## CLI v2.1.0 Release * Date: April 24, 2024 * Version Info * npm: 2.1.0 * Notes: * Truncate description text if length > 40 characters. * Add alias and update location table view for mk8s. ## CLI v2.0.2 Release * Date: April 1, 2024 * Version Info * npm: 2.0.2 * Notes: * Fix terminal interrupt handlers. * Provide user with a manual delete command to delete workload if workload run fails. * Fix generate name bug in helm. * Modify priority order in order for policy to be created before workload. ## CLI v2.0.1 Release * Date: March 18, 2024 * Version Info * npm: 2.0.1 * Notes: * Add CRON `get` command. * Add fetch pages to CRON get and snapshot get. * Limit cron get and snapshot get output to --max. * Add release notes page to readme. * Add delete and get volume commands. * Enforce valid naming in stack command. * Require location for workload connect/exec command. * Add resize terminal feature to workload run interactive. * Fix protocol override in k8s converter. * Add stop replica command. * Add helm rollback and helm history. * Add helm upgrade command. * Remove clone command from volumeset. * Upgrade default buildpacks to `builder:22`. ## CLI v1.6.0 Release * Date: February 7, 2024 * Version Info * npm: 1.6.0 * Notes: * Print `workload cron start` command output as a table. * Add container override arguments to the `workload cron start` command. * Fix file option to load from stdin when file option is optional. * Update description for: `clone`, `Expand volume set`, `snapshot`. * Fix command snapshot date formats. * For snapshot, Update column name from `Created` to `Snapshot Created`. * Logs: add `-f` as alias to `-t`. * Fix `delete` in `workload run` command. * Add external id to AWS and ECR secrets. * Update dependencies. ## CLI v1.5.4 Release * Date: January 23, 2024 * Version Info * npm: 1.5.4 * Notes: * Add `ACTIVE` column for profiles. * Modify helm list to display release names only. * Print `NOTES.txt` on helm install. * Sort helm template output before applying. * Add helm wait option. * Helm --wait option to wait 5m if --timeout is not provided. * Fix for terminal resizing. * Inject GVC into the helm template command as a value. * Add `volume set` and `snapshot` commands. * Add `cron` and `replica` sub-commands to workload. ## CLI v1.5.2 Release * Date: December 6, 2023 * Version Info * npm: 1.5.2 * Notes: * Fix workload connect prompt length and line wraps. * Improve workload connect prompt issue. * Resizing terminal on the initial connection. * Change login port to 48794. * Add check for latest release. * Add stack command. * Add helm command. * Add --replace flag. ## CLI v1.4.0 Release * Date: November 21, 2023 * Version Info * npm: 1.4.0 * Notes: * Fix prompt length and line wrap when connecting to workload + Improve volumeset deletion. * Force delete command. * Fix SAML login. * Add tags enabled for the `workload run` command. ## CLI v1.3.2 Release * Date: October 30, 2023 * Version Info * npm: 1.3.2 * Notes: * Remove deploy timestamp tag from slimmed objects. * Add account get and org create. * Add token server. ## CLI v1.2.6 Release * Date: October 16, 2023 * Version Info * npm: 1.2.6 * Notes: * Remove deploy timestamp tag from slimmed output. * Connect to first replica if multiple are present. * Add ready flag to apply command. * Add remove tag flag. * Adding useful error messages to auto-completion installation. ## CLI v1.2.5 Release * Date: September 28, 2023 * Version Info * npm: 1.2.5 * Notes: * Fix bug in setting sleep for ubuntu image on run cmd. * Deprecate container port property. Use ports going forward. * Inform user on verbose mode when CPLN\_ORG, CPLN\_GVC, CPLN\_ENDPOINT or CPLN\_PROFILE is being used. * Add mk8s command. * Fix GVC add/remove location commands when resource doesn't contain a location property. * Improved slim output format. * Add recursive folder support for apply command. * Add Terraform output option. * Add retry logic to the workload run command. ## CLI v1.1.0 Release * Date: July 9, 2023 * Version Info * npm: 1.1.0 / Build: 925739349 / Version: 9784f19 * Notes: * Added json-slim and yaml-slim output options. ## CLI v1.0.0 Release * Date: June 27, 2023 * Version Info * npm: 1.0.0 / Build: 912602973 / Version: bddd583 * Notes: * Logs: `maxBodySize` not set when reading. * Inform user when the environment variable `CPLN_TOKEN` is being used for authentication. * Updates to `cpln workload run` command. ## CLI v0.0.90 Release * Date: May 5, 2023 * Version Info * npm: 0.0.90 / Build: 859056052 / Version: b58459f * Notes: * Update HTML on `cpln login` browser page. ## CLI v0.0.82-89 Release * Date: April 21, 2023 * Version Info * npm: 0.0.89 / Build: 844733757 / Version: ba7245d * Notes: * Fix get all workloads from all GVCs. * Update image copy command to use a destination profile. * Workload `run` command. * Allow `--tag` to accept an optional value. ## CLI v0.0.82 Release * Date: March 15, 2023 * Version Info * npm: 0.0.82 / Build: 807750891 / Version: 99792cd * Notes: * Added image copy command. ## CLI v0.0.73-81 Release * Notes: * K8s converter not creating identity and policy if not required. * Added prestop hook for k8s converter. * Added GVC environment variables and container inherit environment variables. * Added active deadline seconds to workload update command. * Fixed token for fetching logs, exec, and connect. * Fixed long websocket connections. * Added SAML login. * Fix workload create for standard workload type. * Added workload ready status to workload get command. * Command: Only enable raw mode if it's TTY. ## CLI v0.0.72 Release * Date: November 7, 2022 * Version Info * npm: 0.0.72 / Build: 687290579 / Version: 718968f * Notes: * Added docker store and erase commands. Fixes issue when using both `cpln image` and `docker login` commands. ## CLI v0.0.71 Release * Date: November 2, 2022 * Version Info * npm: 0.0.71 / Build: 683695375 / Version: 3124c60 * Notes: * Added instructions to create an Azure Connector Cloud Account. * Added delete resource from files command `cpln delete`. * Added k8s manifest to Control Plane manifest converter command `cpln convert`. * Added stop/start/suspend commands to `cpln workload`. * Added `cpln workload exec` command. * [Installation Instructions](/cli-reference/installation) ## CLI v0.0.61 Release * Date: August 30, 2022 * Version Info * npm: 0.0.61 / Build: 626945486 / Version: 2f98cdd * Notes: * Updated agent image version. * Added standard workload. ## CLI v0.0.60 Release * Date: August 24, 2022 * Version Info * npm: 0.0.60 / Build: 621247366 / Version: cd5c35e * Notes: * Updated agent image version. ## CLI v0.0.59 Release * Date: July 28, 2022 * Version Info * npm: 0.0.59 / Build: 599431645 / Version: 4ebdf36 * Notes: * Added `NGS` Cloud Account. * Added `NATS Account` Secret type. * Removed GVC domain. * Upgrade buildpacks to version 0.27.0. ## CLI v0.0.57 Release * Date: May 23, 2022 * Version Info * npm: 0.0.57 / Build: 545699003 / Version: e16bbbf * Notes: * Add default of 50 maximum records. * Fix issue when using a value \< 1 to return all records. * Fix issue when updating container memory. * Add additional set flags for the `workload update` command. ## CLI v0.0.55 Release * Date: February 21, 2022 * Version Info * npm: 0.0.55 / Build: 475892711 / Version: 6b87320 * Notes: * Add --psp flag for agent manifest command to configure a pod security policy. * Enable the k8s agent to run as a privileged pod. ## Previous Versions ## CLI v0.0.54 Release * Date: February 10, 2022 * Version Info * npm: 0.0.54 / Build: 467951861 / Version: 7c17ca8 * Notes: * Fix issues with the k8s Agent. * Fix issue when building images on ARM64 machines. ## CLI v0.0.52 Release * Date: January 20, 2022 * Version Info * npm: 0.0.52 / Build: 452736596 / Version: bffc420 * Notes: * Updated login page. * Fix issue persisting profile default GVC when using the --token flag. ## CLI v0.0.50 Release * Date: December 28, 2021 * Version Info * npm: 0.0.50 / Build: 438151551 / Version: f1586e9 * Notes: * Update pack to version 0.23.0. * Added ARM64 support for pack. ## CLI v0.0.49 Release * Date: November 30, 2021 * Version Info * npm: 0.0.49 / Build: 419000724 / Version: cdc6af5 * Notes: * Fix issue building an image using an ARM processor. ## CLI v0.0.48 Release * Date: November 18, 2021 * Version Info * npm: 0.0.48 / Build: 411017996 / Version: d1b67a5 * Notes: * Updated k8s agent version. ## CLI v0.0.47 Release * Date: November 11, 2021 * Version Info * npm: 0.0.47 / Build: 407265118 / Version: 140e31f * Notes: * Added `cpln agent manifest` command to allow running an agent on a k8s cluster. ## CLI v0.0.46 Release * Date: October 11, 2021 * Version Info * npm: 0.0.46 / Build: 386067508 / Version: 7ca3dd7 * Notes: * Update error handling: * All errors are outputted to stderr. * If the -o flag is set to json or yaml, the output will be in the respective format. If -o is set to text, only the output of the .message property from response object will be outputted. Otherwise, output the error.message property from the exception. * If there are multiple actions, any errors will be included in the output. * The exit code will be 1 if at least one action failed. * Added `force-redeployment` subcommand to the `workload` command. ## CLI v0.0.44 Release * Date: September 10, 2021 * Version Info * npm: 0.0.44 / Build: 368794552 / Version: 6435502 * Notes: * New workload subcommands: * connect: Connect to a replica of the workload. * get-replicas: Get the replicas of the referenced workload in a given location. ## CLI v0.0.43 Release * Date: September 7, 2021 * Version Info * npm: 0.0.43 / Build: 365437960 / Version: f3f59d1 * Notes: * New secret type: azure-connector ## CLI v0.0.42 Release * Date: July 16, 2021 * Version Info * npm: 0.0.42 / Build: 338229644 / Version: db33b86 * Notes: * CLI can work without CPLN\_ENDPOINT * Allow environment variables without a value * Add mount volumes to workload ## CLI v0.0.41 Release * Date: July 14, 2021 * Version Info * npm: 0.0.41 / Build: 336244315 / Version: b864944 * Notes: * Create \~/.docker during `cpln image docker-login` ## CLI v0.0.40 Release * Date: June 30, 2021 * Version Info * npm: 0.0.40 / Build: 329714271 / Version: 4c67950 * Notes: * Infers container name from image name unless defined using the --container-name argument. * Set internal firewall to `same-org` when the `--public` flag is used during the creation of a workload. ## CLI v0.0.39 Release * Date: June 22, 2021 * Version Info * npm: 0.0.39 / Build: 325071193 / Version: acbc925 * Notes: * Added 'smart' reveal to secret output format * Added multi-document sources for the `apply` command ## CLI v0.0.38 Release * Date: June 16, 2021 * Version Info * npm: 0.0.38 / Build: 321518748 / Version: aa315ea * Notes: * Remove account subcommand and references * Update buildpacks to version 0.19.0 * Add `create-ecr` secret command * Fix create command for Cloud Account to include additional error messages * Fix issue when shell would close when running `cpln login` ## CLI v0.0.36 Release * Date: June 7, 2021 * Version Info * npm: 0.0.36 / Build: 315420524 / Version: cb4cb4f * Notes: * For the binary package, include the executable `docker-credential-cpln` in the same archive as the CLI ## CLI v0.0.34 Release * Date: May 31, 2021 * Version Info * npm: 0.0.34 / Build: 312124373 / Version: 0a44b37 * Notes: * Make profile optional if at least --endpoint & --token flags are set * When there is a profile present, honor the environment variables 'CPLN\_ORG', 'CPLN\_GVC', 'CPLN\_ENDPOINT', and 'CPLN\_TOKEN' * Install auto-completion for non-interactive installation ## CLI v0.0.33 Release * Date: May 6, 2021 * Version Info * npm: 0.0.33 / Build: 298694423 / Version: 30dc6d4 * Notes: * Added `task` as a policy target kind * Resume `cpln logs` stream on disconnect * Refresh authentication token before getting logs * Fix for missing Docker config.json file ## CLI v0.0.31 Release * Date: May 1, 2021 * Version Info * npm: 0.0.31 / Build: 295632825 / Version: 01e4461 * Notes: * Added `cpln agent info` command ## CLI v0.0.30 Release * Date: April 27, 2021 * Version Info * npm: 0.0.30 / Build: 293303600 / Version: 81bde51 * Notes: * Fix for the closing of a command or PowerShell shell in Windows 10 if the `cpln login` command is executed. * Updated axios to version 0.21.1 # Common Options Source: https://docs.controlplane.com/cli-reference/using-cli/common-options Shared flags and options available across Control Plane CLI commands. These options are available on most CLI commands and help you control context, output, connectivity, and behavior. ## Context and profiles Control which profile, organization, and GVC the command uses. ### `--profile` Override the default profile for a single command. ```bash theme={null} cpln --profile ``` Set a default profile with `cpln profile set-default ` or use the `CPLN_PROFILE` environment variable. ### `--org` Override the default organization. ```bash theme={null} cpln --org ``` Set a default org in your profile with `cpln profile update --org `. ### `--gvc` Override the default Global Virtual Cloud. ```bash theme={null} cpln --gvc ``` Set a default GVC in your profile with `cpln profile update --gvc `. ## Output and formatting Control how command results are displayed. ### `--output` or `-o` Set the output format. ```bash theme={null} cpln --output ``` Available formats: * `text` (default) - Human-readable table * `json` - Full JSON output * `yaml` - Full YAML output * `json-slim` - JSON without IDs, versions, timestamps * `yaml-slim` - YAML without IDs, versions, timestamps * `tf` - Terraform HCL format * `crd` - Kubernetes Custom Resource Definition * `names` - Resource names only Use `json-slim` or `yaml-slim` to get clean output suitable for `cpln apply`. See [Output Formats](/cli-reference/using-cli/output) for details. ### `--color` Enable or disable colored output. ```bash theme={null} cpln --color=false ``` * Type: boolean * Default: `true` * Automatically disabled when stdout is not a terminal ### `--ts` Format timestamps in text output. ```bash theme={null} cpln --ts ``` Available formats: * `iso` - ISO 8601 format * `local` - Local timezone * `age` - Relative time (e.g., "2 hours ago") Only applies when `--output=text`. ### `--max` Limit the number of records displayed. ```bash theme={null} cpln --max ``` * Type: number * Default: `50` * Values less than 1 return all records ## Request and connectivity Control API connectivity and authentication. ### `--token` Override the authentication token for a single command. ```bash theme={null} cpln --token ``` View your profile's token with `cpln profile token `. ### `--endpoint` Override the API URL. ```bash theme={null} cpln --endpoint ``` * Default: `https://api.cpln.io/` Only change this for testing or if directed by Control Plane support. ### `--insecure` or `-k` Ignore TLS certificate errors. ```bash theme={null} cpln --insecure ``` Use only for debugging TLS issues. Prefer fixing certificate trust configuration. ## Resource creation and updates Common flags for `create`, `update`, and `patch` commands. ### `--name` Specify the resource name. ```bash theme={null} cpln create --name ``` ### `--description` or `--desc` Add a description to the resource. ```bash theme={null} cpln create --name --description "Resource description" ``` If not provided, defaults to the resource name. ### `--tag` Add tags to a resource. Tags are key-value pairs used for organization and policy targeting. ```bash theme={null} cpln create --tag = ``` ```bash String value theme={null} cpln create --tag environment=production ``` ```bash Force string with colon theme={null} cpln create --tag id:1234 ``` ```bash Multiple tags theme={null} cpln create --tag env=prod --tag team=platform ``` The `=` syntax auto-detects JSON types (`true` becomes boolean). Use `:` to force string values. ### `--file` or `-f` Provide input from a file. ```bash theme={null} cpln apply --file ``` Use `--file -` to read from stdin: ```bash theme={null} cat resource.yaml | cpln apply --file - ``` ## Query filters Filter results in `query` subcommands. ### `--match` Query match type. ```bash theme={null} cpln query --match ``` Available types: * `all` (default) - Match all conditions * `any` - Match any condition * `none` - Match none of the conditions ### `--property` or `--prop` Filter by resource property. ```bash theme={null} cpln query --property = ``` ```bash Filter by name theme={null} cpln agent query --property name=my-agent ``` ```bash Filter by status theme={null} cpln workload query --property status.phase=running ``` ### `--tag` Filter by tag. ```bash theme={null} cpln query --tag = ``` Example: ```bash theme={null} cpln gvc query --tag environment=production ``` ### `--rel` Filter by resource relation. ```bash theme={null} cpln workload query --rel gvc=my-gvc ``` ## Diagnostics Debug CLI and API interactions. ### `--verbose` or `-v` Enable verbose output to stderr. ```bash theme={null} cpln --verbose ``` Shows additional operational details. ### `--debug` or `-d` Display all HTTP communications with the API. ```bash theme={null} cpln --debug ``` Debug output may include sensitive information. Use with caution. ## Environment variable alternatives Many flags can be set via environment variables: | Flag | Environment Variable | | ----------- | -------------------- | | `--profile` | `CPLN_PROFILE` | | `--token` | `CPLN_TOKEN` | | `--org` | `CPLN_ORG` | | `--gvc` | `CPLN_GVC` | ```bash Set via environment theme={null} export CPLN_ORG=my-org export CPLN_GVC=my-gvc cpln workload get ``` ```bash Set via flags theme={null} cpln workload get --org my-org --gvc my-gvc ``` ### Variables without a flag | Variable | Effect | | ------------------------ | ------------------------------------------------------------------------------------------------------ | | `CPLN_SKIP_UPDATE_CHECK` | Skips the daily check for a newer CLI version and the notice it prints. Set it to any non-empty value. | ```bash theme={null} export CPLN_SKIP_UPDATE_CHECK=true ``` With the check skipped, `cpln upgrade --check` still reports the installed and latest versions on demand. ## Next steps Learn about output format options Configure default contexts # Getting Help Source: https://docs.controlplane.com/cli-reference/using-cli/help Use the built-in help system to explore commands, flags, and options. The Control Plane CLI includes comprehensive built-in help for every command and subcommand. ## Using the help system Display help at any level of the CLI: ```bash Top-level help theme={null} cpln --help ``` ```bash Command help theme={null} cpln --help ``` ```bash Subcommand help theme={null} cpln --help ``` Use `--help` frequently to discover available subcommands, required parameters, and optional flags. ## Help output structure CLI help displays: * **Description**: What the command does * **Usage**: Command syntax and required parameters * **Commands** (if applicable): Available subcommands * **Options**: Flags and their defaults * **Examples** (where available): Common usage patterns ## Available commands ```bash theme={null} cpln Commands: cpln account Manage accounts cpln agent Manage agents cpln apply Create or update a resource using an input file cpln auditctx Manage audit contexts cpln cloudaccount Manage cloud accounts cpln convert Convert k8s files to cpln files cpln cp Allows you to copy files and directories to and from workloads [aliases: copy] cpln delete Delete resources from files cpln domain Manage custom domains cpln group Manage groups cpln gvc Manage global virtual clouds cpln helm Manage helm releases on cpln cpln identity Manage identities within a global virtual cloud [aliases: id] cpln image Manage images and configure Docker login cpln ipset Manage IP Sets cpln location Manage locations [aliases: loc] cpln login [ref] Create a profile named "default" and print a URL and a confirmation code to complete sign-in in any browser cpln logs Show logs cpln metrics Query metrics cpln misc Miscellaneous helper commands cpln mk8s Manage an mk8s cluster cpln operator A Kubernetes operator for Control Plane cpln org Manage organizations cpln policy Manage access policies cpln port-forward Forward one or more local ports to a workload cpln profile Manage local profiles cpln quota Show quotas cpln rest Submit REST requests against the API cpln sandbox Manage sandboxes cpln secret Manage secrets cpln serviceaccount Manage service accounts [aliases: sa] cpln stack manage docker-compose files [aliases: compose] cpln task Manage pending tasks cpln upgrade Upgrade the cpln CLI to the latest version [aliases: update] cpln user Manage users cpln version Print the installed cpln version cpln volumeset Manage a persistent volumeset within a global virtual cloud cpln workload Manage workloads within a global virtual cloud [aliases: w] Options: --help Show help [boolean] --version, -v Show version number [boolean] © 2026 Control Plane Corporation ``` ## Finding specific information ### List all top-level commands ```bash theme={null} cpln --help ``` ### Discover subcommands Every command has subcommands. Use `--help` to see them: ```bash theme={null} cpln gvc --help ``` Shows subcommands like `get`, `create`, `delete`, `patch`, etc. ### View command-specific flags ```bash theme={null} cpln workload get --help ``` Displays all available flags for that specific command. ## Quick reference links Shared flags across all commands Control command output formatting Common issues and solutions Browse all CLI commands ## Common workflows and guides ### Getting started * [Installation](/cli-reference/installation) - Install the CLI * [Quickstart](/cli-reference/get-started/quickstart) - Install, verify, and log in * [Browser-less CLI Login](/guides/browser-less-cli-login) - Authenticate with a service account token for unattended environments * [Profiles](/cli-reference/get-started/profiles) - Configure and manage CLI profiles ### Deploying applications * [cpln stack](/guides/compose-deploy) - Deploy applications using Docker Compose * [cpln apply](/guides/cpln-apply) - Apply configuration files to create or update resources * [cpln delete](/guides/cpln-delete) - Delete resources using configuration files ### Working with workloads * [cpln workload connect](/guides/cli/workload/connect) - Interact with running workload containers * [cpln workload exec](/guides/cli/workload/exec) - Execute commands in workload containers * [cpln workload run](/guides/cli/workload/run) - Run one-off commands in a new workload instance ### Kubernetes integration * [cpln operator](/guides/cli/cpln-operator) - Manage the Kubernetes secret used by the Control Plane operator * [cpln helm](/guides/cpln-helm) - Manage Control Plane resources using Helm charts * [cpln port-forward](/guides/cli/cpln-port-forward) - Forward local ports to workloads running in a GVC ### Advanced operations * [cpln convert](/guides/cli/cpln-convert) - Convert Kubernetes manifests to Control Plane format * [cpln cp](/guides/cli/cpln-cp) - Copy files to and from workloads ### Reference documentation All commands have detailed reference pages in the **Commands** section. Each page includes: * Complete flag reference * Usage examples * Related commands ## Next steps Learn about shared CLI flags Get started with the CLI # Output Formats Source: https://docs.controlplane.com/cli-reference/using-cli/output Control how the CLI renders command results with different output formats. Use the `--output` (or `-o`) flag to choose how command results are displayed. Different formats serve different purposes, from human-readable tables to machine-parseable JSON. ## Available formats **Human-readable table format** (default) ```bash theme={null} cpln gvc get ``` Best for: * Interactive terminal use * Quick visual scanning * Development and debugging Text output may truncate long values. Use `json` or `yaml` for complete data. **Complete JSON output** ```bash theme={null} cpln gvc get --output json ``` Best for: * Programmatic parsing with `jq` * Automation scripts * Inspecting all resource properties ```bash View specific field theme={null} cpln gvc get my-gvc -o json | jq '.spec.staticPlacement.locationLinks' ``` ```bash Format with jq theme={null} cpln workload get -o json | jq -r '.items[].name' ``` **Complete YAML output** ```bash theme={null} cpln gvc get --output yaml ``` Best for: * Human-readable structured data * Inspecting nested configurations * Exporting for documentation ```bash Save to file theme={null} cpln gvc get my-gvc -o yaml > my-gvc.yaml ``` ```bash Parse with yq theme={null} cpln gvc get -o yaml | yq '.items[].name' ``` **Simplified output without metadata** ```bash theme={null} cpln gvc get my-gvc --output json-slim cpln gvc get my-gvc --output yaml-slim ``` Removes non-essential properties: * IDs * Versions * Timestamps * System-generated metadata Best for: * Creating templates for `cpln apply` * GitOps configurations * Copying resources between environments ```bash theme={null} cpln gvc get my-gvc -o yaml-slim > gvc-template.yaml ``` **Terraform HCL format** ```bash theme={null} cpln gvc get my-gvc --output tf ``` Best for: * Generating Terraform configurations * Migrating from CLI to Terraform * Infrastructure as Code workflows See [Terraform Provider](/iac/terraform) for details. **Kubernetes Custom Resource Definition (K8s CRD) format** ```bash theme={null} cpln workload get my-workload --output crd ``` Best for: * Kubernetes operator workflows * K8s-native GitOps (e.g. ArgoCD) * Hybrid Kubernetes/Control Plane setups See [Kubernetes Operator](/core/kubernetes-operator) for details. **Resource names only** ```bash theme={null} cpln gvc get --output names ``` Best for: * Scripts and automation * Piping to other commands * Quick listing ## Output modifiers ### Limit results with `--max` Control the number of records displayed: ```bash theme={null} cpln workload get --max 10 ``` * Default: `50` * Values less than 1 return all records ### Format timestamps with `--ts` Control timestamp formatting in text output: ```bash ISO 8601 format theme={null} cpln workload get --ts iso ``` ```bash Local timezone theme={null} cpln workload get --ts local ``` ```bash Relative time theme={null} cpln workload get --ts age # Output: "2 hours ago" ``` The `--ts` flag only applies when `--output=text`. ### Disable color with `--color` Remove ANSI color codes from output: ```bash theme={null} cpln gvc get --color=false ``` Useful for: * Logging to files * CI/CD environments * Systems that don't support colored output Color is automatically disabled when stdout is not a terminal. ## Common use cases ### Export a resource for GitOps ```bash theme={null} cpln gvc get production-gvc --output yaml-slim > production-gvc.yaml ``` Edit the file, then apply: ```bash theme={null} cpln apply --file production-gvc.yaml ``` ### Parse JSON with jq Extract specific fields: ```bash Get location links theme={null} cpln gvc get my-gvc -o json | jq -r '.spec.staticPlacement.locationLinks[]' ``` ```bash List workload images theme={null} cpln workload get -o json --gvc my-gvc | \ jq -r '.items[].spec.containers[].image' ``` ### Convert to Terraform Export existing resources to Terraform format: ```bash theme={null} cpln gvc get my-gvc -o tf > main.tf cpln workload get my-app --gvc my-gvc -o tf > main.tf ``` ## Troubleshooting output Text output truncates long values for readability. Use `json` or `yaml` to see complete data: ```bash theme={null} cpln gvc get my-gvc --output json ``` Disable color for cleaner logs: ```bash theme={null} cpln --color=false ``` Or redirect stderr to remove color automatically: ```bash theme={null} cpln 2>&1 | tee output.log ``` Limit output with `--max`: ```bash theme={null} cpln workload get --max 20 ``` Or use query filters to narrow results: ```bash theme={null} cpln workload query --tag environment=production ``` ## Next steps Learn about all shared CLI flags Use slim output for GitOps workflows # Shell Completion Source: https://docs.controlplane.com/cli-reference/using-cli/shell-completion Enable tab completion for the Control Plane CLI in your terminal. Shell completion enables tab-based autocompletion for `cpln` commands, subcommands, and flags in your terminal. Press `Tab` to autocomplete commands, discover available subcommands, and fill in flag names. ## Supported shells | Shell | Supported | | ----- | --------- | | Bash | Yes | | Zsh | Yes | | Fish | Yes | ## What gets completed Shell completion supports the following: `cpln workload ` shows available subcommands like `get`, `create`, `delete`. ```bash theme={null} cpln workload # get create delete update patch ... ``` `cpln workload get --` shows available flags like `--org`, `--gvc`, `--output`. ```bash theme={null} cpln workload get -- # --org --gvc --output --max --verbose ... ``` `cpln workload get --profile ` lists your configured profiles. ```bash theme={null} cpln workload get --profile # default staging production ... ``` `cpln workload get --endpoint ` suggests the API endpoint URL. ```bash theme={null} cpln workload get --endpoint # https://api.cpln.io ``` `cpln apply --file ` triggers native file path completion. ```bash theme={null} cpln apply --file # workload.yaml gvc.yaml secrets/ ... ``` ## Install completion Run the following command to install shell completion: ```bash theme={null} cpln misc install-completion ``` By default, this runs in interactive mode and prompts you to select your shell. After installation, **restart your terminal** for the changes to take effect. ### Options | Flag | Alias | Description | | --------- | ----- | ---------------------------------------------------------------------- | | `--shell` | | Specify the shell to install completion for (`bash`, `zsh`, or `fish`) | | `--batch` | `-b` | Run in non-interactive (batch) mode, skipping prompts | ### Non-interactive installation Use `--batch` and `--shell` together to skip the interactive prompts: ```bash theme={null} cpln misc install-completion --batch --shell bash ``` ```bash theme={null} cpln misc install-completion --batch --shell zsh ``` ```bash theme={null} cpln misc install-completion --batch --shell fish ``` After installing completion, restart your terminal or source your shell profile for the changes to take effect. When installing completion for Zsh, `compinit` is automatically configured in `~/.zshrc` if it is not already present. This is required for Zsh's completion system to work and is left in place during uninstallation since other tools may depend on it. ## Uninstall completion To remove shell completion from your profile: ```bash theme={null} cpln misc uninstall-completion ``` ## Troubleshooting **Problem**: Tab completion doesn't work after running `install-completion`. **Solutions**: 1. Restart your terminal or source your shell profile: ```bash theme={null} source ~/.bashrc ``` ```bash theme={null} source ~/.zshrc ``` Fish loads completions automatically. Try restarting your terminal. 2. Verify the CLI is on your PATH: ```bash theme={null} which cpln ``` 3. Reinstall completion: ```bash theme={null} cpln misc uninstall-completion cpln misc install-completion ``` **Problem**: Completion installation fails on a Mac with Apple Silicon. **Solution**: Install Rosetta, then retry: ```bash theme={null} softwareupdate --install-rosetta cpln misc install-completion ``` **Problem**: Tab completion doesn't include recently added commands or flags. **Solution**: Reinstall completion after updating the CLI: ```bash theme={null} cpln misc uninstall-completion cpln misc install-completion ``` ## Command reference Full command reference for install-completion and uninstall-completion # Troubleshooting Source: https://docs.controlplane.com/cli-reference/using-cli/troubleshooting Solutions to common Control Plane CLI issues. Quick solutions for common problems when using the Control Plane CLI. ## Installation and setup **Problem**: The CLI is installed but not found when running `cpln`. **Solutions**: 1. Verify the installation: ```bash theme={null} which cpln ``` ```powershell theme={null} Get-Command cpln ``` ```cmd theme={null} where cpln ``` 2. Check if the binary is on your PATH: ```bash theme={null} echo $PATH ``` Ensure `/usr/local/bin` (or your installation directory) is included. ```powershell theme={null} $env:PATH -split ';' ``` Ensure the directory containing `cpln.exe` is listed. ```cmd theme={null} echo %PATH% ``` Ensure the directory containing `cpln.exe` is listed. 3. Reinstall the CLI: ```bash theme={null} npm install -g @controlplane/cli ``` ```bash theme={null} brew reinstall cpln ``` Re-download and extract to a directory in your PATH. See [Installation](/cli-reference/installation). 4. Restart your terminal after installation. **Problem**: `npm install -g` fails with `EACCES` permission errors. **Solution (recommended)**: Install Node through a version manager such as [nvm](https://github.com/nvm-sh/nvm), [fnm](https://github.com/Schniz/fnm), or [Volta](https://volta.sh/). These install Node and its global package directory inside your home folder, so global installs never need `sudo` and you avoid the permission problem entirely. ```bash theme={null} # Example with nvm nvm install --lts nvm use --lts npm install -g @controlplane/cli ``` **Do not combine a custom `npm config set prefix` with a Node version manager.** nvm, fnm, and Volta manage the global prefix per Node version themselves. Setting your own `prefix` (the older "install without sudo" trick below) puts global packages in a *second* location that the version manager doesn't track — which leads to `cpln` installs that linger after uninstall and `npm` commands that silently target the wrong place. See the accordion **"The CLI lingers after uninstall, or npm targets the wrong location"** below. Use `sudo` (not recommended) or configure a custom npm prefix: ```bash theme={null} # Configure npm prefix mkdir -p ~/.npm-global npm config set prefix '~/.npm-global' ``` Add to your shell profile (`~/.bashrc`, `~/.zshrc`, etc.): ```bash theme={null} export PATH=~/.npm-global/bin:$PATH ``` Reload and reinstall: ```bash theme={null} source ~/.bashrc # or ~/.zshrc npm install -g @controlplane/cli ``` If you later adopt nvm/fnm/Volta, undo this first: `npm config delete prefix`, then remove the `export PATH=~/.npm-global/bin:$PATH` line from your shell profile. Otherwise the two prefixes will conflict. **Binary install alternative**: Move the binary to a user-writable directory instead of `/usr/local/bin`: ```bash theme={null} mkdir -p ~/bin mv cpln ~/bin/ ``` Add `~/bin` to your PATH in `~/.bashrc` or `~/.zshrc`: ```bash theme={null} export PATH="$HOME/bin:$PATH" ``` **Problem**: One or more of the following: * `npm uninstall -g @controlplane/cli` prints `up to date` (or `removed 0 packages`), yet `cpln` still runs. * `npm update -g @controlplane/cli` reports success but `cpln --version` doesn't change. * After installing, `cpln: command not found` in a new terminal even though the install succeeded. **Cause**: You have more than one Node on your machine — commonly a [version manager](https://github.com/nvm-sh/nvm) (nvm/fnm/Volta), a [Homebrew](https://brew.sh/) Node, and/or a custom `~/.npm-global` prefix. `cpln` is a `#!/usr/bin/env node` script, so it runs under whichever `node` is **first on your PATH**, and `npm -g` reads/writes the **prefix of the specific `npm` you invoke**. When those disagree, `npm` manages one location while `cpln` actually lives in another — so uninstall and update appear to do nothing. **Step 1 — See what you actually have.** Run every line; mismatches are the diagnosis: ```bash theme={null} which -a node npm cpln # every copy on PATH, in priority order node -v # which node actually runs npm -v npm config get prefix # where THIS npm installs global packages readlink -f "$(command -v cpln)" # where the cpln on PATH physically lives ``` If `npm config get prefix` does **not** contain the same directory as the resolved `cpln` path, that is exactly why uninstall/update is a no-op — `npm` is pointed at the wrong prefix. **Step 2 — Remove every stale copy.** Uninstall from each prefix that has it, then delete any leftover symlink directly: ```bash theme={null} # Run the uninstall once per Node/prefix that contains it. npm uninstall -g @controlplane/cli # If a cpln still resolves, remove the orphaned symlink + package by hand: rm -f "$(command -v cpln)" "$(command -v docker-credential-cpln 2>/dev/null)" hash -r # clear the shell's cached command paths (zsh/bash) which -a cpln || echo "cpln fully removed" ``` A custom-prefix install lives at `~/.npm-global/{bin,lib/node_modules}/@controlplane`. A Homebrew install lives under `/opt/homebrew/lib/node_modules` (Apple Silicon) or `/usr/local/lib/node_modules` (Intel). An nvm install lives under `~/.nvm/versions/node//lib/node_modules`. **Step 3 — Reinstall into the prefix you intend to use.** Whatever `node` you want must be first on PATH *before* you install, so the install lands where you'll run it: ```bash theme={null} nvm use --lts # or: nvm use — selects the node you want npm install -g @controlplane/cli cpln --version # confirm it runs from the expected location readlink -f "$(command -v cpln)" ``` **Problem**: `cpln` worked, then vanished or reverted to an old version after running `nvm use`, `nvm install`, or opening a new shell. **Cause**: Version managers keep a **separate global package set per Node version**. Installing `@controlplane/cli` under Node 20 does not make it available under Node 22, and switching versions switches which `cpln` (if any) is on PATH. **Solutions**: 1. Install the CLI under the Node version you actually use, then pin that version as the default so new shells use it: ```bash theme={null} nvm alias default # e.g. nvm alias default 22 ``` 2. When upgrading Node, carry your global packages forward: ```bash theme={null} nvm install --reinstall-packages-from= # or, after installing the new version: nvm reinstall-packages ``` 3. Verify after switching: ```bash theme={null} node -v && cpln --version ``` **Problem**: You installed nvm/fnm, but `which node` still points at `/opt/homebrew/bin/node` (or `/usr/local/bin/node`), so your version manager's Node — and the `cpln` installed under it — is ignored. **Cause**: PATH order. Your shell startup file loads the version manager, but a later line **re-prepends** Homebrew's `bin` to PATH (for example `export PATH="/opt/homebrew/bin:$PATH"`, or Homebrew's `shellenv`), so Homebrew's `node` ends up ahead of the version manager's. **Solution**: Make the version manager activate **last** in your `~/.zshrc` / `~/.bashrc`, after any line that prepends Homebrew. With nvm, the robust form is to activate the default version and push its bin to the front of PATH at the end of the file: ```bash theme={null} # ... all other PATH exports, including Homebrew, above this ... export NVM_DIR="$HOME/.nvm" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # Activate the default Node and ensure it wins over Homebrew on PATH. # (nvm use swaps the version in place and will not re-front itself.) command -v nvm >/dev/null && nvm use default --silent >/dev/null 2>&1 [ -n "$NVM_BIN" ] && export PATH="$NVM_BIN:$PATH" ``` Open a new terminal and confirm: ```bash theme={null} which node # should be under ~/.nvm/versions/node/... node -v ``` Keeping Homebrew's Node installed is fine — it just must not shadow the version you intend to use. If you don't need it, `brew uninstall node` (check first that no other Homebrew formula depends on it). **Problem**: `A new version of cpln is now available` adds noise to scripts, CI logs, or shell completions. **Cause**: The CLI checks npm for a newer release at most once a day and shows the notice at most once a day. **Solution**: Set `CPLN_SKIP_UPDATE_CHECK` to any non-empty value to skip the check and the notice: ```bash theme={null} export CPLN_SKIP_UPDATE_CHECK=true ``` You can still check on demand with `cpln upgrade --check`. ## Authentication **Problem**: Commands fail with authentication errors. **Solutions**: 1. Check for typos in the org name: ```bash theme={null} cpln profile get ``` Verify the `org` value is correct. A 403 often means you're trying to access an org that doesn't exist or that you don't have access to. 2. Re-authenticate: ```bash theme={null} cpln login ``` 3. Check which profile is marked as active with a star `*`: ```bash theme={null} cpln profile get ``` 4. Verify the token is valid: ```bash theme={null} cpln profile token ``` 5. For service accounts, regenerate the key: ```bash theme={null} cpln serviceaccount add-key --description ``` Make sure the service account is assigned to a group that has the necessary permissions to access and manage resources (e.g. the `superusers` group). Update your profile with the new token: ```bash theme={null} cpln profile update --token --gvc ``` When using `--token`, always include `--gvc` to preserve or set your default GVC context. **Problem**: `cpln login` stops waiting and reports that the login session expired. **Cause**: The confirmation code is valid for about 10 minutes. **Solution**: Run `cpln login` again to get a fresh URL and code, then complete the sign-in before the code expires. **Problem**: You are on an SSH session, a container, or WSL2, and there is no browser to sign in with. **Solution**: None needed — copy the printed URL to a browser on any device, sign in, and enter the confirmation code. The waiting CLI picks up the credentials automatically. **For unattended automation**, where nobody can complete a sign-in, use [browser-less login](/guides/browser-less-cli-login) with a service account token. **Problem**: Commands execute in the wrong org or GVC. **Solutions**: 1. Check your active profile: ```bash theme={null} cpln profile get ``` 2. Override for a single command: ```bash theme={null} cpln --org --gvc ``` 3. Update your profile defaults: ```bash theme={null} cpln profile update --org --gvc ``` 4. Use a different profile: ```bash theme={null} cpln --profile ``` 5. Set environment variables: ```bash theme={null} export CPLN_ORG=my-org export CPLN_GVC=my-gvc ``` ## Connectivity and network **Problem**: Commands fail with TLS/SSL errors. **Solutions**: 1. Verify your system's certificate trust store is up to date. 2. Check if you're behind a corporate proxy or firewall that intercepts TLS. 3. As a temporary workaround (not recommended for production): ```bash theme={null} cpln --insecure ``` 4. Contact your network administrator to add Control Plane's certificate to your trust store. **Problem**: Commands fail with timeout or connection errors. **Solutions**: 1. Check your internet connection. 2. Verify you can reach the Control Plane API: ```bash theme={null} curl -I https://api.cpln.io/about ``` 3. Check if you're behind a proxy. ## Output and display **Problem**: Text output doesn't show all data. **Solutions**: 1. Use JSON or YAML output for complete data: ```bash theme={null} cpln --output json cpln --output yaml ``` 2. Increase the max results: ```bash theme={null} cpln --max 100 ``` Set `--max 0` to get all the records. 3. Use `json-slim` or `yaml-slim` for cleaner structured output: ```bash theme={null} cpln --output json-slim ``` See [Output Formats](/cli-reference/using-cli/output) for details. **Problem**: Log files contain ANSI color codes. **Solutions**: 1. Disable color: ```bash theme={null} cpln --color=false ``` 2. Color is automatically disabled when stdout is not a terminal. Redirect to a file: ```bash theme={null} cpln -o json > output.log ``` ## Commands and operations **Problem**: CLI doesn't recognize a command or flag. **Solutions**: 1. Verify you're using the correct command syntax: ```bash theme={null} cpln --help ``` 2. Check your CLI version: ```bash theme={null} cpln --version ``` 3. Update to the latest version: ```bash theme={null} npm update -g @controlplane/cli ``` ```bash theme={null} brew upgrade cpln ``` Download the latest binary from [Installation](/cli-reference/installation). ## Debugging Enable verbose or debug output to troubleshoot issues: ```bash Verbose output theme={null} cpln --verbose ``` ```bash Debug mode (shows HTTP traffic) theme={null} cpln --debug ``` Debug output may contain sensitive information (tokens, resource details). Use with caution and avoid sharing publicly. ## Getting help If you can't resolve the issue: * Email us at [support@controlplane.com](mailto:support@controlplane.com) * Reach out on [Slack](https://slack.com) through the support channel ## Next steps Learn about shared CLI flags Use the built-in help system # Compliance Source: https://docs.controlplane.com/compliance Control Plane's security certifications and compliance standards including PCI DSS, SOC 2, HIPAA, and GDPR. Control Plane is committed to providing secure products and services to safely and easily manage billions of digital identities across the globe. Our external certifications reinforce our duty to safeguard our customers. These certifications are a testament to our unwavering dedication, regularly assessing and validating the effectiveness of Control Plane's security practices. ## PCI ### Control Plane is Level 1 PCI DSS compliant Stripe is the payment information and credit card processing provider used by Control Plane. Security is a top priority for Control Plane, and as such, we have invested significant resources to obtain and maintain PCI compliance. This commitment to security is evidenced by annual on-site assessments and continuous risk management to ensure the platform remains secure for all of our users. For a copy of our PCI Attestation of Compliance (AoC), please contact [support@controlplane.com](mailto:support@controlplane.com). ### What is PCI? The Payment Card Industry Data Security Standard (PCI DSS) is a security standard for organizations that handle credit and debit card information. The standard was created to increase controls around payment data and reduce fraud. If you want to sell online and accept payments from Visa, Mastercard, American Express, or Discover, your software and hosting needs to be PCI compliant. ## SOC2 Type II ### Security as a Company Value Guided by Control Plane’s security and compliance principles, we deliver products and services that enable simple, secure access to the digital world. ### Secure Personnel Control Plane prioritizes the security of its own data as well as that of its clients and customers, ensuring that only vetted personnel have access to these resources. * All Control Plane contractors and employees undergo background checks prior to being engaged or employed by us in accordance with local laws and industry best practices. * Confidentiality or other types of Non-Disclosure Agreements (NDAs) are signed by all employees, contractors, and others who have a need to access sensitive or internal information. * We foster a culture of security across our company by continuously training and testing our employees on the latest and emerging security techniques and attack vectors. ### Secure Development * All development projects at Control Plane, including on-premises software products, support services, and our own Digital Identity Cloud offerings, follow secure development lifecycle principles. * All development of new products, tools, services, and major changes to existing ones, undergo a design review to ensure security requirements are incorporated into proposed development. * All team members regularly involved in any system development undergo annual secure development training in coding or scripting languages that they work with, as well as any other relevant training. * Software development is conducted in line with OWASP Top 10 recommendations for web application security. ### Secure Testing Control Plane deploys third-party penetration testing and vulnerability scanning of all production and Internet facing systems on a regular basis. * All new systems and services are scanned prior to being deployed to production. * All new and existing systems, products, services, and updates are thoroughly evaluated from multiple perspectives. Leveraging external penetration testing and our top-tier internal security engineers, we provide users with a comprehensive and real-world view of all Control Plane products and environments. * We perform static and dynamic software application security testing of all code, including open-source libraries, as part of our software development process. ### Cloud Security Control Plane's cloud provides maximum security with complete customer isolation in a modern, multi-tenant cloud architecture. Control Plane's cloud leverages the native physical and network security features of the cloud service and relies on the providers to maintain the infrastructure, services, and physical access policies and procedures. * All customer cloud environments and data are isolated using Control Plane’s patented isolation approach. Each customer environment is stored within a dedicated trust zone to prevent any accidental or malicious co-mingling. * All data is also encrypted at rest and in transmission to prevent any unauthorized access and data breaches. Our entire platform is continuously monitored by dedicated, highly trained Control Plane experts. * We separate each customer's data and our own, utilizing unique encryption keys to ensure data is protected and isolated. * Client’s data protection complies with SOC 2 standards to encrypt data in transit and at rest, ensuring customer and company data and sensitive information are protected at all times. * We implement role-based access controls and the principles of least privileged access, and review revoke access as needed. ### SOC 2 Type 2 Control Plane successfully completed the AICPA Service Organization Control (SOC) 2 Type II audit. The audit confirms that Control Plane’s information security practices, policies, procedures, and operations meet the SOC 2 standards for security. Control Plane was audited by Prescient Assurance, a leader in security and compliance certifications for B2B and SAAS companies worldwide. Prescient Assurance is a certified public accounting firm in the US and Canada, providing risk management and assurance services including but not limited to SOC 2, PCI, ISO, NIST, GDPR, CCPA, HIPAA, CSA STAR, etc. For more information about Prescient Assurance, you may reach out to them at [info@prescientassurance.com](mailto:info@prescientassurance.com). A successful SOC 2 Type II audit report assures Control Plane’s current and future customers that their data is managed with the highest standards of security and compliance. For a copy of our audit report, please contact [support@controlplane.com](mailto:support@controlplane.com). # Access Control Source: https://docs.controlplane.com/concepts/access-control Two-part access control system combining billing account roles with org-level policies. Covers principals, permissions, and fine-grained authorization. Access control on the Control Plane Platform is handled in two separate ways: one for billing accounts, and the other for organizations (orgs). Billing accounts have three roles that can be assigned when inviting users: | Role | Description | | :--------------- | :------------------------------------------------------------ | | `billing_admin` | Full administrative access to billing settings and invoices | | `billing_viewer` | Read-only access to billing information | | `org_creator` | Ability to create new organizations under the billing account | Billing account roles are independent from org-level policies. A `billing_admin` can manage invoices but has no implicit permissions on org resources. For access control on [orgs](/reference/org), there is a policy system that relies on **principals** and **bindings**. This enables fine-grained authorization rules to define the minimum amount of permissions required when accessing resources of the platform. Organization access control follows the principle of least privilege - grant only the permissions necessary for a principal to perform their tasks. *** ## Principals A "principal" is an entity that can be granted permissions. The Control Plane Platform supports four principal types, each scoped to an [org](/reference/org). A distinct human being associated with an email. Users are members of one or more orgs and can be invited by authorized principals. A non-human principal for automation and CI/CD pipelines. Service accounts can be associated with a group on creation. A named collection of users and service accounts. Permissions granted to a group via policies are automatically applied to all its members. A workload identity that grants cloud access and network resource permissions. Reusable across multiple workloads. *** ## Policy A [policy](/reference/policy) governs resource access within an [org](/reference/org) by binding principals to permissions on target resources. A resource is a Control Plane object (e.g., [secret](/reference/secret), [workload](/reference/workload/general), [GVC](/reference/gvc), etc.). A policy can target: * **All resources** of a specific type within your org * **Specific resources** assigned directly by resource self link * **Dynamic resources** matched using [tag queries](/core/misc#tags) For example, a policy can target all GVCs in your org, or specifically only "production-gvc" and "staging-gvc". A binding maps permissions to principals: * **Permissions**: Actions like `create`, `delete`, `edit`, `view`, etc. The [available permissions](/reference/policy#permissions) vary by resource type. * **Principals**: [Users](/reference/user), [groups](/reference/group), [service accounts](/reference/serviceaccount), or [identities](/reference/identity) that receive the permissions. Multiple bindings can exist on a single policy, allowing different permission sets for different principals. *** ## Common Use Cases Create a [group](/reference/group) for your team and add users as members. Create a [policy](/reference/policy) that grants the group permissions on target resources (e.g., `view` and `edit` on specific GVCs). All group members automatically receive these permissions. When team members change, simply update group membership - no policy changes needed. Create a [service account](/reference/serviceaccount) and generate a key for authentication. Add the service account to a group with appropriate permissions, or create a policy granting it `create` and `edit` permissions on workloads. Create a CLI profile using `cpln profile create PROFILE_NAME --org ORG_NAME --token KEY --default`. The `--default` flag makes this profile active for all future commands without needing to specify `--profile` each time. Create an [identity](/reference/identity) and link it to your [workload](/reference/workload/general). Create a [policy](/reference/policy) that grants the identity `reveal` permission on the target [secret](/reference/secret). Reference the secret in your workload as an environment variable (`cpln://secret/SECRET_NAME`) or volume mount. The workload can now access the secret value at runtime. Create a [service account](/reference/serviceaccount) for Terraform or Pulumi. Generate a key and configure your provider to authenticate with it. Create policies granting the service account permissions to manage the resources your IaC templates define (GVCs, workloads, secrets, identities, etc.). *** ## Next Steps Detailed configuration options and permissions for policies Step-by-step guide to creating your first policy # Billing Account Source: https://docs.controlplane.com/concepts/billing Manage billing accounts, invoices, payment methods, and spending alerts. Covers user access controls, billing roles, and cost tracking across organizations. ## Overview A billing account allows you to manage user access, invoices, payment methods, and spending threshold alerts. You can create multiple billing accounts to manage billing independently. Each billing account provides access to: * Account details: Manage account details, billing information, and [spend threshold email alerts](#spend-threshold-email-alert). * [Orgs](/reference/org): View the orgs associated with this billing account. * Invoices: View and download billing invoices. * Payment Methods: Add, update, and remove payment methods. * [Users](#users): Manage user access, roles, and permissions within the billing account. * Cost & Usage: Review cost and usage across all orgs associated with this billing account. ### Spend Threshold Email Alert Spend threshold email alerts help you monitor billing usage and spending. You must first enable the spend alert to receive email notifications when your configured monthly threshold is reached. ### Users You can add users to your billing account and assign billing roles: `billing_admin`, `billing_viewer`, or `org_creator`. Billing roles control access to billing account features and org creation, but they do not grant org-level resource permissions. Existing users gain access after they are added. New users will receive an email with instructions on how to access the billing account. # GVC (Global Virtual Cloud) Source: https://docs.controlplane.com/concepts/gvc Global Virtual Clouds (GVCs) group cloud providers and locations for workload deployment. Covers domain assignment, pull secrets, and multi-cloud isolation. ## Overview A Global Virtual Cloud ([GVC](/reference/gvc)) defines a set of cloud providers and deployment [locations](/reference/location). Creating a GVC builds a unified multi-cloud environment from the selected [locations](/reference/location). [Workloads](/concepts/workload) deployed to the GVC can be served from those [locations](/reference/location). Each [org](/reference/org) can have multiple GVCs, each with its own set of [locations](/reference/location). A [domain name](/reference/domain) configured at the org level can be assigned to a GVC for routing. Each domain is associated with exactly one GVC at a time. Traffic is routed to [workloads](/concepts/workload) using the assigned [domain name](/reference/domain). ## Benefits * GVCs enable your [workload](/concepts/workload) to be deployed easily to multiple cloud providers and locations: * Choose providers such as AWS, Azure, and GCP. * Select [locations](/reference/location) close to your end users. * Select the [locations](/reference/location) that fulfill your workload requirements. * Improve availability if a cloud provider experiences an outage. * Configure granular scaling behavior for your [workload](/concepts/workload). ## Domain Name [Domains](/reference/domain) are configured at the [org](/reference/org) level and routed to workloads via [domain route configuration](/reference/domain#path-based-routing). Workloads receive default endpoints under the domain `cpln.app`; you can configure a custom [domain](/reference/domain) when you want to route your own hostname. ## Pull Secrets Pull secrets are [secrets](/reference/secret) assigned to a GVC and used by [workloads](/concepts/workload) when authentication is required to pull an image from a private registry. Only the [Docker](/reference/secret#docker), [Amazon ECR](/reference/secret#ecr), and [GCP](/reference/secret#google-cloud-platform-gcp) [secret](/reference/secret) types are supported. If the image was pushed to the Control Plane registry for the same [org](/reference/org), no secret is required. A GVC can have multiple pull secrets assigned. A [workload's container](/reference/workload/containers) will use the appropriate secret when pulling images from a private registry. If multiple secrets are configured, the container attempts each secret in sequence. If authentication fails, the deployment is not updated, and image pulls retry with exponential backoff from 10 seconds up to 5 minutes (e.g., 10 seconds, 20 seconds, 40 seconds, etc.). ## Location Routing By default, traffic is routed to the nearest healthy location using latency-based DNS geo-routing. For more advanced routing scenarios, you can configure [location routing options](/reference/gvc#location-routing-options) on a per-GVC basis to control priority-based failover, adjust traffic distribution with latency offsets, and set latency thresholds for location availability. ## Reference Visit the [GVC reference](/reference/gvc) page for additional details. # Org (Organization) Source: https://docs.controlplane.com/concepts/org Organizations are the top-level isolation boundary in Control Plane. Contains all resources including GVCs, workloads, policies, secrets, and user access controls. ## Overview An org is a tightly isolated boundary that contains Control Plane resources such as [domains](/reference/domain), [images](/reference/image), [workloads](/concepts/workload), [GVCs](/reference/gvc), [users](/reference/user), [groups](/reference/group), [service accounts](/reference/serviceaccount), and other resources. A physical organization can create multiple orgs to isolate subsidiaries, environments, or business units. An org name must be **globally unique**. As a Control Plane user, you are a member of at least one org. Depending on your permissions, you can view or manage org-scoped resources such as: **Select any link below to learn more about a resource:** * **Access Control** * [Audit Context](/reference/auditctx) * [Audit Trail](/core/audittrail) * [Groups](/reference/group) * [Policies](/reference/policy) * [Service Accounts](/reference/serviceaccount) * [Users](/reference/user) * [Agents](/reference/agent) * [Cloud Accounts](/reference/cloudaccount) * [Domains](/reference/domain) * [GVC (Global Virtual Cloud)](/reference/gvc) * [Identities](/reference/identity) * [Volume Sets](/reference/volumeset) * [Workloads](/concepts/workload) * [Images](/reference/image) * [Locations](/reference/location) * [Logs](/core/logs) * [Quotas](/reference/quota) * [Secrets](/reference/secret) ## Cross-GVC Communication Workloads in different GVCs within the same org can communicate using [internal firewall rules](/reference/workload/firewall#internal). Internal firewall access defaults to `none`, so cross-GVC communication must be explicitly allowed with `same-org` or `workload-list`. ## Reference See the [org reference](/reference/org) page for configuration details. # Workloads Source: https://docs.controlplane.com/concepts/workload Workloads are the primary deployment unit in Control Plane. Covers container configuration, autoscaling, Capacity AI, logging, and the five workload types. ## Overview A workload represents a backend application such as a microservice. It is comprised of one or multiple containers. Containers within a workload communicate freely on `localhost`. Workloads run in Control Plane AWS, Azure, and GCP accounts, or in your own [BYOK location](/reference/location#byok-locations). The [GVC](/reference/gvc) determines which providers and locations are available. A workload can run in one location or across multiple providers and regions, depending on the GVC and workload placement settings. Workloads are managed using a common interface, regardless of cloud providers. Workload logs are consolidated across replicas, locations, and providers, and can be accessed through the API, CLI, Console, or Grafana. ## Features * [Auto Scaling](#auto-scaling) * DNS geo-routing * [Capacity AI](#capacity-ai) - Intelligent resource management * Load balancing * [Location-specific override](#location-specific-override) of scaling and resource management * Logging * [Probes](#probes) * [Alerts](#alerts) ## Auto Scaling Workload replicas are automatically scaled up and down based on the selected scaling strategy. Selectable Scaling Strategies: * Disabled * CPU Utilization * Memory Utilization * Concurrent Requests Quantity * Requests Per Second * Request Latency See [Autoscaling](/reference/workload/autoscaling) for more information. The minimum and maximum number of replicas that can be deployed are configurable. Scale to zero is available for [Serverless](/reference/workload/types#serverless) workloads using the `rps` or `concurrency` scaling strategies, and for [Standard](/reference/workload/types#standard) and [Stateful](/reference/workload/types#stateful) workloads when using KEDA. When the scale-to-zero condition is met, the workload can scale down to 0 and scale up immediately to fulfill new requests. [Capacity AI](#capacity-ai) is not available if CPU Utilization is selected because dynamic allocation of CPU resources cannot be accomplished while scaling replicas based on the usage of its CPU. See [Capacity AI Restrictions](/reference/workload/capacity#caveats) for the full list. ## Capacity AI A workload can leverage intelligent allocation of its container's resources (CPU and memory) by using Capacity AI. Capacity AI uses historical usage analysis to adjust these resources between configured minimum and maximum values. This approach can substantially reduce costs; however, it may result in temporary performance issues during sudden spikes in usage. Before enabling capacity AI on your workload, review the [Capacity AI reference page](/reference/workload/capacity). ## Location-specific Override By default, both [Capacity AI](#capacity-ai) and [Auto Scaling](#auto-scaling) settings are applied to all deployments at each location enabled in the [GVC](/concepts/gvc). However, these settings can be customized per location to enhance performance for specific audiences. This allows for granular control over how your workload scales in specific locations. For instance, if the majority of your users are in Europe, you can set the European locations to a higher level than the rest of the world. Setting location-specific options ensures that your target users are served quickly and helps reduce costs for unused resources. ## Probes A probe is a Kubernetes feature that is used to monitor the health of an application running in a container. Each container can have a: * Readiness Probe * An endpoint is configured to allow queries, enabling you to check if the workload is available and ready to receive requests. * Liveness Probe * An endpoint is configured to allow queries, enabling you to check if the workload is healthy or needs to be restarted. ## Alerts Using Grafana, you can create alerts on any of the standard metrics exposed by Control Plane, or on your [custom metrics](/reference/workload/custom-metrics). To access Grafana, navigate to one of your orgs in the Control Plane console and click the "Metrics" link. You have full access to Grafana alerting capabilities. For more information, see the [Grafana documentation](https://grafana.com/docs/grafana/latest/alerting/). ## Inter-Workload Networking Workloads are reachable by other workloads at `..cpln.local`, but inter-workload traffic is **denied by default**. Each receiving workload must opt in by setting [`firewallConfig.internal.inboundAllowType`](/reference/workload/firewall#internal) to one of: * `same-gvc` — allow workloads in the same GVC. * `same-org` — allow workloads anywhere in the same org. * `workload-list` — allow specific workloads listed in `inboundAllowWorkload` (can span GVCs). The default is `none`, which blocks all inter-workload traffic. ## Types * **Serverless**: * Workloads that scale to zero when they aren't receiving requests. * **Standard**: * Workloads serve network traffic on multiple ports and can scale to zero only when using KEDA. * **Cron**: * Workloads that run on a schedule, and do not serve network traffic. * **Stateful**: * Similar to a `standard` workload, `stateful` workloads have stable replica identities and hostnames, and can mount a [volume set](/reference/volumeset) for persistent storage. * **VM**: * Run a full [virtual machine](/reference/workload/vm) — its own guest OS and kernel — as a workload, inside the same service mesh, identity, networking, and observability as containers. ## Reference See the [workload reference](/reference/workload) page for additional information. # Accessing Cloud Resources Source: https://docs.controlplane.com/core/accessing-cloud-resources Access AWS, GCP, and Azure services from workloads without embedding credentials. Uses cloud accounts and identities for secure, credential-free access. ## Overview Control Plane enables [workloads](/reference/workload) to access native services from AWS, Azure, and GCP in a least-privilege manner, regardless of where the workloads run. Developers do not need to embed credentials to access services such as S3, DynamoDB, and BigQuery. This capability is **optional**. This feature simplifies credential management by allowing workloads to obtain temporary credentials dynamically instead of relying on embedded secrets. Cloud providers refer to this as "temporary session credentials." For more information, see [AWS temporary security credentials](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html). To grant a [workload](/reference/workload) fine-grained access to cloud resources, complete the following steps: * Register a [cloud account](/reference/cloudaccount) with Control Plane for each cloud provider (AWS, Azure, or GCP) that hosts the resources your workload requires. * Create an [identity](/reference/identity) and assign the desired [cloud access](/guides/create-identity#cloud-access) permissions to resources within each registered [cloud account](/reference/cloudaccount). * Assign the [identity](/reference/identity) to a [workload](/reference/workload). Each workload can have only one assigned identity. [Identities](/reference/identity) can be reused by multiple workloads in the same GVC that require the same permissions. Control Plane must be able to perform the following actions to provision and revoke an [identity's](/reference/identity) access to native cloud services: * Create `Roles` in AWS * Create `App registrations` in Azure * Create `Service Accounts` in GCP For additional detail, refer to the [cloud account](/reference/cloudaccount) reference page for each cloud provider: * [AWS](/reference/cloudaccount#aws-details) * [Azure](/reference/cloudaccount#azure-details) * [GCP](/reference/cloudaccount#gcp-details) # Audit Trail Source: https://docs.controlplane.com/core/audittrail Tamper-proof audit trail for tracking all Control Plane and custom workload actions. Covers UI filtering, API queries, and custom workload audit integration. ## Overview Control Plane provides a tamper-proof audit trail for both Control Plane and custom workload actions. The [audit trail UI](#audit-trail-ui) lets you search, filter, and view these events. Each action performed in the Control Plane console or via the [CLI](/cli-reference/overview) is captured, securely stored, and indexed using the audit trail. In the UI, most resources include an audit trail link that opens the audit trail page with the resource ID prefilled. Additional filters can be added to drill down to specific events. Custom workloads can use the audit trail service without needing to build their own solution. See [custom workloads](#custom-workloads) for details. ## Audit Trail UI The audit trail UI displays details for each captured event. Each action contains: * Timestamp * Name of resource * Type of resource * Version * Status * Message * Subject (the user that acted) * Link to view raw JSON for the event The actions displayed can be filtered by: * Kind of resource * Audit context (the `cpln` audit context shows only Control Plane actions) * Resource name or ID * Subject name * Start and optional end date ## Sample Audit Trail UI Below is a sample of the audit trail UI after executing a query that returns captured actions: Audit trail UI showing a filtered list of captured actions with timestamps, principals, and action types ## Custom Workloads The audit trail architecture is generic and allows any [workload](/concepts/workload) to securely and reliably capture events. To use the audit trail, a [workload](/concepts/workload) must first create an [audit context](/reference/auditctx). See the [audit context](/reference/auditctx) reference for configuration details. Detailed workload integration examples will be available soon. ### View Custom Audit Trail To view the actions captured by your workload, you can use: * Control Plane Audit Trail UI: * You can select an [audit context](/reference/auditctx) to view only those actions. * Control Plane audit API: * Use the API to create a custom UI for audit data. * View the [Audit API OpenAPI spec](https://audit.cpln.io/openapi.json) to review the audit schema and available methods. # Authentication Source: https://docs.controlplane.com/core/authentication Authentication methods for Console UI (SSO via Google, GitHub, Microsoft, SAML) and CLI (interactive login, service accounts, and token management). Control Plane supports multiple authentication methods depending on how you access the platform. *** ## Console UI The Console supports single sign-on (SSO) for user authentication with the following providers: After successful authentication, users' access privileges are determined based on their assigned [group membership](/reference/group) or [policy](/reference/policy). To enable SAML authentication for your organization, contact us on Slack or at [support@controlplane.com](mailto:support@controlplane.com). **Values required from your authentication provider:** * Entity ID * SSO URL * Certificate **Control Plane SAML configuration values:** * Service Provider Entity ID: `cpln.io` * Assertion Consumer Service (ACS) / Callback URL: `https://console.cpln.io/__/auth/handler` *** ## CLI The CLI supports two authentication methods: For interactive use, run the login command, which prints a URL and a confirmation code you use to complete SSO sign-in from any browser: ```bash theme={null} cpln login ``` This creates a default profile for your credentials. The browser does not have to be on the same machine as the CLI. For CI/CD pipelines and automation, use a service account token: ```bash theme={null} # Create a profile using a service account token cpln profile create PROFILE_NAME --org ORG_NAME --token TOKEN --default ``` The `--default` flag sets this profile as the default for future commands. You can also use the `CPLN_TOKEN` environment variable: ```bash theme={null} export CPLN_TOKEN=your-service-account-token ``` Complete guide for CLI authentication, including service account token setup for CI/CD. *** ## Terraform Configure the provider with your organization and authentication credentials: ```hcl theme={null} provider "cpln" { org = "your-org-name" token = var.cpln_token # Service account token } ``` Or use environment variables: ```bash theme={null} export CPLN_ORG=your-org-name export CPLN_TOKEN=your-service-account-token ``` Complete Terraform provider setup and configuration *** ## Pulumi Configure authentication using either Pulumi config or environment variables: ```bash theme={null} pulumi config set cpln:org your-org-name pulumi config set --secret cpln:token your-service-account-token ``` ```bash theme={null} export CPLN_ORG=your-org-name export CPLN_TOKEN=your-service-account-token ``` Complete Pulumi provider setup and configuration *** ## REST API Authenticate API requests using a bearer token in the `Authorization` header: ```bash theme={null} curl --request GET \ --url https://api.cpln.io/org/your-org/gvc \ --header 'Authorization: Bearer YOUR_TOKEN' ``` Tokens can be obtained from: * **Service account key**: Generated when creating a service account * **User access token**: Run `cpln profile token PROFILE_NAME` Complete API documentation with interactive examples *** ## Service Accounts For programmatic access (CI/CD, automation, and IaC), create a [service account](/reference/serviceaccount) and generate a key: In the Console, navigate to **Service Accounts** and click **New**, or use the CLI: ```bash theme={null} cpln serviceaccount create --name my-service-account --org my-org ``` ```bash theme={null} cpln serviceaccount add-key my-service-account --description my-key --org my-org ``` The key is shown only once. Save it immediately in a secure location. Add the service account to a [group](/reference/group) or create a [policy](/reference/policy) that grants the necessary permissions. Use the generated key as your token in the CLI, Terraform, Pulumi, or API requests. Step-by-step guide for creating and configuring service accounts # Kubernetes Operator Source: https://docs.controlplane.com/core/kubernetes-operator Manage Control Plane resources from Kubernetes for GitOps workflows with ArgoCD. Supports GVCs, workloads, identities, secrets, policies, domains, and cloud accounts. The Control Plane Kubernetes Operator enables you to manage Control Plane resources directly from your Kubernetes cluster using custom resource definitions (CRDs). It provides a bridge between Kubernetes-native workflows and Control Plane infrastructure, making it ideal for GitOps deployments with tools like ArgoCD. ## Overview With the operator installed, you can: * **Define resources as YAML** - Create GVCs, workloads, secrets, and other resources as Kubernetes manifests * **Enable GitOps** - Store your infrastructure in Git and deploy automatically with ArgoCD * **Maintain consistency** - Keep your Kubernetes and Control Plane resources in sync ## Supported resources The operator manages the following Control Plane resource types from Kubernetes. Most resources use Control Plane CRDs; secrets use the native Kubernetes Secret format described below. | Resource | Description | | ---------------- | --------------------------------------------------------------- | | `agent` | Secure connectivity to private networks | | `auditctx` | Tamper-proof audit trail for tracking actions | | `cloudaccount` | Cloud provider integrations (AWS, GCP, Azure, NGS) | | `domain` | Custom domain mapping with TLS and geo-routing | | `group` | Membership collection of users and service accounts | | `gvc` | GVC that groups workloads and defines deployment locations | | `identity` | Grants workloads access to cloud resources and private networks | | `ipset` | Reserved public IP addresses for workloads | | `location` | Cloud regions where workloads can be deployed | | `mk8s` | Managed Kubernetes clusters across cloud providers | | `org` | Top-level context for all Control Plane resources | | `policy` | Grants permissions to principals on target resources | | `secret` | Encrypted storage for credentials and sensitive data | | `serviceaccount` | Machine identity for headless API operations | | `volumeset` | Persistent storage with snapshots and autoscaling | | `workload` | Application containers running on Control Plane | ## Key concepts Control Plane custom resources differ from standard Kubernetes resources. Fields like `org`, `gvc`, and `description` are at the **top level**, not inside `spec`: ```yaml theme={null} apiVersion: cpln.io/v1 kind: workload metadata: name: my-workload namespace: default org: my-org # Top level, not in spec gvc: my-gvc # Top level, not in spec description: My app # Top level, not in spec spec: type: serverless # ... ``` Always use the [export feature](#exporting-resources-as-kubernetes-manifests) to generate accurate manifests. Secrets use native Kubernetes Secret objects with a special label and org annotation: ```yaml theme={null} apiVersion: v1 kind: Secret metadata: name: my-secret labels: app.kubernetes.io/managed-by: cpln-operator # Required annotations: cpln.io/org: my-org # Required data: payload: ``` The `app.kubernetes.io/managed-by: cpln-operator` label is required for the operator to manage the secret. The `cpln.io/org` annotation tells the operator which Control Plane org to sync the secret to. Deleting a Kubernetes resource while the operator is running removes the corresponding resource from Control Plane. To prevent this, add the `cpln.io/resource-policy: keep` annotation: ```yaml theme={null} metadata: name: production annotations: cpln.io/resource-policy: keep ``` Resources with this annotation remain in Control Plane even when deleted from Kubernetes. ## Get started Ready to set up the operator? Follow our step-by-step installation guide: Complete guide covering cert-manager installation, Helm deployment, authentication setup, and deploying your first resources ## Exporting resources as Kubernetes manifests You don't have to write manifests from scratch. Control Plane provides built-in tools to export resources in Kubernetes manifest format. **Export existing resources:** Select any resource and click the **Export** dropdown in the upper right corner, then choose **K8s CRD** to download the manifest. **Preview before creating:** When creating a new resource, configure it using the UI, then click **Preview** and select **K8s CRD**. This generates the manifest without deploying the resource, which is useful for storing in Git and deploying via ArgoCD. Use the `--output crd` flag to export any resource: ```bash theme={null} # Export a workload as a Kubernetes manifest cpln workload get my-workload --gvc production -o crd --org my-org # Export a GVC cpln gvc get production -o crd --org my-org # Export and save to a file cpln workload get my-workload --gvc production -o crd --org my-org > workload.yaml ``` This workflow is ideal for transitioning to GitOps: configure resources in the console UI, export them as Kubernetes manifests, commit them to Git, and let ArgoCD manage deployments. ## Deploying resources Once you have Kubernetes manifests, apply them to your cluster using kubectl: ```bash theme={null} kubectl apply -f gvc.yaml kubectl apply -f workload.yaml ``` The operator watches for resource changes and synchronizes them to Control Plane automatically. You can organize resources by namespace: * **One namespace per GVC** for GVC-scoped resources (workloads, identities, volumesets) * **One namespace per org** for org-scoped resources (GVCs, secrets, policies) For production GitOps workflows, store your manifests in Git and use ArgoCD to manage deployments. See the [installation guide](/guides/cli/cpln-operator#argocd-integration) for ArgoCD integration details. ## Additional resources Source code, CRD schemas, and issue tracking Alternative: Deploy resources from YAML using the CLI # Logs Source: https://docs.controlplane.com/core/logs Query and visualize workload logs using LogQL across all cloud providers and regions. Includes Grafana integration, live streaming, and 30-day retention. ## Overview The Logs UI aggregates logs for running [workloads](/concepts/workload) across all configured cloud providers and regions. Logs are displayed in a single view, regardless of where the workload replicas are running. Use [filters](#log-filters) to investigate issues for a specific [workload](/concepts/workload), [location](/reference/location), or container. Use [Grafana Explore](https://grafana.com/docs/grafana/latest/explore/) to query, visualize, and analyze log data. ## LogQL Control Plane logs use LogQL to query log data. For syntax and query examples, see the [LogQL documentation](https://grafana.com/docs/loki/latest/logql/). The following labels can be used to create a query: * container * gvc * location * provider * replica * stream * workload ## Log Filters Query results can be filtered by the following: * [Location](/reference/location) * [Container](/reference/workload/containers) * Start date and optional end date (the date/time selector includes helper buttons ranging from the `Last 5 minutes` to the `Last 30 days`) Selecting a [location](/reference/location) or [container](/reference/workload/containers) automatically adds the value to the LogQL query. ## Live Logs Log streams can be viewed in real time using the `Live` option. After entering the desired query, click `Live` to begin streaming logs. Click `Stop` to end the live stream. ## Grafana Clicking the `Explore on Grafana` link opens Grafana in a new tab. When you open Grafana from a specific [workload](/concepts/workload), the query is prefilled with the [GVC](/reference/gvc) and [workload](/concepts/workload). Grafana gives you the ability to "Explore your data through ad-hoc queries and dynamic drill-down. Split view and compare different time ranges, queries, and data sources side by side." View the [Grafana Explore documentation](https://grafana.com/docs/grafana/latest/explore/). The following image shows the Grafana UI after executing a query: Grafana log explorer showing LogQL query results with timestamp, level, and message fields for workload logs ## Example LogQL Queries * View the logs for a specific [GVC](/reference/gvc) and [workload](/concepts/workload) ```log Query theme={null} {gvc="GVC_NAME", workload="WORKLOAD_NAME"} ``` * Access logs with errors ```log Query theme={null} {gvc="GVC_NAME", workload="WORKLOAD_NAME", container="_accesslog"} |= "\" 50" ``` * Graph access log errors by replica ```log Query theme={null} sum(count_over_time({gvc="GVC_NAME", workload="WORKLOAD_NAME", container="_accesslog"} |= "\" 50"[1m])) by (replica) ``` * Graph access log errors by workload ```log Query theme={null} sum(count_over_time({gvc="GVC_NAME", container="_accesslog"} |= "\" 50"[1m])) by (workload) ``` * View the logs for a GVC, workload, location, and container ```log Query theme={null} {gvc="GVC_NAME", workload="WORKLOAD_NAME", location="aws-eu-central-1", container="CONTAINER_NAME"} ``` * View logs across all workloads and GVCs ```log Query theme={null} {gvc=~".+"} ``` * Count inbound requests by IP This query produces a count of inbound requests grouped by source IP address over the last 10 minutes. Set the Grafana query type to `Instant` so the results can be returned as a sortable list. ```log Query theme={null} sum(count_over_time({gvc="GVC_NAME", workload="WORKLOAD_NAME", container="_accesslog"} | logfmt | regexp "(?P\\d+\\.\\d+\\.\\d+\\.\\d+)"[10m])) by (ip) ``` * Count inbound requests by path This query returns the count of inbound requests grouped by request path for the last 10 minutes where latency exceeds 100ms. Set the Grafana query type to `Instant` so the results can be returned as a sortable list. ```log Query theme={null} sum(count_over_time({gvc="GVC_NAME", workload="WORKLOAD_NAME", container="_accesslog"} | logfmt | regexp "(?P\\/[^\"]*\\s).+\\d+ \\d+ \\d+ (?P\\d+) " | latency > 100 [10m])) by (workload, path) ``` ## Log Retention Policy Logs are stored for 30 days by default. The retention period can be adjusted for each [org](/reference/org). Learn how to [configure log shipping to an external provider](/external-logging/overview). ## CLI To view the CLI documentation for logs, see the [Logs CLI reference](/cli-reference/commands/logs). # Misc Source: https://docs.controlplane.com/core/misc Additional platform features including Grafana alerts for workload monitoring and the tags system for labeling and dynamically filtering resources. ## Grafana Alerts Alerts can be created from the org's Grafana instance. Refer to these [instructions](https://grafana.com/docs/grafana/latest/alerting/unified-alerting/alerting-rules/create-grafana-managed-rule/) on how to create a Grafana managed alert. ## Tags Most Control Plane resources can be labeled with multiple key-value pairs called tags. Tags can be used throughout the UI and by other resources to query, filter, and select resources based on tag keys and values. Most resources include a `Query` option that opens a modal allowing you to filter the resources shown. [Groups](/reference/group) and [policies](/reference/policy) can use tag queries to dynamically assign members and resources. The `Match Tags By` selector values are: | Selector | Definition | | :------- | :-------------------------- | | All | All tag items must match | | Any | At least one tag must match | | None | No tag items may match | To filter resources based on their tags: 1. Click the `Query` button 2. Select the match tag selector 3. Enter a tag name, equality operator, and value. Click `Add`. 4. Enter any additional tags 5. Click `Apply` to display the filtered resources. To remove the filter, click the `Query` button again and click `Clear`. # Query Source: https://docs.controlplane.com/core/query Filter and select resources using tags, properties, and relations. Used by groups, policies, and GVCs for dynamic resource targeting and membership. Queries allow clients and resources to filter and select other resources using [tags](/core/misc#tags), item `properties`, or item `relations`. Queries also support sorting resource lists by property in ascending or descending order. Examples of resources that use queries: * [Groups](/reference/group) can dynamically assign users * [Policies](/reference/policy) can target items or principals with queries * [GVCs](/reference/gvc) can dynamically update assigned locations *** ## Filtering A query consists of one or more terms that target tags, properties, or relations (`rel`). For a `rel` term, the value depends on the resource kind. For example, workloads have a `gvc` relation, which allows you to filter workloads in an org by the GVC they belong to. In the Console, each page that lists items of a kind has a query button at the top of the table. The `Match Tags By` selector values are: | Selector | Definition | | :------- | :-------------------------- | | All | All tag items must match | | Any | At least one tag must match | | None | No tag items may match | To filter resources based on their tags: 1. Click the `Query` button 2. Select which term you want to target: tag, property, or rel 3. Type a value for the selected term key 4. Select the operator and type the term value if needed 5. Click `Apply` and the filtered resources will be displayed To remove the filter, click the `Query` button again and click `Clear`. Every item kind supports a query command. Below are a few examples: ```bash theme={null} cpln workload query --match any --rel gvc=my-first-gvc --rel gvc=my-second-gvc ``` ```bash theme={null} cpln workload query --match all --tag environment=production --tag region=europe ``` ```bash theme={null} cpln workload query --match all --rel gvc=emea --tag payment-service=true --tag thisTagExists --tag version ``` See the [CLI reference](/cli-reference/overview) for the corresponding item kind for more details. Every item kind supports an API request path that ends with `/-query` and accepts `POST` as the HTTP method. Below are a few examples: ```text theme={null} https://api.cpln.io/org/myorg/workload/-query ``` ```text theme={null} https://api.cpln.io/org/myorg/agent/-query ``` Here is an example request body for querying with the API: ```json theme={null} { "spec": { "match": "all", "terms": [ { "op": "=", "tag": "region", "value": "emea" }, { "rel": "gvc", "op": "=", "value": "mygvc" } ], "sort": { "by": "name", "order": "asc" } } } ``` See the [API reference](/api-reference/api) for the corresponding item kind for more details. *** ## Sorting When fetching a list of items, you can sort by various properties in ascending or descending order. ### Sort Order | Order | Description | | :----- | :-------------------------------------------------- | | `asc` | Ascending order (A-Z, oldest first, lowest first) | | `desc` | Descending order (Z-A, newest first, highest first) | ### Common Sort Fields The following sort fields are available for most resources: | Field | Description | | :------------- | :-------------------------- | | `id` | Unique resource identifier | | `name` | Resource name | | `version` | Resource version number | | `description` | Resource description | | `created` | Creation timestamp | | `lastModified` | Last modification timestamp | ### Resource-Specific Sort Fields Some resources have additional sort fields: | Field | Description | | :--------- | :------------------------------- | | `origin` | Origin of the location | | `provider` | Cloud provider (aws, gcp, azure) | | `region` | Cloud region | | Field | Description | | :------- | :------------------ | | `origin` | Origin of the group | | Field | Description | | :--------- | :------------------------------------ | | `provider` | Cloud provider (aws, gcp, azure, ngs) | | Field | Description | | :------ | :----------------- | | `idp` | Identity provider | | `email` | User email address | | Field | Description | | :------- | :------------------- | | `origin` | Origin of the policy | # Security Source: https://docs.controlplane.com/core/security Platform security model covering authentication, authorization, TLS certificates, cipher suites, domain customization, and 12-factor app principles. ## Authentication and Authorization ### User Accounts Access to the Control Plane console or CLI is granted by authenticating with single sign-on (SSO) through one of the following providers: * Google * GitHub * Microsoft * Security Assertion Markup Language (SAML) Multi-factor authentication (MFA) is recommended for your chosen provider. ### Service Accounts Access to the Control Plane CLI using a [service account](/reference/serviceaccount) is granted through a generated token. During token generation, the token can be copied to the clipboard or downloaded. Once the token modal is dismissed, the token is no longer available for display or retrieval. If the token is lost or compromised, it must be regenerated. ### Authorization Authorization for all Control Plane resources is controlled using fine-grained policies assigned to the following principal types: * [Users](/reference/user) * [Groups](/reference/group) * [Service accounts](/reference/serviceaccount) * [Workload identities](/reference/identity) ## TLS Certificates ### External Certificates All communications from external sources use end-to-end TLS to destination workloads. The server certificates are generated by [Let's Encrypt](https://letsencrypt.org) and are rotated every 60 days. Default workload endpoints allow TLS 1.2 or greater with the following ciphers: * TLS\_RSA\_WITH\_AES\_256\_GCM\_SHA384 * TLS\_RSA\_WITH\_AES\_128\_GCM\_SHA256 * TLS\_ECDHE\_RSA\_WITH\_CHACHA20\_POLY1305\_SHA256 * TLS\_ECDHE\_RSA\_WITH\_AES\_256\_GCM\_SHA384 * TLS\_ECDHE\_RSA\_WITH\_AES\_128\_GCM\_SHA256 * TLS\_CHACHA20\_POLY1305\_SHA256 * TLS\_AES\_256\_GCM\_SHA384 * TLS\_AES\_128\_GCM\_SHA256 When using a [domain](/reference/domain), the allowed TLS version and ciphers can be customized. ### Internal Certificates All internal communication between workloads and from workloads to other Control Plane services uses mutual TLS (mTLS) with a unique client certificate per workload. Workload client certificates are rotated every hour and use TLSv1.2 with the ECDHE-RSA-AES256-GCM-SHA384 cipher. ## Firewall Security Control Plane uses industry-standard firewall technologies. All workloads are configured to be fully restricted with no internal or external communication enabled by default, except for internal health check monitoring. ### External Firewall Inbound access to a workload can be enabled or disabled for the entire internet, or limited to a specific list of CIDRs. Outbound access from a workload can be enabled or disabled for the entire internet, or limited to a specific list of CIDRs or hostnames. ### Internal Firewall By default, the workload's internal firewall is disabled. Each workload can be configured to allow inbound communications from: * Workloads in the same [GVC](/reference/gvc) * Workloads in the same [org](/reference/org) * Specific workloads * Workloads in the same GVC as well as specific workloads from other GVCs in the same org * The workload itself, which enables replicas of the workload to access other replicas ### Network Resource Access Network resource access via [agents](/reference/agent) (configured within a [workload identity](/reference/identity)) is implicitly allowed through the firewall. ## Workload Access * The internal Kubernetes API access is restricted from all workloads by multiple firewall levels. * No Kubernetes access tokens are available to the workloads. * Access to kernel system calls is filtered using gVisor, which provides an isolation boundary between the application and the host kernel. * Workloads can communicate with: * The Control Plane metadata service, which provides short-lived cloud credentials based on the identity of the workload. * The Control Plane [audit service](/reference/auditctx). ## GVC Isolation Every workload receives discovery information for other workloads across the org, but communication is disabled by default using firewalls and client certificate validation. ## Workload Isolation All workloads are isolated at the org level based on the use of: * Host-based firewalls * Client certificates * Proxies Direct communication between containers residing in different orgs is not possible. External endpoints can be used to communicate with workloads in other orgs. Isolation between workloads within an org is defined based on the workloads' internal firewall configuration. ## Container Isolation Containers are isolated by the use of: * cgroups * Namespaces * Restricted syscalls * Restricted capabilities (groups of syscalls) ## Security Headers The following headers are sanitized and replaced with valid content before being forwarded to running workloads: * x-envoy-external-address * Used to verify the source address of the external requester. * x-forwarded-client-cert * Used to verify that communications between workloads were made using mTLS and to verify the identity of the requesting workload. * x-forwarded-for * x-forwarded-proto * x-request-id * referer ## Identity Cloud Access ### Amazon * Leverages AWS roles and policies to create least-privileged, short-lived tokens that are assigned to workloads during startup. * Network traffic between Control Plane and the AWS API is over a TLS connection. * During the creation of a Cloud Account targeting AWS, a policy within an AWS account is created that allows the Control Plane AWS account the ability to perform the following actions: * iam:CreatePolicy * iam:UpdateAssumeRolePolicy * iam:DetachRolePolicy * iam:TagRole * iam:UpdateRoleDescription * iam:DeletePolicy * iam:CreateRole * iam:DeleteRole * iam:AttachRolePolicy * iam:UpdateRole * iam:PutRolePolicy * iam:TagPolicy ### Azure Connector * Leverages Azure Function Apps to create least-privileged, short-lived tokens that are assigned to workloads during startup. * Network traffic between Control Plane and the Azure Function App endpoint is over a TLS connection and the request body is signed and encrypted using [JOSE](https://jose.readthedocs.io/en/latest/). * The Function App is assigned the owner role within the Azure subscription. Users with permissions to create/update workload identities have the ability to assign any scope and roles within the subscription. ### Google Cloud * Leverages GCP service accounts to create least-privileged, short-lived tokens that are assigned to workloads during startup. * Network traffic between Control Plane and the GCP API is over a TLS connection. * During the creation of a cloud account targeting GCP, the Control Plane GCP service account is added to a project and granted the following roles: * Viewer * Project IAM Admin * Service Account Admin * Service Account Token Creator ## Data Security ### Log Access and Retention Policy All logs generated by an org are accessible only to users with the `readLogs` [permission](/reference/org#permissions). Logs are retained for 30 days by default. Retention settings for logs, metrics, and traces can be adjusted on the [org](/reference/org). ### Secrets Org-level secrets are encrypted at rest using envelope encryption and protected with TLS while in transit. Secrets are stored on multiple cloud providers using cloud-based Hardware Security Modules (HSM). ## Platform Security ### Vulnerability Policy Security updates and patches are applied regularly and meet all compliance and regulatory requirements. For zero-day vulnerabilities, updates are applied as soon as they are available and verified. All scheduled maintenance that could cause downtime is communicated via email and Discord. If you find any security issues or have security questions, please email [secops@controlplane.com](mailto:secops@controlplane.com). ## Platform Compliance ### Data Centers The Control Plane platform is hosted at the following providers: * Amazon via Amazon Web Services (AWS) * Google via Google Cloud Platform (GCP) * Microsoft via Azure Each provider complies with the following: * Sarbanes-Oxley (SOX) * ISO 27001 * SOC 1 and SOC 2/SSAE 16/ISAE 3402 (previously SAS 70 Type II) * PCI Level 1 [AWS](https://aws.amazon.com/compliance/pci-dss-level-1-faqs/), [Azure](https://docs.microsoft.com/en-us/azure/compliance/offerings/offering-pci-dss), [GCP](https://cloud.google.com/security/compliance/pci-dss) * FISMA Moderate ### Payment Processing [Stripe](https://stripe.com/) is used to encrypt and process credit card payments. Stripe is [PCI Service Provider Level 1](https://stripe.com/docs/security/stripe) compliant. ## Platform Privacy Control Plane is committed to protecting customer privacy. For details, see the [Privacy Policy](https://controlplane.com/privacy-policy). Support staff have access to the following: * Infrastructure support staff have access to monitor workload activity to maintain the stability of the platform. # Terms Source: https://docs.controlplane.com/core/terms Key terminology for the Control Plane platform including kinds, items, context, org/GVC scoping, and resource links. ## Kind Each object in Control Plane belongs to a kind. Kinds are the fundamental resources you create, manage, and connect, such as `Workload`, `VolumeSet`, `Agent`, and `Service Account`. Each instance of a kind is referred to as an `item` in the platform. The Control Plane API powers all clients, including the Terraform provider, Pulumi provider, Console UI, and `cpln` CLI. API requests usually act on one item or multiple items. ## Context All clients work better within a context, which is a pair of an `org` item and a `gvc` item. Context makes it easier for clients to refer to items within an org or [GVC](/reference/gvc). Some kinds are scoped to a GVC, while others are scoped to an org. `Workload`, `Identity`, and `VolumeSet` kinds are scoped under a GVC. All other kinds are scoped under an org. ## Links Each `item` is accessible by its link. This URL-like address is an essential part of the platform. There are two ways to structure an item link, which is called a `selfLink`: `fullLink` and `relativeLink`. When a kind is scoped to a GVC, its relative link must include the GVC name. A relative link relies on the `context` set in the client being used. The following examples show org-scoped and GVC-scoped item links. | Kind | Item Name | Full Link | Relative Link | | -------- | ---------- | ---------------------------------------- | ------------------------------- | | Org | myorg | /org/myorg | NA | | Secret | mysecret | /org/myorg/secret/mysecret | //secret/mysecret | | GVC | mygvc | /org/myorg/gvc/mygvc | //gvc/mygvc | | Workload | myworkload | /org/myorg/gvc/mygvc/workload/myworkload | //gvc/mygvc/workload/myworkload | ## Clients There are multiple ways to use the platform, referred to as `clients`. Examples include the Console UI, Control Plane API, `cpln` CLI, Terraform provider, and Pulumi provider. ## Containers Refer to the [Kubernetes containers](https://kubernetes.io/docs/concepts/containers/) concepts page. ## Grafana Alerts Alerts can be created from the org's Grafana instance. Refer to these [instructions](https://grafana.com/docs/grafana/latest/alerting/unified-alerting/alerting-rules/create-grafana-managed-rule/) on how to create a Grafana managed alert. ### Built-in Alert Rules The Grafana instance accessed from the `Metrics` link of your workload includes several automatically provisioned objects, including built-in alert rules. These rules can send notifications to one or more preferred contact points, such as email. By default, notifications for these alerts are disabled. To receive notifications, add a contact point. For details, see the Grafana [contact point documentation](https://grafana.com/docs/grafana/latest/alerting/fundamentals/notifications/contact-points/). You can find the built-in alert rules in your Grafana instance under `Alerts` > `Alert rules`: * `container-restarts` * This alert triggers if any of your workloads restart repeatedly. Specifically, it activates if the Control Plane custom metric `container_restarts` exceeds 1 within a 5 minute period. * `stuck-deployments` * This alert triggers if any of your deployments are stuck. * It checks for multiple container restarts within specific groups (GVC and workload). If any group exceeds one restart in the last 15 minutes, it triggers an alert. To edit or create new alerts, go to the `Alerts` > `Alert rules` section in your Grafana instance. If you edit built-in alerts, your changes will be saved. However, if you delete any built-in alerts, they will be recreated automatically the next time you log in to Grafana. To disable built-in alerts, edit the corresponding alert rule and turn on the `Pause evaluation` toggle in the alert editing view. ## Public IPs The list of public IPs for each cloud provider and region can be obtained. Navigate to the `Locations` list page from the main navigation bar. Select a location to view the IP ranges listed on the `Info` page. Use the command below to view the IP ranges of all locations. ```bash theme={null} cpln location get -o json ``` The output can also be formatted as YAML by using the flag `-o yaml` instead of `-o json`. To view a single location, replace `{locationName}` with a valid location name in the command below. ```bash theme={null} cpln location get {locationName} -o json ``` See the [CLI location command reference](/cli-reference/commands/location) for more details. The location list endpoint and location-specific endpoint return the location object with the `ipRanges` property. See the [Get All Locations API endpoint](/api-reference/location/get-all-locations) for details. The list of IPs may be required by external services that restrict which IPs are allowed to access them. The IPs may change when underlying infrastructure changes. Control Plane recommends automating location API checks and updating external services' allowed IP address lists when changes occur. ## Workload Deployment Errors * `Error: exitCode: 1 message: standard_init_linux.go:228: exec user process caused: exec format error` * The configured image was built for the incorrect platform. Control Plane requires the image target `amd64`. Refer to the [Push an Image Guide](/guides/push-image#step-2-build-a-new-image-using-docker-and-a-dockerfile-optional) for additional details. # CloudWatch Source: https://docs.controlplane.com/external-logging/cloudwatch Configure log shipping from Control Plane to AWS CloudWatch for real-time monitoring and log analysis. ## Overview [AWS CloudWatch](https://aws.amazon.com/cloudwatch/) is a comprehensive monitoring and management service designed to provide real-time insights into your AWS resources and applications. It enables you to collect and track metrics, collect and monitor log files, set alarms, and automatically react to changes in your AWS environment. By utilizing [AWS CloudWatch](https://aws.amazon.com/cloudwatch/), you can gain valuable insights into your system's performance, troubleshoot issues promptly, and maintain a high level of operational health. Its integration with other AWS services makes it an essential tool for maintaining and optimizing your cloud infrastructure. Follow the steps below to configure log shipping to CloudWatch. ## Step 1 - Credential Procurement AWS credentials are required to ship logs to CloudWatch. To obtain AWS credentials and store it, follow the guide here: [AWS key](/reference/secret#amazon-web-services-aws) ## Step 2 - Configure External Logging External logging can be configured by using either the [UI Console](#enable-logging-using-the-ui-console) or [CLI](#enable-logging-using-the-cli). For `groupName` and `streamName` follow [Fluent Bit templating](https://docs.fluentbit.io/manual/administration/configuring-fluent-bit/classic-mode/record-accessor#limitations-of-record_accessor-templating). Use $stream, $location, $provider, $replica, $workload, $gvc, $org, $container, \$version to template. ### Enable Logging using the UI Console 1. From the Control Plane Console UI, click on `Org` in the left menu. 2. Click `External Logs` in the middle context menu. 3. Select `CloudWatch` and fill out the required fields. 4. Select the AWS secret created to authenticate to AWS. Refer to the [credential procurement](#step-1-credential-procurement) section to obtain and configure the necessary credentials. 5. Click `Save`. 6. After the configuration is complete, log entries will be available at CloudWatch within a few minutes. ### Enable Logging using the CLI The external logging configuration can be created / updated using the CLI's `cpln org patch ORG_NAME -f FILE.yaml` command. Below is an example of an [Org](/reference/org) manifest (in YAML). Edit and save the YAML as a file and use it as an input to the CLI's `cpln org patch ORG_NAME -f FILE.yaml` command. Refer to the [credential procurement](#step-1-credential-procurement) section to obtain and configure the necessary credentials. * Substitute: ORG\_NAME, AWS\_SECRET and possibly the region, retentionDays, groupName and streamName. ```yaml YAML theme={null} kind: org name: ORG_NAME spec: logging: cloudWatch: region: us-east-1 credentials: //secret/AWS_SECRET retentionDays: 7 groupName: $gvc streamName: $workload ``` # Coralogix Source: https://docs.controlplane.com/external-logging/coralogix Configure log shipping from Control Plane to Coralogix for log management, analytics, and visualization. ## Overview [Coralogix](https://coralogix.com/) is a log management and analytics platform designed to simplify log data storage, analysis, and visualization. It offers various features that make it an excellent choice for storing server logs. Using [Coralogix](https://coralogix.com/) to store server logs can streamline your log management process, providing you with valuable insights into your server's performance, identifying potential issues early on, and improving overall application and system reliability. Follow the steps below to configure log shipping to Coralogix. ## Step 1 - Credential Procurement An API key is required to ship logs to Coralogix. Follow these steps to obtain the API key, store it as an [Opaque Secret](/reference/secret#opaque), and configure external logging. 1. From the Coralogix Dashboard, click the `Data Flow` link (in the header), then click the `API Keys` link. 2. Generate and/or copy the `Send Your Data` API Key. 3. From the Control Plane Console UI, click `Secrets` from the left menu. 4. Click the `New` button. 5. Enter a `Name` for the secret, and select `Opaque` from the Secret Type list. 6. Paste the string from step #2 into the content text box and click `Save`. 7. This secret will be used when configuring logging using the [UI Console](#enable-logging-using-the-ui-console) or [CLI](#enable-logging-using-the-cli). ## Step 2 - Configure External Logging External logging can be configured by using either the [UI Console](#enable-logging-using-the-ui-console) or [CLI](#enable-logging-using-the-cli). ### Enable Logging using the UI Console 1. From the Control Plane Console UI, click on `Org` in the left menu. 2. Click `External Logs` in the middle context menu. 3. Select `Coralogix` and fill out the required fields. 4. Select the Opaque secret created to authenticate to Coralogix. Refer to the [credential procurement](#step-1-credential-procurement) section to obtain and configure the necessary credentials. 5. Click `Save`. 6. After the configuration is complete, log entries will be available at Coralogix within a few minutes. The cluster name will be the domain name that resolves when using the Coralogix dashboard. ### Enable Logging using the CLI The external logging configuration can be created / updated using the CLI's `cpln org patch ORG_NAME -f FILE.yaml` command. Below is an example of an [Org](/reference/org) manifest (in YAML). Edit and save the YAML as a file and use it as an input to the CLI's `cpln org patch ORG_NAME -f FILE.yaml` command. Refer to the [credential procurement](#step-1-credential-procurement) section to obtain and configure the necessary credentials. * Substitute: `ORG_NAME`, `OPAQUE_SECRET`, and possibly the cluster. Use the cluster URL that matches your Coralogix account. The cluster will be the domain name that resolves when using the Coralogix dashboard. ```yaml YAML theme={null} kind: org name: ORG_NAME spec: logging: coralogix: cluster: coralogix.com credentials: //secret/OPAQUE_SECRET ``` # Datadog Source: https://docs.controlplane.com/external-logging/datadog Configure log shipping from Control Plane to Datadog for comprehensive log management, analytics, and visualization. ## Overview [Datadog](https://www.datadoghq.com) is a robust and comprehensive monitoring service for cloud-scale applications, providing full visibility into IT infrastructure. The service combines metrics and events from servers, databases, applications, tools, and services to present a unified view of an entire stack. [Datadog](https://www.datadoghq.com) is capable of ingesting, analyzing, and visualizing logs from a variety of sources, including servers. This feature is a part of [Datadog](https://www.datadoghq.com/)'s Log Management solution, which provides essential capabilities such as real-time log tailing and filtering, log analytics, and detailed visualizations. It can handle a wide range of log formats, including but not limited to, JSON, syslog, and common application logs. [Datadog](https://www.datadoghq.com) offers capabilities for log retention and archiving, enabling compliance with various regulatory standards. It also supports role-based access controls to secure your logs and control who can access what information. Follow the steps below to configure log shipping to [Datadog](https://www.datadoghq.com). ## Step 1 - Credential Procurement An API key is required to ship logs to [Datadog](https://www.datadoghq.com). Follow these steps to obtain the API key, store it as an [Opaque Secret](/reference/secret#opaque), and configure external logging. 1. From the [Datadog](https://www.datadoghq.com) dashboard, hover over your username at the bottom of the left menu and click `Organization Settings`. 2. In the middle menu, click `API Keys`. 3. Click the `New Key` button in the upper right corner, enter a key name, and click `Create Key`. 4. Click the `Copy Key` button. This will copy the key to your clipboard. Click the `X` to close the modal. 5. From the Control Plane Console UI, click `Secrets` from the left menu. 6. Click the `New` button. 7. Enter a `Name` for the secret, and select `Opaque` from the `Secret Type` list. 8. Paste the string from step #4 into the content text box and click `Create`. 9. This secret will be used when configuring logging using the [UI Console](#enable-logging-using-the-ui-console) or [CLI](#enable-logging-using-the-cli). ## Step 2 - Configure External Logging External logging can be configured by using either the [UI Console](#enable-logging-using-the-ui-console) or [CLI](#enable-logging-using-the-cli). ### Enable Logging using the UI Console 1. From the Control Plane Console UI, click on `Org` in the left menu. 2. Click `External Logs` in the middle context menu. 3. Select `Datadog` and fill out the required fields. 4. Select the Opaque secret created to authenticate to [Datadog](https://www.datadoghq.com). Refer to the [credential procurement](#step-1-credential-procurement) section to obtain and configure the necessary credentials. 5. Click `Save`. 6. After the configuration is complete, log entries will be available at [Datadog](https://www.datadoghq.com) within a few minutes. The hostname will be similar to the domain name that resolves when using the [Datadog](https://www.datadoghq.com) dashboard. (i.e., The host `http-intake.logs.us3.datadoghq.com` maps to the dashboard domain `us3.datadoghq.com`) ### Enable Logging using the CLI The external logging configuration can be created / updated using the CLI's `cpln org patch ORG_NAME -f FILE.yaml` command. Below is an example of an [Org](/reference/org) manifest (in YAML). Edit and save the YAML as a file and use it as an input to the CLI's `cpln org patch ORG_NAME -f FILE.yaml` command. Refer to the [credential procurement](#step-1-credential-procurement) section to obtain and configure the necessary credentials. * Substitute: `ORG_NAME`, `OPAQUE_SECRET`, and possibly the host. Use the host URL that matches your [Datadog](https://www.datadoghq.com) account. The host will be a similar domain name that resolves when using the [Datadog](https://www.datadoghq.com) dashboard. ```yaml YAML theme={null} kind: org name: ORG_NAME spec: logging: datadog: host: http-intake.logs.us3.datadoghq.com credentials: //secret/OPAQUE_SECRET ``` # Logz.io Source: https://docs.controlplane.com/external-logging/logz-io Configure log shipping from Control Plane to Logz.io for observability with AI-powered log analysis and real-time visualization. ## Overview [Logz.io](https://www.logz.io) is a comprehensive observability platform that provides better visibility into applications and systems. The fully managed SaaS solution allows you to collect, parse, and analyze logs, metrics, traces, and security data in one place, using AI and machine learning to help identify and resolve issues, respond faster to incidents, and manage costs effectively. With [Logz.io](https://www.logz.io), you can aggregate and visualize your server log data in real-time, making it easier understanding the events happening across your infrastructure easier. [Logz.io](https://www.logz.io) supports long-term log retention and is built with security in mind, providing features like role-based access control, ensuring that your sensitive log data is properly protected. Follow the steps below to configure shipping data from Control Plane to [Logz.io](https://www.logz.io): ## Step 1 - Generate Token Credentials [Logz.io](https://www.logz.io) requires an authorization token in order to process data and logs. You’ll need to store the token as an [Opaque Secret](/reference/secret#opaque) and configure external logging. 1. Log into your [Logz.io](https://www.logz.io) account and open the [Data shipping tokens](https://app.logz.io/#/dashboard/settings/manage-tokens/data-shipping?product=logs) page. 2. Copy the **listener URL** and the relevant **account token** you want to use. Next, log into your [Control Plane Console UI](https://console.cpln.io/), and follow these steps: 1. Navigate to `Secrets` and create a new secret. 2. Name your secret and select `Opaque` from the Secret Type list. 3. Paste the [Logz.io](https://www.logz.io) token string into the content text box and click Create. This secret will be used when configuring logging using the [UI Console](#enable-logging-using-the-ui-console) or [CLI](#enable-logging-using-the-cli). ## Step 2 - Configuring Shipping Data to Logz.io You need to enable logging from your [UI Console](#enable-logging-using-the-ui-console) or [CLI](#enable-logging-using-the-cli). ### Enable Logging using the UI Console 1. Open the [Control Plane Console UI](https://console.cpln.io/), click on `Org` in the left menu. 2. Click `External Logs` in the middle context menu. 3. Select `Logz.io` and fill out the required fields. 4. Select the Opaque secret created to authenticate to [Logz.io](https://www.logz.io). Refer to the [generate token credentials](#step-1-generate-token-credentials) section to obtain and configure the necessary credentials. 5. Click Save. You will be able to see log entries at [Logz.io](https://www.logz.io) shortly after the configuration has been completed. The `Listener Host` value is shown on the [Manage tokens](https://app.logz.io/#/dashboard/settings/manage-tokens/data-shipping?product=logs) page. ### Enable Logging using the CLI The external logging configuration can be created / updated using the CLI's `cpln org patch ORG_NAME -f FILE.yaml` command. Below is an example of an [Org](/reference/org) manifest (in YAML). Edit and save the YAML as a file and use it as an input to the CLI's `cpln org patch ORG_NAME -f FILE.yaml` command. Refer to the [generate token credentials](#step-1-generate-token-credentials) section to obtain and configure the necessary credentials. * Substitute: `ORG_NAME`, `OPAQUE_SECRET`, and possibly the listener host. Use the listener host URL that matches your [Logz.io](https://www.logz.io) account. The host will be a similar domain name that resolves when using the [Logz.io](https://www.logz.io) dashboard. ```yaml YAML theme={null} kind: org name: ORG_NAME spec: logging: logzio: credentials: //secret/OPAQUE_SECRET listenerHost: listener.logz.io ``` # Loki Source: https://docs.controlplane.com/external-logging/loki Forward Control Plane org logs natively to Grafana Loki or any Loki-compatible backend over the Loki push API. ## Overview [Grafana Loki](https://grafana.com/oss/loki/) is a horizontally scalable, multi-tenant log aggregation system. Control Plane can forward all Org logs natively to any Loki-compatible target — a self-hosted Loki, [Grafana Cloud Logs](https://grafana.com/products/cloud/logs/), or any backend that accepts the Loki push API — using the native Loki output rather than a generic protocol. Because logs are sent over the native Loki API, Control Plane log metadata is attached as indexed Loki [labels](#forwarded-log-labels) (including `service_name`, which drives Grafana's service detection), so logs are immediately queryable by workload, GVC, location, and more. Follow the steps below to configure log shipping to Loki. ## Step 1 - Prerequisites * **Endpoint**: A reachable URL for your Loki instance (see [Endpoint Format](#endpoint-format)). * **Authentication** (optional): A [Username & Password Secret](/reference/secret#username-and-password) for HTTP basic auth, if your Loki target requires it (Grafana Cloud and most hosted Loki services do). * **Tenant ID** (optional): The tenant identifier for multi-tenant Loki deployments (for example, the user/instance ID for Grafana Cloud). ## Step 2 - Configure External Logging External logging can be configured by using either the [UI Console](#enable-logging-using-the-ui-console) or [CLI](#enable-logging-using-the-cli). ### Enable Logging using the UI Console 1. From the Control Plane Console UI, click on `Org` in the left menu. 2. Click `External Logs` in the middle context menu. 3. Select `Loki` and fill out the endpoint, and the credentials and tenant ID if your target requires them. 4. Click `Save`. 5. After the configuration is complete, log entries will begin forwarding to Loki within a few minutes. ### Enable Logging using the CLI The external logging configuration can be created / updated using the CLI's `cpln org patch ORG_NAME -f FILE.yaml` command. Below is an example of an [Org](/reference/org) manifest (in YAML). Edit and save the YAML as a file and use it as an input to the CLI's `cpln org patch ORG_NAME -f FILE.yaml` command. * Substitute: `ORG_NAME`, the `endpoint`, and (if required) `USERPASS_SECRET` and `tenantId`. ```yaml YAML theme={null} kind: org name: ORG_NAME spec: logging: loki: endpoint: https://logs-prod-001.grafana.net credentials: //secret/USERPASS_SECRET tenantId: "123456" ``` `credentials` and `tenantId` are both optional. Provide whichever your target requires (or neither for an unauthenticated, single-tenant endpoint). ## Endpoint Format The `endpoint` is required. TLS, the port, and the push path are derived from its format: | Endpoint | TLS | Port | Push URI | | ---------------------------- | --- | -------------------- | -------------------------------------- | | `https://host[:port][/path]` | On | From URL, else `443` | The URL path, else `/loki/api/v1/push` | | `http://host[:port][/path]` | Off | From URL, else `80` | The URL path, else `/loki/api/v1/push` | | `host[:port]` | On | As given, else `443` | `/loki/api/v1/push` | Provide the base URL (for example `https://logs-prod-001.grafana.net`) and Control Plane will append the default `/loki/api/v1/push` path. Include an explicit path only if your target uses a non-standard push endpoint. ## Authentication If your Loki target requires authentication, set `credentials` to a [Username & Password Secret](/reference/secret#username-and-password). The username and password are sent as HTTP basic auth on every push request. For Grafana Cloud, the username is your instance/user ID and the password is a Grafana Cloud access token. Omit `credentials` for an unauthenticated endpoint, such as a Loki instance reachable only on a trusted internal network. ## Multi-Tenant Loki For multi-tenant Loki deployments, set `tenantId` to the tenant the logs should be written under. It is sent as the tenant ID on each push request. Leave it unset for single-tenant Loki. ## Forwarded Log Labels Each forwarded record carries the Control Plane log metadata as indexed Loki labels, and the log message body as the log line: | Label | Description | | -------------- | ------------------------------------------------ | | `service_name` | Workload name (drives Grafana service detection) | | `workload` | Workload name | | `org` | Org name | | `gvc` | GVC name | | `location` | Location the log originated from | | `provider` | Cloud provider | | `version` | Workload version | | `container` | Container name | | `stream` | Output stream (`stdout` or `stderr`) | | `replica` | Replica name | All logs continue to remain accessible through Control Plane, adhering to the [current log retention policy](/core/logs#log-retention). # OpenTelemetry Source: https://docs.controlplane.com/external-logging/opentelemetry Forward Control Plane org logs to any OpenTelemetry-compatible collector or backend over OTLP/HTTP. ## Overview [OpenTelemetry](https://opentelemetry.io) is a vendor-neutral standard for collecting and exporting telemetry data. Control Plane can forward all Org logs to any OpenTelemetry-compatible target — an [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/) or any backend that accepts logs over OTLP/HTTP — letting you route logs to a destination of your choice and fan them out from there. Follow the steps below to configure log shipping to an OpenTelemetry target. ## Step 1 - Prerequisites * **Endpoint**: A reachable URL for your OpenTelemetry collector or OTLP/HTTP backend (see [Endpoint Format](#endpoint-format)). * **Authentication** (optional): Either custom [headers](#authentication) or an [Opaque Secret](/reference/secret#opaque) holding an `Authorization` header value, depending on what your target requires. ## Step 2 - Configure External Logging External logging can be configured by using either the [UI Console](#enable-logging-using-the-ui-console) or [CLI](#enable-logging-using-the-cli). ### Enable Logging using the UI Console 1. From the Control Plane Console UI, click on `Org` in the left menu. 2. Click `External Logs` in the middle context menu. 3. Select `OpenTelemetry` and fill out the endpoint and any headers or credentials your target requires. 4. Click `Save`. 5. After the configuration is complete, log entries will begin forwarding to your OpenTelemetry target within a few minutes. ### Enable Logging using the CLI The external logging configuration can be created / updated using the CLI's `cpln org patch ORG_NAME -f FILE.yaml` command. Below is an example of an [Org](/reference/org) manifest (in YAML). Edit and save the YAML as a file and use it as an input to the CLI's `cpln org patch ORG_NAME -f FILE.yaml` command. * Substitute: `ORG_NAME`, the `endpoint`, and any `headers` or `credentials` your target requires. ```yaml YAML theme={null} kind: org name: ORG_NAME spec: logging: opentelemetry: endpoint: https://otel-collector.example.com/v1/logs headers: x-custom-header: value credentials: //secret/OPAQUE_SECRET ``` `headers` and `credentials` are both optional. Provide whichever your target requires (or neither for an unauthenticated endpoint). ## Endpoint Format The `endpoint` is required. TLS and the port are derived from its format: | Endpoint | TLS | Port | Notes | | -------------------------- | --- | -------------------- | ---------------------------------------------------------------- | | `https://host[:port]/path` | On | From URL, else `443` | Recommended. The path (e.g. `/v1/logs`) is used as the logs URI. | | `http://host[:port]/path` | Off | From URL, else `80` | Unencrypted; use only for in-cluster collectors. | | `host:port` | On | As given | TLS is always enabled for this form. | | `host` | On | `443` | TLS enabled, default port. | Use the full `https://host/v1/logs` form so the protocol, port, and logs path are explicit. ## Authentication Most targets require an authentication header. There are two ways to provide it: * **`headers`** — a map of header name/value pairs sent on every export. Use this for any header your target expects, such as `api-key`, `x-…`, or `Authorization`. This is the most flexible option. * **`credentials`** — a link to an [Opaque Secret](/reference/secret#opaque) whose value is sent as the `Authorization` header. Use this when you want to keep the credential in a secret rather than inline in the Org manifest. When using `credentials`, store the **full `Authorization` header value, base64-encoded**, in the Opaque Secret (set the secret's `encoding` to `base64`). Control Plane base64-decodes the payload and sends the result as the `Authorization` header. A target that authenticates with a non-`Authorization` header must use `headers` instead. ## Forwarded Log Attributes Each forwarded record carries the Control Plane log metadata as OpenTelemetry log attributes: | Attribute | Description | | ---------- | -------------------------------- | | `org` | Org name | | `gvc` | GVC name | | `workload` | Workload name | | `version` | Workload version | | `replica` | Replica name | | `location` | Location the log originated from | | `provider` | Cloud provider | | `log` | The log message body | All logs continue to remain accessible through Control Plane, adhering to the [current log retention policy](/core/logs#log-retention). # Overview Source: https://docs.controlplane.com/external-logging/overview Ship org logs to external providers like S3, CloudWatch, Coralogix, Datadog, Logz.io, and Stackdriver for off-site storage and analysis. Log shipping to an external provider is a data replication and disaster recovery strategy used to keep a copy of server logs in an off-site location, often hosted by a third-party service provider. This approach ensures that critical server logs are safely stored in a separate location, providing an additional layer of protection in case of data loss, server failure, or other emergencies. When logs are shipped to an external provider, all logs will remain accessible through Control Plane, adhering to the [current log retention policy](/core/logs#log-retention). Log shipping can be configured to multiple external providers. Control Plane offers the ability to ship all Org logs to the following providers: 1. [Amazon S3](/external-logging/s3) 2. [CloudWatch](/external-logging/cloudwatch) 3. [Coralogix](/external-logging/coralogix) 4. [Datadog](/external-logging/datadog) 5. [Logz.io](/external-logging/logz-io) 6. [Loki](/external-logging/loki) 7. [Stackdriver](/external-logging/stackdriver) 8. [Syslog](/external-logging/syslog) 9. [OpenTelemetry](/external-logging/opentelemetry) # Amazon S3 Source: https://docs.controlplane.com/external-logging/s3 Configure log shipping from Control Plane to Amazon S3 for scalable, durable cloud storage of your org logs. ## Overview [Amazon S3](https://aws.amazon.com/s3/) (Simple Storage Service) is a highly scalable, durable, and secure cloud storage service offered by Amazon Web Services (AWS). It is commonly used for various data storage purposes, including storing server logs. Using [S3](https://aws.amazon.com/s3/) to store server logs provides a reliable, scalable, and secure solution for retaining valuable log data. The centralized storage also simplifies log management, enhances data analysis capabilities, and supports compliance and auditing requirements. AWS's extensive global infrastructure ensures that your log data is available and accessible from anywhere with low latency, making [S3](https://aws.amazon.com/s3/) a popular choice for log storage in the cloud. Follow the steps below to configure log shipping to [Amazon S3](https://aws.amazon.com/s3/). ## Step 1 - Credential Procurement Authentication credentials are required to ship logs to [S3](https://aws.amazon.com/s3/). Follow these steps to obtain the credentials, store them as an [AWS Secret](/reference/secret#amazon-web-services-aws), and configure external logging. 1. Refer to [these instructions](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html) to create a new AWS IAM user in your AWS account. * Select `Programmatic access` when creating the user and take note of the Access and Secret Key. 2. This user, at a minimum, must have an associated policy with the `s3:PutObject` action. **Sample AWS Policy (substitute S3\_BUCKET\_NAME):** ```json JSON theme={null} { "Version": "2012-10-17", "Statement": [ { "Sid": "VisualEditor0", "Effect": "Allow", "Action": "s3:PutObject", "Resource": "arn:aws:s3:::S3_BUCKET_NAME/*" } ] } ``` 3. From the Control Plane Console UI, click `Secrets` from the left menu. 4. Click the `New` button. 5. Enter a `Name` for the secret, and select `AWS` from the Secret Type list. 6. Enter the `Access Key` and `Secret Key` and click `Save`. 7. This secret can now be used when configuring logging using the [UI Console](#enable-logging-using-the-ui-console) or [CLI](#enable-logging-using-the-cli). ## Step 2 - Configure External Logging External logging can be configured by using either the [UI Console](#enable-logging-using-the-ui-console) or the [CLI](#enable-logging-using-the-cli). ### Enable Logging using the UI Console 1. From the Control Plane Console UI, click on `Org` in the left menu. 2. Click `External Logs` in the middle context menu. 3. Select `S3` and fill out the required fields. 4. Select the AWS secret to authenticate to S3. Refer to the [credential procurement](#step-1-credential-procurement) section to obtain and configure the necessary credentials. 5. Click `Save`. 6. After the configuration is complete, log entries will be available at S3 within a few minutes. The `prefix` will be the folder where the logs will be written. The folder structure will follow the format: `PREFIX/ORG_NAME/YEAR/MONTH/DAY/HOUR/MINUTE/LOG_FILE.jsonl` The .jsonl file will contain \~1-3k of single line log entries in JSON. Each entry will contain the following keys: * time * log * location, * version * provider * container * replica * workload * gvc * org * stream ### Enable Logging using the CLI The external logging configuration can be created / updated using the CLI's `cpln org patch ORG_NAME -f FILE.yaml` command. Below is an example [Org](/reference/org) manifest (in YAML). Edit and save the YAML as a file and use it as an input to the CLI's `cpln org patch ORG_NAME -f FILE.yaml` command. Refer to the [credential procurement](#step-1-credential-procurement) section to obtain and configure the necessary credentials. * Substitute: `ORG_NAME`, `S3_BUCKET_NAME`, `AWS_SECRET`, and `AWS_REGION`. ```yaml YAML theme={null} kind: org name: ORG_NAME spec: logging: s3: bucket: S3_BUCKET_NAME credentials: //secret/AWS_SECRET prefix: / region: AWS_REGION ``` # Stackdriver Source: https://docs.controlplane.com/external-logging/stackdriver Configure log shipping from Control Plane to Google Cloud Stackdriver (Operations Suite) for monitoring and log analysis. ## Overview [Stackdriver](https://cloud.google.com/products/operations), now known as Google Cloud Operations Suite, is a powerful monitoring, logging, and diagnostics tool for Google Cloud Platform (GCP) and other cloud environments. It offers a range of features to help you gain deep insights into your system's performance and health. With [Stackdriver](https://cloud.google.com/products/operations), you can collect and analyze logs, monitor infrastructure and application metrics, set up custom alerts, and automate responses to critical events. By leveraging [Stackdriver](https://cloud.google.com/products/operations), you can enhance your operational efficiency, quickly identify and resolve issues, and ensure the reliability and stability of your applications and services. Follow the steps below to configure log shipping to Stackdriver. ## Step 1 - Credential Procurement GCP credentials are required to ship logs to Stackdriver. To obtain GCP credentials and store them, follow the guide here: [GCP key](/reference/secret#google-cloud-platform-gcp) ## Step 2 - Configure External Logging External logging can be configured by using either the [UI Console](#enable-logging-using-the-ui-console) or [CLI](#enable-logging-using-the-cli). Use a valid GCP location for the `location` field. E.g. us-east1 ### Enable Logging using the UI Console 1. From the Control Plane Console UI, click on `Org` in the left menu. 2. Click `External Logs` in the middle context menu. 3. Select `Stackdriver` and fill out the required fields. 4. Select the GCP secret created to authenticate to GCP. Refer to the [credential procurement](#step-1-credential-procurement) section to obtain and configure the necessary credentials. 5. Click `Save`. 6. After the configuration is complete, log entries will be available at Stackdriver within a few minutes. ### Enable Logging using the CLI The external logging configuration can be created / updated using the CLI's `cpln org patch ORG_NAME -f FILE.yaml` command. Below is an example of an [Org](/reference/org) manifest (in YAML). Edit and save the YAML as a file and use it as an input to the CLI's `cpln org patch ORG_NAME -f FILE.yaml` command. Refer to the [credential procurement](#step-1-credential-procurement) section to obtain and configure the necessary credentials. * Substitute: ORG\_NAME, GCP\_SECRET and possibly the location. ```yaml YAML theme={null} kind: org name: ORG_NAME spec: logging: stackdriver: location: us-east1 credentials: //secret/GCP_SECRET ``` # Syslog Source: https://docs.controlplane.com/external-logging/syslog ## Overview [Syslog](https://www.rfc-editor.org/rfc/rfc3164) is a protocol used for sending and storing log messages across networked devices and systems. With Syslog, you can collect and analyze log messages, monitor system and network activity, set up centralized log management, and automate responses to critical events. This enhances operational visibility and ensures the reliability and stability of your applications and infrastructure. Follow the steps below to configure log shipping to Syslog. ## Step 1 - Prerequisites * **Syslog Endpoint**: A reachable hostname for your Syslog server. * **Port**: Standard Syslog uses port `514` (TCP). ## Step 2 - Configure External Logging External logging can be configured by using either the [UI Console](#enable-logging-using-the-ui-console) or [CLI](#enable-logging-using-the-cli). ### Enable Logging using the UI Console 1. From the Control Plane Console UI, click on `Org` in the left menu. 2. Click `External Logs` in the middle context menu. 3. Select `Syslog` and fill out the required fields. 4. Click `Save`. 5. After the configuration is complete, log entries will begin forwarding to your Syslog Server within a few minutes. ### Enable Logging using the CLI The external logging configuration can be created / updated using the CLI's `cpln org patch ORG_NAME -f FILE.yaml` command. Below is an example of an [Org](/reference/org) manifest (in YAML). Edit and save the YAML as a file and use it as an input to the CLI's `cpln org patch ORG_NAME -f FILE.yaml` command. * Substitute: ORG\_NAME, HOST\_NAME, PORT, and possibly the severity. ```yaml YAML theme={null} kind: org name: ORG_NAME spec: logging: syslog: format: rfc3164 host: HOST_NAME mode: tcp port: PORT severity: 6 ``` # Create an Agent Source: https://docs.controlplane.com/guides/agent Define an agent and generate a bootstrap config file for installing the agent in your internal environment or at one of the cloud providers. ## Overview Follow these steps to define an [agent](/reference/agent) and generate a bootstrap config file that will be used when installing the agent within your internal environment or at one of the cloud providers. ## Prerequisites * Review the [agent](/reference/agent) reference page. * Have [permissions](/reference/policy#permissions) to create an [agent](/reference/agent). * Optional: Install the [CLI](/cli-reference/installation). ## Create using the UI Console 1. Create a new agent using one of the following methods: * Click `Agents` in the left menu, then click `New`, or * Click the `Create` dropdown in the upper right corner and select `Agent`. 2. Enter a unique name and optional description. 3. Optional: Click `Tags` and enter any [tags](/core/misc#tags). 4. Click `Create`. The console will display the bootstrap config JSON. Save the JSON manually or click `Download Config File`. 5. Click `Next`. The scripts displayed are generated using the bootstrap config. Copy or download the appropriate script: `User Data script` for AWS and Azure and the `Startup script` for GCP, as it will be used during the installation of the agent. 6. Click `Done`. Properly save the bootstrap config JSON text. It will not be accessible after closing the modal. If you lose the text, you will need to delete and recreate the agent. ## Create using the CLI 1. At a command prompt, execute the following to create an agent: ```bash theme={null} cpln agent create --name NEW_AGENT_NAME --org ORG_NAME > bootstrap-config.json ``` 2. The output of the command will be the bootstrap config JSON text. Executing the command above will save the text in a file called `bootstrap-config.json`. The `User Data script` for AWS and Azure and the `Startup script` for GCP must be obtained in the UI. These steps are covered in [Configure an Agent](/guides/setup-agent). Detailed CLI documentation is available in the [agent CLI reference](/cli-reference/commands/agent). ## Next Steps Once the bootstrap config file or provisioning scripts have been generated and saved, follow the [Configure an Agent](/guides/setup-agent) guide to install and configure an agent: * [Amazon Web Services (AWS)](/guides/setup-agent#aws) * [Microsoft Azure](/guides/setup-agent#azure) * [Google Cloud Platform (GCP)](/guides/setup-agent#gcp) * [Running locally in Docker](/guides/setup-agent#running-locally-in-docker) * [Kubernetes (k8s) Cluster](/guides/setup-agent#kubernetes-k8s-cluster) # Blue/Green Deployments Source: https://docs.controlplane.com/guides/blue-green-deployments Deploy new application versions with zero downtime using parallel workloads and instant traffic switching. ## Overview Blue/green deployment is a release strategy that reduces downtime and risk by running two identical production environments: one active (blue) and one idle (green). When a new version is ready, you deploy it to the idle environment, validate it, and then switch all traffic over instantly. If something goes wrong, you roll back by switching traffic back to the original environment. On Control Plane, blue/green deployments are implemented using two separate Workloads within the same GVC, combined with Domain path-based or subdomain-based routing to control which workload receives live traffic. This guide assumes you already have a GVC, a working workload, and a domain configured for your application. See the [Create a GVC](/guides/create-gvc), [Create a Workload](/guides/create-workload), and [Configure a Domain](/guides/configure-domain) guides if you need to set those up first. *** ## How It Works Control Plane's Domain resource supports path-based and subdomain-based routing, allowing requests to be directed to any workload within a GVC. A blue/green deployment takes advantage of this by maintaining two workloads simultaneously and updating the Domain route to point to whichever is currently active. ### The Two Environments The `blue` workload runs the current production version of your application. The `green` workload runs the new version you want to deploy. Only one workload receives external traffic at any given time, but both remain running until the switch is confirmed. | Blue Environment (Current) | Green Environment (New) | | ---------------------------------------- | ------------------------------------------ | | Serves all live production traffic | Receives no external traffic until cutover | | Validated, stable release | New version under test and validation | | Scaled to handle full production load | Can be pre-scaled before traffic switch | | Active target in Domain routing rules | Inactive; endpoint accessible for testing | | Deleted or kept on standby after cutover | Becomes the new blue after promotion | ### Traffic Routing Traffic is controlled by a Control Plane `Domain` resource. By updating the `workloadLink` field in a domain route, you redirect all incoming traffic from the blue workload to the green workload. This switch takes effect globally across all locations in your GVC within seconds, without downtime. Control Plane domains support two routing modes for blue/green deployments: * **Path-based routing** (`dnsMode: cname`) - Routes requests to different workloads based on URL path prefix. Recommended for most blue/green setups because you maintain full DNS control and it is CDN/WAF compatible. * **Subdomain-based routing** (`dnsMode: ns`) - Assigns a unique subdomain to each workload automatically. Use this if your architecture requires distinct DNS names per workload. ### The Deployment Lifecycle 1. Deploy the green workload with the new image version. 2. Validate the green workload using its internal Control Plane endpoint or a staging domain. 3. Update the Domain routing rule to point to the green workload. 4. Monitor the green workload in production. 5. Decommission the blue workload, or keep it on standby for rapid rollback. Because the switch is a single Domain configuration change, rollback is equally instant: update the route back to the blue workload link. *** ## Prerequisites * A Control Plane account with an active Org. * The Control Plane CLI (`cpln`) installed and authenticated. See the [CLI Reference](/cli-reference/overview) for setup instructions. * An existing GVC with at least one location configured. * A `blue` workload already deployed and serving production traffic. * A Domain configured and associated with your GVC. * The new application image built and pushed to your org's private registry. See the [Push an Image](/guides/push-image) guide. Replace all placeholder values in the examples below (`ORG_NAME`, `GVC_NAME`, `DOMAIN_NAME`, and image tags) with values specific to your environment. The CLI commands and YAML manifests in this guide are generic examples intended to illustrate structure. For a real deployment, export the configuration of your existing blue workload and use that as the basis for the green workload definition. This ensures your green workload inherits all of your current container settings, environment variables, resource limits, probes, and firewall rules exactly. Export your current workload using the CLI: ```bash theme={null} cpln workload get my-app-blue --gvc my-gvc --org ORG_NAME -o yaml-slim > green-workload.yaml ``` Then update the `name` field to `my-app-green` and change the container `image` to the new version before applying. You can also export from the Console by navigating to your workload, clicking **Actions**, and selecting **Export**. *** ## Step-by-Step Guide All steps in this guide can be completed using either the Control Plane CLI or the [Console UI](https://console.cpln.io). CLI commands are shown throughout for reproducibility and automation. Where the Console UI approach differs meaningfully, instructions are included inline. ### Step 1: Deploy the Green Workload Create a new workload running the new image version. Choose its name carefully before creating it — workload names cannot be changed after creation. A clear, consistent naming scheme (such as `my-app-blue` and `my-app-green`, or `my-app-v1` and `my-app-v2`) makes it easy to identify which workload is active at a glance. The most reliable starting point is to export the configuration of your existing blue workload and modify it, rather than writing a manifest from scratch. This guarantees the green workload matches your current production settings exactly. Export and prepare the green workload manifest: ```bash theme={null} # Export the blue workload's current configuration cpln workload get my-app-blue --gvc my-gvc --org ORG_NAME -o yaml-slim > green-workload.yaml # Update the name and image in the exported file, then apply ``` In the exported file, change `name` to `my-app-green` and update the container `image` to the new version tag, then apply it: ```bash theme={null} cpln apply --file green-workload.yaml --org ORG_NAME ``` Alternatively, you can create the workload directly with the CLI or apply a manifest written from scratch. The CLI command and YAML below are simplified generic examples for reference only; your actual workload will have more configuration inherited from the export. ```bash theme={null} cpln workload create \ --name my-app-green \ --image //image/my-app:2.0.0 \ --gvc my-gvc \ --port 8080 \ --public ``` The following is a simplified example for reference. In practice, export your blue workload's configuration as described above and edit the `name` and `image` fields. ```yaml theme={null} kind: workload name: my-app-green gvc: my-gvc spec: type: serverless containers: - name: my-app-green image: //image/my-app:2.0.0 ports: - number: 8080 protocol: http defaultOptions: autoscaling: minScale: 1 maxScale: 5 metric: rps target: 100 ``` Then apply it: ```bash theme={null} cpln apply --file green-workload.yaml --org ORG_NAME ``` 1. Navigate to your GVC in the [Console](https://console.cpln.io) and click **Workloads** in the left menu. 2. Click **New** and enter the name for your green workload (e.g. `my-app-green`). 3. To base it on your existing blue workload, open `my-app-blue`, click **Actions**, and select **Clone**. Update the name and image tag on the clone form before saving. 4. Update the container image to the new version tag and confirm all other settings match your blue workload. 5. Click **Create**. The workload will begin deploying across all configured locations. The green workload gets its own internal Control Plane endpoint (the canonical endpoint) immediately. You can use this to validate the new version before it receives any production traffic. ### Step 2: Validate the Green Workload Before switching traffic, verify that the green workload is healthy and behaving as expected. Use the workload's canonical endpoint, which is accessible without modifying any domain routing. Check the workload deployment status: ```bash theme={null} cpln workload get my-app-green --gvc my-gvc --org ORG_NAME ``` Retrieve the canonical endpoint: ```bash theme={null} cpln workload get my-app-green --gvc my-gvc --org ORG_NAME -o json | jq '.status.endpoint' ``` In the Console, navigate to your workload and check the **Health** indicator on the Info page. The canonical endpoint URL is listed under **Endpoints** and can be opened directly from there. Run smoke tests, integration tests, or manual checks against the canonical endpoint to confirm the new version is production-ready. The green workload is completely isolated from your production domain at this stage. You can also temporarily add a second domain or a staging path prefix routed to the green workload for more realistic pre-production validation. ### Step 3: Switch Traffic to Green Once validation passes, update your Domain resource to route production traffic to `my-app-green` instead of `my-app-blue`. This is the cutover step. As with the workload, you can export your current domain configuration as a starting point: ```bash theme={null} cpln domain get DOMAIN_NAME --org ORG_NAME -o yaml-slim > domain.yaml ``` Then update the `workloadLink` in the relevant route to point to `my-app-green` and apply. The YAML shown in the tabs below is a generic example illustrating which field to change. If your domain uses `dnsMode: cname`, update the `workloadLink` in the relevant route: ```yaml theme={null} kind: domain name: DOMAIN_NAME spec: dnsMode: cname gvcLink: //gvc/my-gvc ports: - number: 443 protocol: http2 routes: - prefix: / port: 8080 workloadLink: //gvc/my-gvc/workload/my-app-green # <-- Updated ``` Apply the change: ```bash theme={null} cpln apply --file domain.yaml --org ORG_NAME ``` If your domain uses `dnsMode: ns`, traffic is automatically routed to workloads by name. Switching traffic means directing users to the new workload's subdomain (`my-app-green.your-domain.com`) or updating your application's DNS CNAME to point to the green workload endpoint. For a fast cutover without editing YAML files, patch the domain directly via the CLI: ```bash theme={null} cpln rest patch /org/ORG_NAME/domain/DOMAIN_NAME \ --patch '{"spec":{"ports":[{"number":443,"routes":[{"prefix":"/", "workloadLink":"//gvc/my-gvc/workload/my-app-green","port":8080}]}]}}' \ --org ORG_NAME ``` 1. In the [Console](https://console.cpln.io), navigate to your Org and click **Domains** in the left menu. 2. Select your domain and click **Edit**. 3. Under **Ports**, expand the relevant port and locate the route with the `prefix` you want to switch. 4. Update the **Workload** field from `my-app-blue` to `my-app-green`. 5. Click **Update**. The change propagates to all GVC locations within seconds. The domain update propagates to all locations in the GVC within seconds. There is no staged rollout of the routing change itself; it is atomic and global. ### Step 4: Monitor the Green Workload After the traffic switch, monitor the green workload to confirm it is handling production load correctly. Keep the blue workload running during this observation period to enable rapid rollback. View workload logs in real time: ```bash theme={null} cpln logs --workload my-app-green --gvc my-gvc --org ORG_NAME --tail ``` Check replica health and scaling: ```bash theme={null} cpln workload get my-app-green --gvc my-gvc --org ORG_NAME ``` Use the Control Plane Console Metrics view (powered by Grafana) to inspect error rates, latency, and request throughput for the green workload. Set up Grafana alerts on the green workload before the cutover. Alert on error rate, p99 latency, and 5xx responses so you are notified immediately if the new version degrades. ### Step 5: Decommission or Keep the Blue Workload Once you are confident the green workload is stable, you have two options for the blue workload: If you do not need a rapid rollback standby, delete the blue workload to free up resources: ```bash theme={null} cpln workload delete my-app-blue --gvc my-gvc --org ORG_NAME ``` In the Console, navigate to the `my-app-blue` workload, click **Actions**, and select **Delete**. Scale the blue workload to its minimum replica count (or to zero if using KEDA autoscaling) to reduce cost while keeping it available for rollback: ```bash theme={null} cpln workload update my-app-blue \ --set spec.defaultOptions.autoscaling.minScale=0 \ --gvc my-gvc \ --org ORG_NAME ``` In the Console, navigate to the `my-app-blue` workload, click **Scaling** in the left menu, and set the **Min Scale** value to `0`. Serverless workloads can scale to zero automatically when traffic stops. If your blue workload is of type `serverless` and receives no traffic, it scales down on its own without any intervention. After a suitable observation period, delete the old blue workload. For the next release cycle, your current green workload becomes the new production baseline. Deploy the next version as a new workload using whatever name fits your convention (for example, swapping back to `my-app-blue`), update the Domain route, and repeat the process. *** ## Rolling Back If you detect a problem with the green workload, rolling back is the reverse of the cutover: update the Domain route to point back to the blue workload. 1. In the [Console](https://console.cpln.io), navigate to your Org and click **Domains** in the left menu. 2. Select your domain and click **Edit**. 3. Under **Ports**, expand the relevant port and locate the route pointing to `my-app-green`. 4. Update the **Workload** field back to `my-app-blue`. 5. Click **Update**. Traffic returns to the blue workload within seconds. Update your domain manifest to revert the `workloadLink` and apply it: ```yaml theme={null} kind: domain name: DOMAIN_NAME spec: dnsMode: cname gvcLink: //gvc/my-gvc ports: - number: 443 protocol: http2 routes: - prefix: / port: 8080 workloadLink: //gvc/my-gvc/workload/my-app-blue # <-- Reverted ``` ```bash theme={null} cpln apply --file domain.yaml --org ORG_NAME ``` Because the blue workload was never stopped, rollback is instant. Traffic returns to the previous version within seconds of the domain update propagating. *** ## Automating with CI/CD Blue/green deployments integrate naturally into CI/CD pipelines using the Control Plane CLI or Terraform. The pipeline automates the image build, green workload creation, health check, and traffic switch steps. ### GitHub Actions Example The following workflow builds the new image, deploys it to a green workload, runs a health check, and performs the traffic cutover if the check passes. ```yaml theme={null} name: Blue/Green Deploy on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest env: CPLN_ORG: ${{ secrets.CPLN_ORG }} CPLN_GVC: my-gvc CPLN_TOKEN: ${{ secrets.CPLN_TOKEN }} IMAGE_TAG: ${{ github.sha }} steps: - uses: actions/checkout@v4 - name: Install Control Plane CLI run: npm install -g @controlplane/cli - name: Authenticate run: cpln profile update default --token $CPLN_TOKEN - name: Build and push image run: cpln image build --name my-app:$IMAGE_TAG --push --org $CPLN_ORG - name: Deploy green workload run: | sed 's|IMAGE_TAG|'"$IMAGE_TAG"'|g' green-workload.yaml | \ cpln apply --file - --org $CPLN_ORG - name: Wait for green workload to be ready run: cpln workload get my-app-green --gvc $CPLN_GVC --org $CPLN_ORG --wait-ready - name: Run smoke tests run: ./scripts/smoke-test.sh $(cpln workload get my-app-green \ --gvc $CPLN_GVC --org $CPLN_ORG -o json | jq -r '.status.endpoint') - name: Switch traffic to green run: cpln apply --file domain-green.yaml --org $CPLN_ORG - name: Delete old blue workload run: cpln workload delete my-app-blue --gvc $CPLN_GVC --org $CPLN_ORG ``` Store your `CPLN_ORG` and `CPLN_TOKEN` values as GitHub Actions secrets. Never hardcode credentials in pipeline files. For more CI/CD examples using GitLab, Bitbucket, and CircleCI, see the [CI/CD Examples](/guides/gitops) guide. *** ## Advanced Patterns ### Mirror Traffic for Pre-Production Validation Control Plane domains support a `mirror` routing property that sends a copy of live traffic to a secondary workload. This allows you to validate the green workload under real production load before committing to the cutover, without exposing users to any green responses. ```yaml theme={null} routes: - prefix: / port: 8080 workloadLink: //gvc/my-gvc/workload/my-app-blue mirror: - workloadLink: //gvc/my-gvc/workload/my-app-green percent: 100 ``` With mirroring enabled, the blue workload still serves all production responses. The green workload receives an identical copy of each request but its responses are discarded. You can inspect green workload logs and metrics to confirm correctness before the switch. Mirror traffic is fire-and-forget: the mirrored workload's response is not returned to the client, so errors on the green workload are invisible to users. ### Multi-Location Considerations Control Plane workloads and domains are global by default. When you update a Domain route, the change propagates to every location in the GVC. There is no built-in mechanism to switch one location at a time. If you need location-specific traffic control, use separate GVCs for each region and route at the DNS layer, or use the GVC [location routing options](/reference/gvc#location-routing-options) to adjust traffic distribution by latency or priority. Contact [Control Plane support](mailto:support@controlplane.com) at [support@controlplane.com](mailto:support@controlplane.com) for guidance on complex multi-region deployment patterns. ### Environment Naming Conventions The `blue`/`green` names are conventions. Some teams prefer `my-app-stable` and `my-app-canary`, or versioned names like `my-app-v1` and `my-app-v2`. Because workload names cannot be changed after creation, choose your naming scheme before creating workloads and apply it consistently. Only one workload should be the target of the production Domain route at any time. You may also use Control Plane tags to annotate which workload is currently active, making it easy to identify the production workload programmatically: ```bash theme={null} cpln workload update my-app-green \ --set tags.deployment-role=active \ --gvc my-gvc --org ORG_NAME cpln workload update my-app-blue \ --set tags.deployment-role=standby \ --gvc my-gvc --org ORG_NAME ``` *** ## Quick Reference | Action | Command | | ----------------------- | ------------------------------------------------------------------------------------------------- | | Create green workload | `cpln workload create --name my-app-green --image //image/my-app:2.0 --gvc GVC` | | Check workload status | `cpln workload get my-app-green --gvc GVC --org ORG` | | View workload logs | `cpln logs --workload my-app-green --gvc GVC --org ORG --tail` | | Update workload image | `cpln workload update my-app-green --set spec.containers.main.image=//image/my-app:2.1 --gvc GVC` | | Switch domain to green | `cpln apply --file domain-green.yaml --org ORG` | | Roll back to blue | `cpln apply --file domain-blue.yaml --org ORG` | | Scale blue to zero | `cpln workload update my-app-blue --set spec.defaultOptions.autoscaling.minScale=0 --gvc GVC` | | Delete blue workload | `cpln workload delete my-app-blue --gvc GVC --org ORG` | | Export workload as YAML | `cpln workload get my-app-blue --gvc GVC -o yaml-slim > workload.yaml` | | Add deployment tag | `cpln workload update my-app-green --set tags.deployment-role=active --gvc GVC` | *** ## Related Resources Full workload configuration options including autoscaling, probes, and container settings. Domain routing modes, path-based routing, mirror routing, and TLS configuration. Using cpln apply for declarative, repeatable resource management. Pipeline examples for GitHub Actions, GitLab CI, Bitbucket, and CircleCI. Build and push container images to your org's private Control Plane registry. How GVCs define location placement, pull secrets, and DNS geo-routing. # Browser-less CLI Login Source: https://docs.controlplane.com/guides/browser-less-cli-login Authenticate with the Control Plane CLI using service account tokens for unattended environments. Authenticate the CLI with a [service account](/reference/serviceaccount) token instead of an interactive login. This is the right choice for unattended automation — CI/CD pipelines, scripts, and long-lived containers — where no person is available to complete a sign-in. For interactive use, you no longer need a browser on the machine running the CLI. `cpln login` prints a URL and a confirmation code that you can complete from a browser on any device, so it works over SSH, inside containers, and in WSL2. Reach for a service account token when the login must be **unattended**, not merely browser-less. See [Authentication](/cli-reference/get-started/authentication). ## Prerequisites The user setting this up must be a member of the [superusers group](/reference/group#built-in-groups) or have a [policy](/reference/policy) targeting [service accounts](/reference/serviceaccount) with these permissions: * `create` * `edit` * `addKey` Install the Control Plane CLI before proceeding. See [Installation](/cli-reference/installation). ## Setup 1. Navigate to **Service Accounts** in the left menu 2. Click **New** or use the **Create** dropdown 3. Enter a unique name and optional description 4. Assign to a group (e.g., `superusers`, `viewers`, or a custom group) to grant initial permissions 5. Click **Create** ```bash theme={null} cpln serviceaccount create --name automation --description "Browser-less CLI access" ``` See the [Create a Service Account](/guides/create-service-account) guide for detailed instructions. 1. Open the service account you created 2. Click the **Keys** link 3. Enter a key description and click **Add** 4. Copy and securely store the generated key ```bash theme={null} cpln serviceaccount add-key automation --description cli-access ``` Save the returned key securely. Store this key securely. It cannot be retrieved after creation. Create [policies](/reference/policy) to grant the service account access to the resources it needs (GVCs, workloads, secrets, etc.). See the [Policy guide](/guides/policy) for creating policies via the console. ```bash theme={null} # Create a policy granting access to a GVC cpln policy create --name automation-policy \ --targetKind gvc \ --targetLinks //gvc/my-gvc \ --permissions view,edit # Add the service account as a principal cpln policy add-binding automation-policy \ --principalLinks //serviceaccount/automation ``` For testing, you can add the service account to the `superusers` [group](/reference/group#built-in-groups) for full access, then scope down permissions later. Configure the CLI to use the service account token: ```bash theme={null} cpln profile create automation --token --org --gvc --default ``` When using `--token`, always include `--gvc` to set a default GVC context for your commands. Your CLI is now configured for unattended access. Test it with: ```bash theme={null} cpln workload get ``` ## Alternative: Environment variables Instead of creating a profile, you can use environment variables: ```bash theme={null} export CPLN_TOKEN= export CPLN_ORG= export CPLN_GVC= cpln workload get ``` This is useful for CI/CD pipelines and containers where you don't want to persist a profile. ## Troubleshooting If CLI commands fail with permission errors: 1. **Test with superusers**: Temporarily add the service account to the `superusers` [group](/reference/group#built-in-groups) 2. **Verify commands work**: If they now work, the issue is missing permissions 3. **Add specific permissions**: Create [policies](/guides/policy) granting access to the required resources 4. **Remove from superusers**: Once policies are in place, remove the service account from superusers Verify the correct profile is set as default: ```bash theme={null} cpln profile get ``` Set the correct profile as default: ```bash theme={null} cpln profile set-default automation ``` 1. Verify the token is correct and hasn't been revoked 2. Generate a new key if needed: ```bash theme={null} cpln serviceaccount add-key automation --description new-key ``` 3. Update your profile with the new token: ```bash theme={null} cpln profile update automation --token ``` For more troubleshooting help, see the [Troubleshooting](/cli-reference/using-cli/troubleshooting) page. ## Next steps Automate deployments with the CLI Manage multiple authentication contexts Create and manage service accounts Configure access permissions # Build with Buildpacks Source: https://docs.controlplane.com/guides/buildpacks Build container images without Dockerfiles using Cloud Native Buildpacks. [Cloud Native Buildpacks](https://buildpacks.io) automatically detect your application language and create an optimized container image without writing a Dockerfile. They handle dependency installation, compilation, and runtime configuration based on conventions for each language. ## Quick Start Build and push an image using buildpacks: ```bash theme={null} cpln image build --name my-app:v1 --push ``` If your project has no Dockerfile, the CLI will install [pack](https://buildpacks.io/docs/tools/pack/) (if not already available) and use `pack build` under the hood. All buildpack-related flags are passed directly to pack. This page covers local builds, which need Docker. A [remote build](/cli-reference/get-started/images#build-options) (`cpln image build --remote`) does not run pack: it detects how to build the source on its own, so the builder and buildpack flags below do not apply to it. ## Build Options | Flag | Description | | -------------------------- | ----------------------------------------------------------------------- | | `--builder`, `-B` | CNB-compatible builder image (default: `heroku/builder:24_linux-amd64`) | | `--buildpack`, `-b` | Additional buildpack to use (can be specified multiple times) | | `--dir` | Build context directory (default: current directory) | | `--push` | Push the image to your org's private registry after building | | `--no-cache` | Build without using cache | | `--env`, `-e` | Environment variable for the build (can be specified multiple times) | | `--env-file` | File containing environment variables (can be specified multiple times) | | `--trust-builder` | Trust the builder image (skip security prompts) | | `--trust-extra-buildpacks` | Trust additional buildpacks | | `--platform`, `-p` | Target platform (default: `linux/amd64`) | `--build-arg` and `--secret` belong to Dockerfile builds. Buildpacks have no `ARG` mechanism and no secret mount, so a buildpack build warns about any build argument it was given and rejects `--secret` outright. Pass build-time values with `--env` or `--env-file` instead, keeping in mind that a buildpack can persist them into the image, so they are not a place for credentials. ## Common Builders | Builder | Description | | ------------------------------------- | ---------------------------------------------------------------- | | `heroku/builder:24` | Default. Supports Node.js, Python, Go, Java, Ruby, PHP, and more | | `gcr.io/buildpacks/builder:google-22` | Google Cloud buildpacks | | `paketobuildpacks/builder-jammy-base` | Paketo community buildpacks (includes .NET, Rust support) | ## Language Conventions The following conventions apply to the default `heroku/builder:24` unless otherwise noted. Other builders may have different requirements. **Required files:** * `package.json` in the project root * `package-lock.json` in the project root Run `npm install` before building to ensure `package-lock.json` is up to date with your dependencies. **Start command:** * If `index.js` or `server.js` exists in the root, it will be used automatically * Otherwise, add a `scripts.start` command in `package.json` * Alternatively, create a `Procfile` in the project root **Example Procfile:** ``` web: node src/app.js ``` **Example package.json (with start script):** ```json theme={null} { "name": "my-app", "scripts": { "start": "node src/app.js" }, "engines": { "node": "20.x" } } ``` **Example package.json (with index.js or server.js in root):** ```json theme={null} { "name": "my-app", "engines": { "node": "20.x" } } ``` Specify your Node.js version in `engines.node` to ensure consistent builds. **Detection (required - one of):** * `requirements.txt` (pip) * `uv.lock` (uv) * `poetry.lock` (Poetry) These files are used to detect a Python app and install dependencies. **Extra requirements:** * Using **uv** requires a `.python-version` file * Using **Poetry** for a non-packaged app requires `package-mode = false` in `pyproject.toml` **Start command (required for web apps):** * Create a `Procfile` in the project root * Python buildpacks do **not** auto-detect an entry file * Without a `Procfile`, the app may build but will not start **Example Procfile:** ``` web: gunicorn -b 0.0.0.0:$PORT app:app ``` **Runtime requirements:** * Server must bind to `0.0.0.0` * Server must listen on `$PORT` **Example requirements.txt:** ``` flask==3.0.0 gunicorn==21.2.0 ``` **Required files:** * `go.mod` in the project root **Project structure:** * The `main` package must be in the project root **Build behavior:** * Compiles the `main` package * Binary is automatically set as the start command **Example go.mod:** ``` module github.com/myorg/myapp go 1.21 ``` **Required files:** * `pom.xml` in the project root **Build behavior:** * Runs `mvn package` **Start command:** * Spring Boot JARs: start command is auto-detected * Non-Spring executable JARs: add a `Procfile` (recommended) **Runtime requirements:** * Server must bind to `0.0.0.0` * Server must listen on `$PORT` If you need a specific JDK version, pin it explicitly (otherwise the builder default may be used). **Example pom.xml excerpt (Spring Boot):** ```xml theme={null} jar org.springframework.boot spring-boot-maven-plugin ``` **Required files:** * `build.gradle` or `build.gradle.kts` in the project root * `gradlew` (Gradle Wrapper) in the project root **Build behavior:** * Runs `./gradlew build` **Start command:** * Spring Boot JARs: start command is auto-detected * Non-Spring executable JARs: add a `Procfile` (recommended) **Runtime requirements:** * Server must bind to `0.0.0.0` * Server must listen on `$PORT` If you need a specific Java version, add `system.properties` (e.g. `java.runtime.version=21`). Avoid Gradle toolchains unless you configure toolchain downloads. **Required files:** * `Gemfile` and `Gemfile.lock` in the project root **Start command:** * Rails apps: default start command exists, but a `Procfile` is recommended * Non-Rails apps: create a `Procfile` with `web: ` **Runtime requirements:** * Server must listen on `$PORT` **Example Procfile (non-Rails):** ``` web: bundle exec puma -p $PORT config.ru ``` **Example Procfile (Rails with Puma):** ``` web: bundle exec puma -C config/puma.rb ``` **Required files:** * `composer.json` and `composer.lock` in the project root **Start command:** * Create a `Procfile` with `web: ` **Example Procfile (Apache):** ``` web: heroku-php-apache2 public/ ``` Rust is not supported by the default `heroku/builder:24`. Use the Paketo Community Rust buildpack. **Required files:** * `Cargo.toml` in the project root * `Cargo.lock` in the project root * A binary target (e.g. `src/main.rs` or `src/bin/*.rs`) **Build command:** ```bash theme={null} cpln image build --name my-rust-app:v1 --push -b docker.io/paketocommunity/rust ``` **Build behavior:** * Builds the project using `cargo build --release` * The resulting binary is used as the default launch command (or can be overridden via a Procfile) **Example Cargo.toml:** ```toml theme={null} [package] name = "my-app" version = "0.1.0" edition = "2021" [dependencies] actix-web = "4" ``` **Process configuration (optional):** ```procfile theme={null} web: my-app ``` > Note: The binary name is derived from `[package].name` and must match the Procfile entry exactly. **Runtime requirements:** * Server must listen on `$PORT` (read from environment variable) C# / .NET is not supported by the default `heroku/builder:24`. Use the Paketo .NET buildpacks via a Paketo builder. **Required files:** * A `.csproj` or `.fsproj` file in the project root, or * A `.sln` file with referenced projects **Build command:** ```bash theme={null} cpln image build --name my-dotnet-app:v1 --push -B paketobuildpacks/builder-jammy-base ``` **Build behavior:** * Runs `dotnet publish` (Release) * Automatically detects the entry point DLL for single-project apps **Start command:** * Auto-detected for single-project solutions * Optional: add a `Procfile` to explicitly set or override the start command (recommended for multi-project solutions) **Example .csproj:** ```xml theme={null} net9.0 ``` **Example Procfile (optional):** ``` web: dotnet MyApp.dll ``` **Runtime requirements:** * Server must bind to `0.0.0.0` (not `127.0.0.1`) * Server must listen on `$PORT` (recommended: set `ASPNETCORE_URLS` to `http://0.0.0.0:$PORT`) The simplest way to ensure correct port binding is to set `ASPNETCORE_URLS` in your workload: ``` ASPNETCORE_URLS=http://0.0.0.0:$PORT ``` ## The Procfile A `Procfile` defines how your application is started by the platform. Place it in the project root. ```text theme={null} web: ``` **Notes:** * Some languages auto-detect a start command (e.g., Go binaries, Spring Boot JARs) * Other languages require a `Procfile` to start a web server ## Build Examples ```bash theme={null} # Basic build with auto-detection cpln image build --name my-app:v1 --push # Use a different builder cpln image build --name my-app:v1 --push -B gcr.io/buildpacks/builder:google-22 # Add a specific buildpack (e.g., for Rust) cpln image build --name my-rust-app:v1 --push -b docker.io/paketocommunity/rust # Pass build-time environment variables cpln image build --name my-app:v1 --push -e NODE_ENV=production -e LOG_LEVEL=info # Use an env file cpln image build --name my-app:v1 --push --env-file .env.build # Build for a different platform cpln image build --name my-app:v1 --push --platform linux/arm64 # Trust builder and extra buildpacks (useful in CI/CD) cpln image build --name my-app:v1 --push --trust-builder --trust-extra-buildpacks ``` ## Troubleshooting The buildpack built your image but doesn't know how to start it. Common causes: 1. **Missing Procfile**: Create a `Procfile` in your project root: ``` web: ``` 2. **Missing start script (Node.js)**: Add a `start` script to `package.json`: ```json theme={null} "scripts": { "start": "node server.js" } ``` 3. **Wrong port**: Your app must listen on the `$PORT` environment variable, not a hardcoded port. Buildpacks detect languages based on specific files. Ensure you have the required files for your language: | Language | Required File(s) | | --------- | ----------------------------------------------------------------------------------- | | Node.js | `package.json` + `package-lock.json` | | Python | `requirements.txt`, `uv.lock`, or `poetry.lock` | | Go | `go.mod` | | Java | `pom.xml` or `build.gradle` | | Ruby | `Gemfile` + `Gemfile.lock` | | PHP | `composer.json` + `composer.lock` | | Rust | `Cargo.toml` + `Cargo.lock` (requires `-b docker.io/paketocommunity/rust`) | | C# / .NET | `.csproj`, `.fsproj`, or `.sln` (requires `-B paketobuildpacks/builder-jammy-base`) | Rust and C# / .NET are not supported by the default `heroku/builder:24`. You must specify an additional buildpack or use a different builder as shown above. In CI/CD environments, add the trust flags: ```bash theme={null} cpln image build --name my-app:v1 --push --trust-builder --trust-extra-buildpacks ``` These languages require different builders or buildpacks: **Rust:** ```bash theme={null} cpln image build --name my-app:v1 --push -b docker.io/paketocommunity/rust ``` **C# / .NET:** ```bash theme={null} cpln image build --name my-app:v1 --push -B paketobuildpacks/builder-jammy-base ``` Buildpacks cache dependencies between builds. If builds are slow: 1. Ensure you're not using `--no-cache` unless necessary 2. Check if your dependency files changed (triggers full rebuild) 3. Consider using a builder optimized for your language ## Learn More Conceptual overview of images in Control Plane Detailed guide for building and pushing images Full CLI command reference Official Cloud Native Buildpacks documentation # Centralized Metrics Management Source: https://docs.controlplane.com/guides/centralized-metrics-management Access metrics from multiple orgs using a centralized Grafana instance by adding Prometheus data sources across organizations. ## Overview To access metrics from multiple ORGs using a centralized Grafana, it is necessary to add a data source for each [Org](/reference/org). This method is also applicable for integrating external Prometheus data sources with Control Plane's Grafana, enabling centralized metrics management. In this guide, we will demonstrate adding the Prometheus data source from `org-2` to the Grafana instance of `org-1`. This integration will enable the observation of metrics from both `org-1` and `org-2` using the Grafana interface of `org-1`. **Note**: Replace `org-1` and `org-2` with the actual names of your organizations. ### Follow the steps below #### In `org-2` 1. Log in to `org-2`, and create a new Service Account named `grafana-data-source`. 2. Create a new **key** for the `grafana-data-source` Service Account that is just created and save it. 3. Create a **policy** named `grafana-data-source` that enables `readMetrics` for the `grafana-data-source` Service Account. ```yaml YAML theme={null} kind: policy name: grafana-data-source description: grafana-data-source tags: {} bindings: - permissions: - readMetrics principalLinks: - /org/org-2/serviceaccount/grafana-data-source target: all targetKind: org ``` #### In `org-1` 1. Log in to `org-1`, and navigate to Grafana by going to **Metrics** in the [Control Plane Console](https://console.cpln.io/console/). 2. Proceed to `Connections` (available on the sidebar) > **Data Sources**. 3. Click on `Add new data source` and then select `Prometheus` as the type of the data source. 4. Complete the configuration: a. Name: Use a descriptive name. For example, the name of the target organization, `org-2`. b. URL: `https://metrics.cpln.io/metrics/org/org-2` (Note: Replace `org-2` with the actual organization's name). c. Add a custom HTTP Header for `authorization` with the value: `Bearer `. Replace `` with the key token generated for the `grafana-data-source` Service Account in `org-2` (step 2 under [In `org-2`](#in-org-2)). Then, click **Save & Test**. After these steps, you should be able to select the `org-2` data source when using the `Explore` feature in the `org-1` Grafana. **Import Dashboard for Multiple Sources** 5. In your `org-1` Grafana, create a dashboard named `Multi Source Metrics Overview Cloud` to view metrics from various data sources. This is achieved by importing the dashboard. 1. Download the dashboard JSON file from [this link](https://grafana.com/api/dashboards/20378/revisions/1/download). 2. Import the downloaded dashboard into Grafana in `org-1`. You can now select `org-2` as the data source in the `Multi Source Metrics Overview Cloud` dashboard you imported into `org-1`. Additional data sources, including those external or from other organizations, can be added by following the instructions in steps 1-4 under `org-1`. # Convert Kubernetes Manifests Source: https://docs.controlplane.com/guides/cli/cpln-convert Migrate from Kubernetes to Control Plane by converting Deployments, StatefulSets, CronJobs, ConfigMaps, Secrets, Ingresses, and more. The `cpln convert` command transforms Kubernetes manifests into Control Plane resources, enabling seamless migration from Kubernetes environments. ## When to use this Migrate existing K8s workloads, secrets, and storage to Control Plane Leverage your existing Kubernetes YAML files without rewriting them See how K8s resources translate to Control Plane before applying Convert and apply K8s manifests in automated pipelines ## Supported resources ### Workload conversions | Kubernetes Kind | Control Plane Kind | Notes | | --------------------- | ------------------------------------------------- | --------------------------------------- | | Deployment | [Workload](/reference/workload/general) | Type determined by spec analysis | | ReplicaSet | [Workload](/reference/workload/general) | Type determined by spec analysis | | ReplicationController | [Workload](/reference/workload/general) | Legacy K8s resource | | StatefulSet | [Workload](/reference/workload/general) | Type determined by spec analysis | | DaemonSet | [Workload](/reference/workload/general) | Type determined by spec analysis | | Job | [Workload (cron)](/reference/workload/types#cron) | Converted to cron with default schedule | | CronJob | [Workload (cron)](/reference/workload/types#cron) | Schedule preserved from spec | ### How workload type is determined The converter analyzes your Kubernetes spec to automatically select the appropriate Control Plane workload type: Set the `cpln.io/workload-type` annotation on the Deployment, StatefulSet, DaemonSet, ReplicaSet, or its pod template to pin the type explicitly: ```yaml theme={null} metadata: annotations: cpln.io/workload-type: serverless # serverless | standard | stateful ``` The pin overrides the automatic inference below. A workload that mounts a **dedicated** volumeset can only be pinned to `stateful`, and pinning `serverless` on a workload whose shape conflicts with serverless requirements (e.g., multiple ports) emits a warning and is validated by the API. Jobs and CronJobs always convert to `cron` and ignore the annotation. **Job** and **CronJob** resources always become `cron` workloads. * CronJobs preserve their schedule from `spec.schedule` * Jobs use a default schedule of `* * * * *` (every minute) The workload becomes `stateful` if any container mounts a **dedicated volumeset** (from a PersistentVolumeClaim or volumeClaimTemplates with single-node access modes such as `ReadWriteOnce`). Claims with multi-node access modes (`ReadWriteMany`, `ReadOnlyMany`) convert to [shared volumesets](#how-volumesets-are-configured), which any workload type can mount — they do not force the stateful type. Stateful takes precedence over standard when both conditions are met. All non-cron workloads start as `standard`. The type remains `standard` unless a stateful condition is detected. Conditions that explicitly confirm `standard`: * A container has **no ports** or **more than one port** * A container uses **gRPC health probes** (liveness or readiness) * The workload has **rollout options** (from K8s `strategy`, `updateStrategy`, `minReadySeconds`, or `podManagementPolicy`) The workload type is **immutable** after creation. When a converted workload already exists with a different type, `cpln apply --k8s` fails with `Workload type may not be changed` (405). Pin the type with the `cpln.io/workload-type` annotation to keep conversions stable across CLI versions. CLI versions before 3.9.0 inferred `serverless` for some Deployments; from 3.9.0 on, the default is `standard`. ### Conversion annotations The converter honors these annotations on the workload object metadata (Deployment, StatefulSet, DaemonSet, ReplicaSet, ReplicationController, Job, CronJob) or on its pod template metadata. Object-level annotations take precedence over template-level ones. | Annotation | Values | Effect | | ------------------------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------- | | `cpln.io/workload-type` | `serverless`, `standard`, `stateful` | Pins the converted workload type, overriding automatic inference | | `cpln.io/inherit-env` | `true`, `false` | Sets `inheritEnv` on every converted container, so [GVC-level environment variables](/reference/gvc#env) flow into the workload | | `cpln.io/internal-inbound-allow-type` | `none`, `same-gvc`, `same-org` | Overrides the [internal firewall inference](#firewall-and-public-exposure) for the converted workload | `cron` and `vm` cannot be pinned. Cron workloads carry a job schedule that only Jobs and CronJobs provide (they always convert to `cron`). [VM workloads](/reference/workload/types) boot from a disk image instead of running containers, so no container-based Kubernetes kind can convert to one. ```yaml Example theme={null} apiVersion: apps/v1 kind: Deployment metadata: name: api annotations: cpln.io/workload-type: serverless cpln.io/inherit-env: 'true' cpln.io/internal-inbound-allow-type: same-gvc ``` With kustomize, apply annotations across a whole stack using `commonAnnotations`, or per-resource with a patch. ### Environment variables Container environment variables convert as follows: | Kubernetes source | Control Plane result | | -------------------------------------- | --------------------------------------------------------------------------------- | | `env` with `value` | Inline value, preserved as-is | | `env` with `valueFrom.configMapKeyRef` | `cpln://secret/.` reference | | `env` with `valueFrom.secretKeyRef` | `cpln://secret/.` reference | | `env` with `valueFrom.fieldRef` | `cpln://reference/` reference | | `envFrom` with `configMapRef` | One variable per ConfigMap `data` key, referencing the converted secret | | `envFrom` with `secretRef` | One variable per Secret `data`/`stringData` key, referencing the converted secret | `envFrom` expansion follows Kubernetes semantics: * An explicit `env` entry **overrides** an `envFrom` variable with the same name. * A key present in multiple `envFrom` sources takes the value of the **last** source. * A `prefix` on the source is prepended to every expanded variable name. * ConfigMap `binaryData` keys are not expanded (Kubernetes does not expose them as environment variables either). The converter needs the referenced ConfigMap or Secret **in the conversion input** to know its keys: * A missing non-optional `configMapRef` fails the conversion (the pod could not start on Kubernetes either). * A missing `optional: true` `configMapRef` is skipped with a warning. * A missing `secretRef` (e.g., a Secret that already exists on Control Plane) produces a **warning** because its keys cannot be enumerated — include the Secret manifest in the input or add the variables to `env` manually. * A `secretRef` to a Secret that converts to a non-dictionary type (a `payload` key becomes opaque, basic-auth becomes userpass) is skipped with a warning, since its keys are not individually addressable. * Expanded variable names starting with `CPLN_` are skipped with a warning; that prefix is reserved. ### Secret and config conversions | Kubernetes Kind | Control Plane Kind | Notes | | --------------- | --------------------------------------------------- | --------------------------- | | Secret | [Secret](/reference/secret) | Direct conversion | | ConfigMap | [Secret (dictionary)](/reference/secret#dictionary) | Stored as dictionary secret | ### Storage conversions | Kubernetes Kind | Control Plane Kind | Notes | | --------------------- | --------------------------------- | ------------------ | | PersistentVolumeClaim | [Volumeset](/reference/volumeset) | Persistent storage | ### Domain conversions | Kubernetes Kind | Control Plane Kind | Notes | | --------------- | --------------------------- | ----------------------------------- | | Ingress | [Domain](/reference/domain) | Routes converted from Ingress rules | ### How secrets are converted Kubernetes Secrets are converted based on their `type` field: | K8s Secret Type | Control Plane Secret Type | Notes | | -------------------------------- | ------------------------- | ---------------------------------------------------------------- | | `kubernetes.io/dockerconfigjson` | `docker` | Extracts `.dockerconfigjson` field | | Any secret with a `payload` key | `opaque` | Checked before type-specific handling | | `kubernetes.io/basic-auth` | `userpass` | Extracts `username` and `password` fields | | `kubernetes.io/tls` | `dictionary` | Validated for `tls.crt` and `tls.key`, stored as key-value pairs | | Default | `dictionary` | All other secrets become dictionaries | ConfigMaps are always converted to `dictionary` secrets, with all key-value pairs preserved. If a secret has both `data` and `stringData` fields, it is always converted to `dictionary` with both merged. ### How volumesets are configured PersistentVolumeClaims are converted to volumesets with these settings: | Property | Source (in priority order) | Default | | --------------------- | -------------------------------------------------------------------------------------------- | --------------------- | | **Capacity** | PersistentVolume `spec.capacity.storage` → PVC `spec.resources.requests.storage` | 10 GB | | **Performance class** | StorageClass parameters (see below) | `general-purpose-ssd` | | **File system type** | PVC `accessModes` (see below) → PV volume source `fsType` → StorageClass `parameters.fsType` | `ext4` | **Shared volumesets from multi-node access modes:** A PVC whose `accessModes` include `ReadWriteMany` or `ReadOnlyMany` converts to a **shared** volumeset (`fileSystemType: shared`, `performanceClass: shared`). Shared volumesets can be mounted by workloads of any type and by multiple workloads at once — matching how RWX claims behave on Kubernetes. All other claims convert to dedicated block-storage volumesets, which only `stateful` (or `vm`) workloads can mount, one workload at a time. **High-performance SSD detection:** If the StorageClass `parameters` contain any of these values, the volumeset uses `high-throughput-ssd`: * AWS: `io1`, `io2` * GCP: `pd-extreme` * Azure: `UltraSSD_LRS` * VMware: `thick` * Other: `fast`, `persistent_1` ### How domains are converted Kubernetes Ingresses are converted to Control Plane [Domains](/reference/domain). The converter maps Ingress rules (host + paths) to domain routes, resolving backend Services to workloads. #### Field mapping | Kubernetes Ingress | Control Plane Domain | Notes | | ------------------------------------------- | -------------------------- | --------------------------------------------------- | | `metadata.labels` | `tags` | Labels preserved as domain tags | | `spec.rules[].host` | `name` | Wildcard prefix (`*.`) stripped for domain name | | `spec.rules[].host` (wildcard) | `spec.acceptAllSubdomains` | Set to `true` when host starts with `*.` | | `spec.rules[].http.paths[].path` | Route `prefix` or `regex` | Depends on `pathType` | | `spec.rules[].http.paths[].backend.service` | Route `workloadLink` | Resolved via Service selector → workload pod labels | | Service `targetPort` | Route `port` | Container port resolved through the Service | #### Path type handling | Kubernetes `pathType` | Control Plane Route | Example | | ------------------------ | -------------------------- | ----------------------------------------------------- | | `Prefix` | `prefix: "/api"` | Default behavior | | `Exact` | `regex: "^/api/v1/users$"` | Special characters escaped, anchored with `^` and `$` | | `ImplementationSpecific` | `prefix: "/legacy"` | Treated as Prefix | #### Service-to-workload resolution The converter resolves each Ingress backend to a Control Plane workload through the following steps: The converter locates the Kubernetes Service referenced by the Ingress backend's `service.name`. The Service's `selector` labels are matched against workload pod template labels to find the target workload. The Service port's `targetPort` is resolved to the actual container port: * **Numeric targetPort** — used directly as the container port * **Named targetPort** — resolved by matching the port name in the container spec * **No targetPort** — falls back to the Service port number #### Domain defaults All converted domains use these fixed settings: | Property | Value | | ------------------------- | -------- | | **Port** | `443` | | **Protocol** | `http2` | | **DNS mode** | `cname` | | **Certificate challenge** | `http01` | #### Wildcard and apex domain merging When an Ingress contains rules for both `example.com` and `*.example.com`, the converter merges them into a single domain named `example.com` with `acceptAllSubdomains: true`. Routes from both rules are combined and deduplicated. Domains with `acceptAllSubdomains` enabled require a [Dedicated Load Balancer](/reference/gvc#load-balancer) on the target GVC. #### GVC context If a `--gvc` is provided, workload links use it directly (e.g., `//gvc/my-gvc/workload/api`). Otherwise, the converter inserts a `{{GVC}}` placeholder that you must replace before applying. ### Auto-generated resources When workloads reference secrets (via environment variables or volume mounts), the converter automatically creates: 1. **Identity** - Named `identity-{workload-name}`, linked to the workload 2. **Policy** - Named `policy-{workload-name}`, granting `reveal` permission on referenced secrets This ensures workloads can access their secrets without manual policy configuration. ### Firewall and public exposure The converter analyzes your Kubernetes Services and Ingresses to determine how each workload should be reachable: | Kubernetes Configuration | Control Plane Firewall | | --------------------------------------------------------------------------- | ------------------------------------------------------- | | Service type `LoadBalancer` matching pod labels | External inbound allowed (`0.0.0.0/0`) | | Ingress routing to a Service matching pod labels | External inbound allowed (`0.0.0.0/0`) | | No public exposure detected | External inbound blocked (default) | | Any Service selecting the pod (ClusterIP, NodePort, LoadBalancer, headless) | Internal inbound allowed from the same GVC (`same-gvc`) | | No Service selecting the pod | Internal inbound blocked (`none`, default) | On Kubernetes, any pod can reach a Service inside the cluster. The converter preserves that reachability: a workload backed by a Service converts with `firewallConfig.internal.inboundAllowType: same-gvc`, so dependent workloads (databases, message brokers, internal APIs) keep talking to each other after migration. Use the `cpln.io/internal-inbound-allow-type` [annotation](#conversion-annotations) to override the inference per workload — for example, `none` to lock a workload down even though a Service selects it. All workloads have external **outbound** traffic allowed by default (`0.0.0.0/0`). When a workload's internal firewall is `none`, calls from other workloads in the GVC fail in ways that look like application errors on the caller side (e.g., `connection reset by peer`); the target workload's access log shows `rbac_access_denied_matched_policy[none]`. If intra-GVC calls fail after a conversion done with an older CLI, set `same-gvc` on the target: `cpln workload update --set spec.firewallConfig.internal.inboundAllowType=same-gvc`. ### Informational resources These Kubernetes resources are not directly converted but inform the conversion: | Resource | How it's used | | --------------------------- | --------------------------------------------------------------------------------------------------- | | **HorizontalPodAutoscaler** | Sets `minScale`, `maxScale`, `scaleToZeroDelay`, and CPU target | | **Service** | Protocol inference, internal firewall inference, public exposure detection, domain route resolution | | **ServiceAccount** | Image pull secrets extraction | | **EndpointSlice** | Service-to-Pod mapping for selectorless services | | **PersistentVolume** | Capacity and file system type for volumesets | | **StorageClass** | Performance class and file system type for volumesets | ## Basic usage ```bash theme={null} cpln convert --file ``` This outputs the converted Control Plane resources to stdout. ## Options | Option | Description | | ------------ | ------------------------------------------------------------------------- | | `--file` | Path to K8s JSON/YAML file. Use `--file -` for stdin | | `--protocol` | Override port protocol for all containers: `http`, `http2`, `grpc`, `tcp` | | `--verbose` | Show original K8s resources with ignored properties highlighted | ## How port protocol is inferred When you don't specify `--protocol`, the converter automatically infers the protocol for each container port using a multi-level strategy (in priority order): If a Kubernetes Service explicitly declares `appProtocol` on a port that targets this container, that protocol is used. ```yaml theme={null} spec: ports: - port: 50051 targetPort: 50051 appProtocol: grpc # Explicitly declares gRPC ``` The converter checks if any Service port targeting this container has a name with a protocol prefix. ```yaml theme={null} spec: ports: - name: grpc-api # "grpc" prefix → gRPC protocol port: 50051 ``` The container's own port name is checked for protocol prefixes. ```yaml theme={null} ports: - name: http-web # "http" prefix → HTTP protocol containerPort: 8080 ``` If the port has a liveness or readiness probe, the probe type determines the protocol: * `grpc` probe → gRPC * `httpGet` probe → HTTP * `tcpSocket` probe → TCP Common port numbers are mapped to their typical protocols. See the tables below. ### Recognized protocol prefixes Port names starting with these prefixes are automatically mapped: | Prefix | Protocol | | ----------------------------- | -------- | | `http`, `https`, `ws`, `wss` | HTTP | | `http2`, `h2`, `h2c` | HTTP/2 | | `grpc`, `grpc-web`, `grpcweb` | gRPC | ### Well-known port mappings These port numbers are automatically assigned protocols when no other signal is available: | Port | Protocol | | ---------------------------- | -------- | | 80, 81, 443 | HTTP | | 3000, 3001, 3002 | HTTP | | 8000, 8008, 8080, 8081, 8088 | HTTP | | 8443, 9090 | HTTP | | Port | Protocol | | --------------------------------- | -------- | | 50051, 50052, 50053, 50054, 50055 | gRPC | | 6565 | gRPC | If no protocol can be inferred, the converter defaults to `tcp`. ## Example conversion Create a file named `k8s.yaml`: ```yaml k8s.yaml theme={null} apiVersion: apps/v1 kind: Deployment metadata: name: example-deployment labels: app: nginx spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:latest ports: - containerPort: 80 ``` ```bash theme={null} cpln convert --file k8s.yaml ``` ```yaml theme={null} kind: workload name: example-deployment spec: type: standard containers: - name: nginx image: 'nginx:latest' ports: - number: 80 protocol: http defaultOptions: capacityAI: false autoscaling: minScale: 3 maxScale: 3 ``` Convert and apply in one command: ```bash theme={null} cpln apply --file k8s.yaml --k8s true ``` Or pipe the conversion output: ```bash theme={null} cpln convert --file k8s.yaml | cpln apply --file - ``` ## More conversion examples A simple Deployment with one HTTP port becomes standard (the default type): ```yaml k8s-deployment.yaml theme={null} apiVersion: apps/v1 kind: Deployment metadata: name: api spec: replicas: 2 selector: matchLabels: app: api template: spec: containers: - name: api image: my-api:v1 ports: - containerPort: 8080 ``` Converts to a **standard** workload (the default type for all non-cron, non-stateful workloads). A Deployment with multiple ports or gRPC probes also becomes standard: ```yaml k8s-multiport.yaml theme={null} apiVersion: apps/v1 kind: Deployment metadata: name: grpc-service spec: replicas: 2 selector: matchLabels: app: grpc-service template: spec: containers: - name: grpc-service image: my-grpc:v1 ports: - containerPort: 50051 readinessProbe: grpc: port: 50051 ``` Converts to a **standard** workload (gRPC probe triggers standard type). A StatefulSet with persistent storage becomes stateful: ```yaml k8s-statefulset.yaml theme={null} apiVersion: apps/v1 kind: StatefulSet metadata: name: postgres spec: serviceName: postgres replicas: 1 selector: matchLabels: app: postgres template: spec: containers: - name: postgres image: postgres:15 ports: - containerPort: 5432 volumeMounts: - name: data mountPath: /var/lib/postgresql/data volumeClaimTemplates: - metadata: name: data spec: accessModes: ["ReadWriteOnce"] resources: requests: storage: 10Gi ``` Converts to a **stateful** workload with a volumeset (storage triggers stateful type). CronJobs preserve their schedule: ```yaml k8s-cronjob.yaml theme={null} apiVersion: batch/v1 kind: CronJob metadata: name: daily-backup spec: schedule: "0 2 * * *" jobTemplate: spec: template: spec: containers: - name: backup image: backup-tool:latest command: ["/bin/sh", "-c", "backup.sh"] restartPolicy: OnFailure ``` Converts to a **cron** workload with schedule `0 2 * * *`. ConfigMaps become dictionary secrets: ```yaml k8s-configmap.yaml theme={null} apiVersion: v1 kind: ConfigMap metadata: name: app-config data: DATABASE_URL: "postgres://localhost:5432/mydb" LOG_LEVEL: "info" ``` Converts to a **dictionary secret** containing the key-value pairs. An Ingress with a Deployment and Service becomes a domain with routes: ```yaml k8s-ingress.yaml theme={null} apiVersion: apps/v1 kind: Deployment metadata: name: api-server spec: replicas: 2 selector: matchLabels: app: api template: metadata: labels: app: api spec: containers: - name: api image: my-api:v1 ports: - containerPort: 8080 --- apiVersion: v1 kind: Service metadata: name: api-service spec: selector: app: api ports: - port: 80 targetPort: 8080 --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: api-ingress labels: team: backend spec: rules: - host: api.example.com http: paths: - path: /api pathType: Prefix backend: service: name: api-service port: number: 80 ``` Converts to a **workload** and a **domain** with routes pointing to the workload: ```yaml theme={null} kind: workload name: api-server spec: type: standard containers: - name: api image: 'my-api:v1' ports: - number: 8080 protocol: http defaultOptions: capacityAI: false autoscaling: minScale: 2 maxScale: 2 firewallConfig: external: inboundAllowCIDR: - 0.0.0.0/0 outboundAllowCIDR: - 0.0.0.0/0 internal: inboundAllowType: same-gvc inboundAllowWorkload: [] --- kind: domain name: api.example.com description: 'Converted from K8s Ingress: api-ingress' tags: team: backend spec: dnsMode: cname certChallengeType: http01 ports: - number: 443 protocol: http2 routes: - prefix: /api workloadLink: '//gvc/{{GVC}}/workload/api-server' port: 8080 ``` Provide `--gvc` to replace the `{{GVC}}` placeholder with an actual GVC name in workload links. ## Override protocol inference To bypass automatic protocol inference and set a specific protocol for all ports, use the `--protocol` flag: ```bash theme={null} # Force all ports to use gRPC cpln convert --file k8s.yaml --protocol grpc # Force all ports to use TCP cpln convert --file k8s.yaml --protocol tcp ``` ## Verbose mode Use `--verbose` to see which K8s properties were converted and which were ignored: ```bash theme={null} cpln convert --file k8s.yaml --verbose ``` Ignored properties are highlighted in yellow and marked with `(ignored)`. ## Delete converted resources To remove resources that were applied from a K8s manifest: ```bash theme={null} cpln delete --file k8s.yaml --k8s true ``` ## Troubleshooting The workload type is immutable. This error means the type inferred by the current conversion differs from the type of the already-existing workload — commonly after upgrading across CLI v3.9.0, which changed the inferred default from a serverless/standard mix to `standard`. Pin the type in the K8s manifest so it stays stable across CLI versions: ```yaml theme={null} metadata: annotations: cpln.io/workload-type: serverless ``` To intentionally change a workload's type, create a new workload with the target type and migrate traffic to it — the type cannot be changed in place. Workloads not selected by any Kubernetes Service convert with the internal firewall closed (`inboundAllowType: none`). Callers see errors like `connection reset by peer` while the target's access log shows `rbac_access_denied_matched_policy[none]`. Include the Services in the conversion input so the converter infers `same-gvc`, set the `cpln.io/internal-inbound-allow-type` annotation, or open the firewall after the fact with `cpln workload update --set spec.firewallConfig.internal.inboundAllowType=same-gvc`. `envFrom` sources expand only when the referenced ConfigMap or Secret is part of the conversion input, because the converter needs the keys. Check the conversion warnings on stderr: a Secret that lives only on Control Plane cannot be expanded — include its manifest in the input or add the variables to `env` manually. Some Kubernetes resources don't have direct Control Plane equivalents. Use `--verbose` to see which properties are ignored during conversion. ConfigMaps are converted to dictionary secrets. Reference them in your workload using environment variables or volume mounts pointing to the secret. HorizontalPodAutoscaler settings inform the workload's autoscaling configuration. Check the converted workload's `defaultOptions.autoscaling` section. Services are used to inform port mappings but don't create standalone resources. Ports are configured directly on the workload's container spec. PersistentVolumeClaims are converted to volumesets. Ensure the PVC is referenced by a StatefulSet's volumeClaimTemplates for proper conversion. Ingress conversion requires the referenced Services and their matching workloads (Deployments, StatefulSets, etc.) to be present in the same file. The converter uses the Service selector to find the target workload by matching pod template labels. Ingress backend Services must have a `selector` defined so the converter can resolve which workload the Service targets. Services without selectors (e.g., ExternalName services) are not supported for domain conversion. When converting Ingresses without specifying `--gvc`, workload links contain a `{{GVC}}` placeholder. Provide `--gvc` during conversion or replace the placeholder before applying. ## Next steps Learn more about applying resources Remove resources from Control Plane Understand workload configuration Convert command reference # Copy Files to/from Workloads Source: https://docs.controlplane.com/guides/cli/cpln-cp Transfer files and directories between your local machine and running workload containers. The `cpln cp` command copies files and directories between your local machine and workload containers, similar to `kubectl cp` or `docker cp`. ## When to use this Copy config files, scripts, or data to a running container Retrieve log files, generated data, or debug artifacts from containers Transfer debugging tools or inspect container state Move files between local development and deployed workloads ## Prerequisites Install the Control Plane CLI before proceeding. See [Installation](/cli-reference/installation). You need a running [workload](/reference/workload) in at least one location. See the [Create a Workload](/guides/create-workload) guide. The `tar` binary must be installed in the container image. If your container image doesn't include `tar`, the `cpln cp` command will fail. Most standard images (Debian, Ubuntu, Alpine) include it by default. You need `exec` permission on the workload. See [Workload Permissions](/reference/workload#permissions). ## Basic usage ```bash theme={null} # Copy to workload cpln cp : # Copy from workload cpln cp : ``` ## Options | Option | Description | | --------------- | ---------------------------------------------------- | | `--location` | Target location (defaults to first available in GVC) | | `--replica` | Target replica (defaults to first replica) | | `--container` | Target container (defaults to first container) | | `--no-preserve` | Don't preserve ownership and permissions | ## Copy to a workload Copy a local file to a container: ```bash theme={null} cpln cp config.json my-app:/app/config/ ``` The file is placed in `/app/config/config.json` in the container. Copy an entire directory: ```bash theme={null} cpln cp ./data my-app:/app/data ``` The directory and all contents are copied recursively. Copy and rename in one command: ```bash theme={null} cpln cp local.json my-app:/app/config/settings.json ``` ## Copy from a workload Download a file from a container: ```bash theme={null} cpln cp my-app:/var/log/app.log ./logs/ ``` Download an entire directory: ```bash theme={null} cpln cp my-app:/app/generated ./output/ ``` Download and rename: ```bash theme={null} cpln cp my-app:/app/data.json ./backup-data.json ``` ## Target specific replicas When a workload has multiple replicas across locations, specify the exact target: ```bash theme={null} cpln cp config.json my-app:/app/ \ --location aws-us-west-2 \ --replica my-app-7f8d9c-abc12 ``` Use `cpln workload replica get ` to list available replicas and their locations. ## Multi-container workloads For workloads with multiple containers, specify the target container: ```bash theme={null} cpln cp logs.sh my-app:/scripts/ --container sidecar ``` If `--container` is not specified, the command targets the first container defined in the workload spec. ## Common workflows ### Upload a configuration file ```bash theme={null} # Copy config to a specific environment cpln cp production.env my-app:/app/.env --gvc production ``` ### Download application logs ```bash theme={null} # Get logs from a specific location cpln cp my-app:/var/log/app/ ./debug-logs/ --location aws-eu-central-1 ``` ### Transfer debug tools ```bash theme={null} # Upload a debugging script cpln cp debug.sh my-app:/tmp/ cpln workload exec my-app -- chmod +x /tmp/debug.sh cpln workload exec my-app -- /tmp/debug.sh ``` ## Troubleshooting The container image doesn't include `tar`. Either: * Use an image with `tar` installed * Install it in your Dockerfile: `RUN apt-get install -y tar` * For Alpine: `RUN apk add --no-cache tar` The destination path may require elevated permissions. Try copying to `/tmp/` first, then move with `exec`: ```bash theme={null} cpln cp file.txt my-app:/tmp/ cpln workload exec my-app -- mv /tmp/file.txt /app/ ``` The workload may not be running in the specified location: ```bash theme={null} # Check workload status cpln workload get my-app # List replicas cpln workload replica get my-app ``` ## Next steps Run commands inside containers Interactive shell access Access workload ports locally Full cp command reference # Kubernetes Operator Source: https://docs.controlplane.com/guides/cli/cpln-operator Deploy and manage Control Plane resources using Kubernetes custom resource definitions (CRDs). The Control Plane [Kubernetes Operator](/core/kubernetes-operator) enables you to manage Control Plane resources directly from your Kubernetes cluster using custom resource definitions (CRDs). It bridges the gap between Kubernetes-native workflows and Control Plane infrastructure. ## What you'll achieve By the end of this guide, you will have: 1. A Kubernetes cluster with the Control Plane operator installed 2. Authentication configured between your cluster and Control Plane 3. The ability to deploy and manage Control Plane resources using Kubernetes manifests 4. Optional ArgoCD integration for GitOps workflows ## When to use this Manage Control Plane resources alongside your Kubernetes manifests in Git Deploy Control Plane resources through ArgoCD applications Manage GVCs, workloads, secrets, and more using CRDs Define your entire Control Plane infrastructure declaratively ## Prerequisites A running Kubernetes cluster (v1.19+). This can be: * A managed cluster (EKS, GKE, AKS) * A local cluster (kind, minikube, Docker Desktop) * Any conformant Kubernetes distribution If you don't have a cluster, see [Quick start (local cluster)](#quick-start-local-cluster) to set one up. Install Helm v3.0+ for deploying the operator: ```bash theme={null} # macOS brew install helm # Linux curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash # Windows choco install kubernetes-helm ``` For other installation methods, see the [Helm installation guide](https://helm.sh/docs/intro/install/). Ensure kubectl is configured and can communicate with your cluster: ```bash theme={null} kubectl cluster-info ``` You need to be signed up to Control Plane and have access to an org. If you don't already: * [Sign up](https://console.cpln.io/signup) for Control Plane and create a [billing account](/concepts/billing) * [Create an Org](/guides/create-org) You also need permissions to create service accounts within your org. ## Quick start (local cluster) If you don't have a Kubernetes cluster, you can set up a local one for testing. Docker must be installed and running on your machine for local Kubernetes clusters. [Install kind](https://kind.sigs.k8s.io/docs/user/quick-start/#installation), then create a cluster: ```bash theme={null} kind create cluster --name cpln-operator # Verify kubectl cluster-info ``` Then continue with the [installation steps](#installation) below. [Install minikube](https://minikube.sigs.k8s.io/docs/start/), then start a cluster: ```bash theme={null} minikube start # Verify kubectl cluster-info ``` Then continue with the [installation steps](#installation) below. ## Installation ### Step 1: Install cert-manager The operator requires [cert-manager](https://cert-manager.io/) for webhook certificate management. ```bash theme={null} kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.16.3/cert-manager.yaml ``` Wait for cert-manager to be ready: ```bash theme={null} kubectl wait --for=condition=Available deployment --all -n cert-manager --timeout=300s ``` ### Step 2: Install the operator Add the Control Plane Helm repository and install the operator: ```bash theme={null} # Add the Helm repository helm repo add cpln https://controlplane-com.github.io/k8s-operator helm repo update # Install the operator in the controlplane namespace helm install cpln-operator cpln/cpln-operator \ -n controlplane \ --create-namespace ``` Verify the operator is running: ```bash theme={null} kubectl get pods -n controlplane -l app=operator ``` ### Step 3: Configure authentication The operator needs credentials to communicate with the Control Plane API. You have two options: The CLI automatically creates the service account and Kubernetes secret: ```bash theme={null} cpln operator install \ --serviceaccount k8s-operator \ --org YOUR_ORG_NAME ``` See the [CLI reference](/cli-reference/commands/operator#operator-install) for available options. If you prefer manual control, create the secret yourself: **1. Create a service account in Control Plane:** ```bash theme={null} cpln serviceaccount create --name k8s-operator --org YOUR_ORG_NAME ``` **2. Add it to a group with appropriate permissions:** ```bash theme={null} cpln group add-member superusers --serviceaccount k8s-operator --org YOUR_ORG_NAME ``` **3. Generate a key:** ```bash theme={null} cpln serviceaccount add-key k8s-operator --description "cpln operator" --org YOUR_ORG_NAME -o json ``` Save the `key` value from the output. **4. Create the Kubernetes secret:** ```bash theme={null} # Create the namespace if it doesn't exist kubectl create namespace controlplane --dry-run=client -o yaml | kubectl apply -f - ``` Create a file named `operator-secret.yaml` with the following content: ```yaml theme={null} apiVersion: v1 kind: Secret metadata: name: YOUR_ORG_NAME namespace: controlplane labels: app.kubernetes.io/managed-by: cpln-operator annotations: cpln.io/service-account: k8s-operator data: token: BASE64_ENCODED_KEY # base64 encode your key: echo -n "YOUR_KEY" | base64 ``` Apply the secret: ```bash theme={null} kubectl apply -f operator-secret.yaml ``` ## Deploying resources Control Plane resources are managed from Kubernetes manifests. Each custom resource requires: * `org`: The target Control Plane organization * `gvc`: The target GVC (for GVC-scoped resources like workloads and identities) We recommend organizing resources by namespace: * One namespace per GVC for GVC-scoped resources (workloads, identities, volumesets) * One namespace per org for org-scoped resources (GVCs, secrets, policies) The custom resource structure differs from standard Kubernetes resources. Fields like `org`, `gvc`, and `description` are at the top level, not inside `spec`. Always use the [export feature](/core/kubernetes-operator#exporting-resources-as-kubernetes-manifests) to generate accurate manifests. ### Apply with kubectl You can apply Kubernetes manifests directly using kubectl. Save the following GVC manifest to a file (e.g., `gvc.yaml`): ```yaml theme={null} apiVersion: cpln.io/v1 kind: gvc metadata: name: my-gvc namespace: default org: YOUR_ORG_NAME # Replace with your Control Plane org name description: my-gvc spec: staticPlacement: locationLinks: - //location/aws-eu-central-1 ``` Apply it to your cluster: ```bash theme={null} kubectl apply -f gvc.yaml ``` Verify the resource was created: ```bash theme={null} kubectl get gvcs ``` The operator syncs the resource to Control Plane. You can verify the GVC exists: ```bash theme={null} cpln gvc get my-gvc --org YOUR_ORG_NAME ``` ## ArgoCD integration The operator integrates seamlessly with ArgoCD for GitOps workflows. Once the operator is installed, you can point ArgoCD at a Git repository containing YAML manifests or a Helm chart. This section assumes ArgoCD is already installed on your cluster. See the [ArgoCD installation guide](https://argo-cd.readthedocs.io/en/stable/getting_started/) if you haven't set it up yet. ### Defining ArgoCD applications An ArgoCD Application defines what to deploy (source) and where to deploy it (destination). For Control Plane resources, you can use either a Helm chart or raw YAML manifests stored in a Git repository. Save the manifest to a file (e.g., `app.yaml`) and update the placeholder values before applying. Point ArgoCD at a Helm repository containing your Control Plane resource templates: ```yaml theme={null} apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: my-helm-app namespace: argocd # This is usually where ArgoCD is installed spec: project: default destination: server: https://kubernetes.default.svc # Cluster API server URL namespace: your-namespace # Target namespace in your cluster source: repoURL: https://your-org.github.io/your-repo/ # URL of your Helm repository chart: my-cpln-chart # Name of your Helm chart targetRevision: 0.1.0 # Chart version helm: values: | org: your-org-name syncPolicy: automated: prune: true # Automatically delete resources no longer defined in the chart selfHeal: true # Automatically sync drifted resources ``` Point ArgoCD at a Git repository containing Control Plane resource manifests: ```yaml theme={null} apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: my-cpln-app namespace: argocd # This is usually where ArgoCD is installed spec: project: default destination: server: https://kubernetes.default.svc # Cluster API server URL namespace: your-namespace # Target namespace in your cluster source: repoURL: https://github.com/your-org/your-repo.git # Your Git repository URL path: manifests # Path to the directory containing YAML files targetRevision: main # Branch, tag, or commit syncPolicy: automated: prune: true # Automatically delete resources no longer defined in the repo selfHeal: true # Automatically sync drifted resources ``` Apply your ArgoCD Application: ```bash theme={null} kubectl -n argocd apply -f app.yaml ``` ### Example application The [k8s-operator repository](https://github.com/controlplane-com/k8s-operator) includes a ready-to-use example that deploys a GVC, workload, identity, and other resources. Copy the following manifest and save it to a file (e.g., `example-app.yaml`): ```yaml theme={null} apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: my-helm-app namespace: argocd spec: project: default destination: server: 'https://kubernetes.default.svc' namespace: fresh source: repoURL: 'https://cuppojoe.github.io/argo-example/' chart: argo-example targetRevision: 0.2.3 helm: values: | org: YOUR_ORG_NAME # Replace with your Control Plane org name syncPolicy: automated: prune: true selfHeal: true ``` Replace `YOUR_ORG_NAME` with your actual Control Plane org name before applying. Apply the manifest: ```bash theme={null} kubectl apply -f example-app.yaml ``` The example Helm chart creates the following Control Plane resources: * **GVC** named `fresh` in `aws-eu-central-1` * **Workload** with a serverless container * **Identity** for cloud access * **Policy** for permissions * **Secret** for credentials * Additional resources (agent, domain, ipset) ### Connecting to the ArgoCD UI To access the ArgoCD UI, retrieve the admin password and port-forward the service: ```bash theme={null} # Print the initial admin password kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath='{.data.password}' | base64 -d && echo # Port-forward to the ArgoCD UI kubectl -n argocd port-forward service/argocd-server 18081:443 ``` Open a browser and navigate to `https://localhost:18081`. Accept the self-signed certificate and log in with username `admin` and the password from the command above. Store your Control Plane manifests in a Git repository and point ArgoCD at it. Changes merged to your main branch will automatically sync to Control Plane. ## Uninstalling ### Remove operator credentials Remove the authentication secret: ```bash theme={null} cpln operator uninstall --org YOUR_ORG_NAME ``` Or manually: ```bash theme={null} kubectl delete secret YOUR_ORG_NAME -n controlplane ``` ### Remove the operator ```bash theme={null} helm uninstall cpln-operator -n controlplane ``` ### Remove cert-manager (optional) If you no longer need cert-manager: ```bash theme={null} kubectl delete -f https://github.com/cert-manager/cert-manager/releases/download/v1.16.3/cert-manager.yaml ``` Uninstalling the operator does not delete Control Plane resources that were created by it. Resources in Control Plane will continue to exist. ### Preventing resource deletion Deleting a Kubernetes resource while the operator is running will remove the corresponding resource from Control Plane. To prevent this, add the `cpln.io/resource-policy: keep` annotation: ```yaml theme={null} kind: gvc apiVersion: cpln.io/v1 org: your-org-name metadata: name: production annotations: cpln.io/resource-policy: keep spec: # ... ``` With this annotation, deleting the Kubernetes resource will not delete the Control Plane resource. ## Troubleshooting Check cert-manager is running: ```bash theme={null} kubectl get pods -n cert-manager ``` Verify webhook certificates: ```bash theme={null} kubectl get certificates -n controlplane ``` Check operator logs: ```bash theme={null} kubectl logs -n controlplane -l app=operator -f ``` Verify the authentication secret exists (look for a secret named after your org): ```bash theme={null} kubectl get secrets -n controlplane ``` If the secret for your org doesn't exist, run the [`cpln operator install`](#using-the-cli-recommended) command to create it. Ensure the service account has appropriate permissions in Control Plane. Check which group and/or policy the service account belongs to and verify it has the necessary permissions. To reconfigure authentication, run the [`cpln operator install`](#using-the-cli-recommended) command which will create a service account and add it to the `superusers` group. View the full resource spec available for each CRD: ```bash theme={null} kubectl explain gvc.spec kubectl explain workload.spec ``` Create the controlplane namespace: ```bash theme={null} kubectl create namespace controlplane ``` Secrets use native Kubernetes Secret objects with special labels, not CRDs. See the [secrets handling](/core/kubernetes-operator#secrets-handling) documentation for the correct format and required labels. ## Next steps Learn about CRD structure, secrets handling, and more Learn about GVC configuration options Explore workload deployment options Configure cloud access and permissions # Port Forward to Workloads Source: https://docs.controlplane.com/guides/cli/cpln-port-forward Access workload ports locally by forwarding traffic through a secure tunnel. The `cpln port-forward` command creates a secure tunnel between your local machine and a workload, allowing you to access ports without exposing them publicly. Similar to `kubectl port-forward`, this enables local access to databases, internal services, and debugging endpoints. ## When to use this Connect to PostgreSQL, MySQL, Redis, or other databases running in workloads Access admin panels, metrics endpoints, or debug ports locally Test against live services without deploying your code Interact with services that aren't publicly exposed ## Prerequisites Install the Control Plane CLI. See [Installation](/cli-reference/installation). You need a running [workload](/reference/workload) in at least one location. See the [Create a Workload](/guides/create-workload) guide. You need `connect` permission on the workload. See [Workload Permissions](/reference/workload#permissions). ## Basic usage ```bash theme={null} cpln port-forward : ``` This forwards traffic from `localhost:` to `` on the workload. You can get a ready-to-use port-forward command from the Console. Navigate to your workload, click **Connect** in the left pane, and copy the port-forward command which includes the location and port mapping (e.g., `80:80`). ## Options | Option | Description | | ------------ | -------------------------------------------------------------------- | | `--address` | Local addresses to listen on, comma-separated (default: `localhost`) | | `--location` | Target location (defaults to first available in GVC) | | `--replica` | Target a specific replica (defaults to any available) | ## Port format options Map a different local port to the remote port: ```bash theme={null} cpln port-forward my-db 5433:5432 ``` Access the database at `localhost:5433`. Use the same port locally and remotely: ```bash theme={null} cpln port-forward my-db 5432 ``` This is shorthand for `5432:5432`. Let the system assign a local port: ```bash theme={null} cpln port-forward my-db :5432 ``` The CLI outputs the assigned port. Useful when the port may be in use. ## Forward multiple ports Forward several ports in one command: ```bash theme={null} cpln port-forward my-app 8080:80 8443:443 9090:9090 ``` Access the app at: * `localhost:8080` (HTTP) * `localhost:8443` (HTTPS) * `localhost:9090` (metrics) ## Listen on multiple addresses Expose the forwarded port on multiple network interfaces: ```bash theme={null} cpln port-forward my-db 5432:5432 --address 127.0.0.1,192.168.1.10 ``` This allows other machines on your LAN to connect through `192.168.1.10:5432`. Listening on non-localhost addresses exposes the port to your network. Use with caution. ## Target specific locations Forward to a workload in a specific location: ```bash theme={null} cpln port-forward my-db 5432:5432 --location aws-eu-central-1 ``` Choosing a location closer to you reduces latency. ## Common workflows ### Connect to PostgreSQL ```bash theme={null} # Start port forward cpln port-forward postgres-db 5432:5432 --gvc production # In another terminal, connect with psql psql -h localhost -p 5432 -U postgres ``` ### Access Redis ```bash theme={null} cpln port-forward redis-cache 6379:6379 # Connect with redis-cli redis-cli -h localhost -p 6379 ``` ### Debug a web service ```bash theme={null} cpln port-forward my-api 8080:80 9090:9090 # Access the API curl http://localhost:8080/health # Access metrics curl http://localhost:9090/metrics ``` ### Access admin panels ```bash theme={null} # Forward to admin port cpln port-forward my-app 9000:9000 # Open in browser open http://localhost:9000/admin ``` ## Port forwarding to unexposed ports You can forward to any port on the workload, even if no container is listening on that port. This is useful for debugging or accessing services that bind to ports dynamically. ## Troubleshooting The local port is occupied. Use a different port or an ephemeral port: ```bash theme={null} cpln port-forward my-db :5432 ``` The workload may not be listening on that port. Verify the container port: ```bash theme={null} cpln workload get my-db --output yaml ``` Check the `ports` section in the container spec. The workload may not be running in the specified location: ```bash theme={null} cpln workload replica get my-db ``` Try a different location or wait for replicas to start. You need `connect` permission on the workload. Check your policies. ## Next steps Interactive shell access Run commands in containers Transfer files to/from workloads Full port-forward command reference # Connect to Workloads Source: https://docs.controlplane.com/guides/cli/workload/connect Open an interactive shell session inside a running workload container. The `cpln workload connect` command opens an interactive terminal session to a running container, similar to `docker exec -it` or `kubectl exec -it`. Use it to inspect container state, debug issues, or run commands interactively. ## When to use this Explore container filesystem, check processes, and inspect state Diagnose issues directly inside the running container Run one-off commands, database migrations, or maintenance tasks View logs and files that aren't exposed externally ## Prerequisites Install the Control Plane CLI. See [Installation](/cli-reference/installation). You need a running [workload](/reference/workload) in at least one location. See the [Create a Workload](/guides/create-workload) guide. You need `exec` permission on the workload. See [Workload Permissions](/reference/workload#permissions). ## Connect via Console You can connect to workloads directly from the Control Plane Console: Open your workload in the Console. In the left pane, click **Connect**. Use the dropdowns to select: * **Location**: The deployment location to connect to * **Container**: The container within the workload * **Replica**: The specific replica instance Type the shell command to use (e.g., `bash`, `sh`, `node`). Click **Connect** to open an interactive terminal session in your browser. You can copy the generated `cpln workload connect` command from the Console and paste it into your terminal to connect from the CLI instead. ## Connect via CLI ### Basic usage ```bash theme={null} cpln workload connect ``` This opens a `bash` shell in the first available container and replica. ### Options | Option | Description | | ------------- | --------------------------------------------------------- | | `--location` | Target location (defaults to first available in GVC) | | `--replica` | Target a specific replica (defaults to first replica) | | `--container` | Target a specific container (defaults to first container) | | `--shell, -s` | Shell to use (default: `bash`) | ### Connect to a specific location ```bash theme={null} cpln workload connect my-app --location aws-eu-central-1 ``` List available locations with `cpln workload get-deployments my-app`. ### Connect to a specific replica When a workload has multiple replicas, target a specific one: ```bash theme={null} # List replicas cpln workload replica get my-app --location aws-eu-central-1 # Connect to a specific replica cpln workload connect my-app --replica my-app-7f8d9c-abc12 ``` ### Connect to a specific container For workloads with multiple containers (sidecars, init containers): ```bash theme={null} cpln workload connect my-app --container sidecar ``` ### Use a different shell Some containers don't have `bash`. Use `sh` or another shell: ```bash theme={null} cpln workload connect my-app --shell sh ``` ```bash theme={null} cpln workload connect my-app --shell ash ``` ```bash theme={null} cpln workload connect my-app --shell zsh ``` ### Common workflows #### Debug a crashing container ```bash theme={null} # Connect and check logs cpln workload connect my-app cat /var/log/app.log # Check processes ps aux # Check environment env | grep -i config ``` #### Inspect database state ```bash theme={null} cpln workload connect postgres-db --shell sh # Inside the container psql -U postgres -c "SELECT * FROM users LIMIT 10;" ``` #### Check filesystem ```bash theme={null} cpln workload connect my-app # Check disk usage df -h # Find large files du -sh /app/* ``` #### Run a migration ```bash theme={null} cpln workload connect my-app # Run database migration for example npm run migrate # or python manage.py migrate ``` ## Troubleshooting The container doesn't have bash. Use a different shell: ```bash theme={null} cpln workload connect my-app --shell sh ``` Alpine-based images typically use `ash` or `sh`. The workload may not be running: ```bash theme={null} cpln workload get my-app cpln workload replica get my-app ``` Wait for replicas to start or check the workload configuration. The shell may be exiting. Try with a different shell or check container health: ```bash theme={null} cpln workload get-deployments my-app ``` You need `exec` permission on the workload. Check your policies or contact your admin. ## Next steps Run non-interactive commands Transfer files to/from workloads Access workload ports locally Full connect command reference # Run Commands with Cron Workloads Source: https://docs.controlplane.com/guides/cli/workload/cron-run Use a persistent cron workload runner to execute commands with better performance. The `cpln workload cron run` command runs a command using a persistent suspended cron workload. The runner workload is created on first use and reused for subsequent runs, avoiding the overhead of creating and deleting a workload each time. This is the recommended approach for running one-off commands. If you need to create a temporary standard workload instead (e.g., to clone an existing workload's configuration), use [`cpln workload run`](/guides/cli/workload/run). ## When to use this Execute database migrations with fast startup times Run maintenance scripts repeatedly without workload creation overhead Fire-and-forget jobs that run without blocking your terminal Connect to an interactive session inside the runner workload ## Prerequisites Install the Control Plane CLI. See [Installation](/cli-reference/installation). You need `create` and `exec` permissions on workloads. See [Workload Permissions](/reference/workload#permissions). ## Basic usage ```bash theme={null} cpln workload cron run -- [args...] ``` On first use, this: 1. Creates a persistent suspended cron workload as the runner 2. Starts a job execution with your command 3. Waits for the job to complete and outputs the result On subsequent runs, the existing runner workload is reused, making startup significantly faster. ## Options | Option | Description | | ------------------------- | ----------------------------------------------------------------------------------------------------------- | | `--image` | Override image | | `-i, --interactive` | Make the session interactive | | `-b, --background` | Run in background without waiting for completion (fire and forget) | | `-t, --timeout ` | Maximum time to wait for job completion (default: 600) | | `--cpu` | Set allocated CPU for the main container | | `--memory, --mem` | Set allocated memory for the main container | | `--env` | Environment variables in KEY=VALUE format (can be specified multiple times) | | `-s, --shell` | Shell to use when interactive flag is true (default: `bash`) | | `--location` | Location to run the command | | `--container` | Which container to run the command in | | `--tag` | Attach tags (e.g., `--tag drink=water`) | | `--identity` | Identity to assign to the cron runner workload. Reuses an existing runner with that identity if one exists. | `--interactive` and `--background` are mutually exclusive. ## Execution modes ### Default (wait for completion) Waits for the job to complete and outputs the final status: ```bash theme={null} cpln workload cron run -- echo "Hello from Control Plane!" ``` ### Background mode Fires and forgets, outputting the job ID immediately: ```bash theme={null} cpln workload cron run --image node:20 --background -- npm run db:migrate ``` Use this for long-running tasks where you don't need to wait for the result. ### Interactive mode Waits for the job to start, then connects via WebSocket for an interactive terminal session: ```bash theme={null} cpln workload cron run --interactive -- bash ``` ### Specify a shell ```bash theme={null} cpln workload cron run --interactive --shell sh -- cat /etc/os-release ``` ## Use a custom image ```bash theme={null} cpln workload cron run --image python:3.11 -- python --version ``` ## Common workflows The cron runner uses `ubuntu:latest` by default, which won't have your application tools (Node.js, Python, etc.). Use `--image` to specify an image that has the tools your command needs. ### Run database migrations ```bash theme={null} cpln workload cron run \ --image node:20 \ -- npm run db:migrate ``` ### Run data processing in the background ```bash theme={null} cpln workload cron run \ --image python:3.11 \ --background \ --cpu 500m \ --memory 1Gi \ -- python /scripts/process-data.py ``` ### Pass environment variables ```bash theme={null} cpln workload cron run \ --image node:20 \ --env APP_MODE=maintenance \ --env LOG_LEVEL=debug \ -- node maintenance-script.js ``` ### Run in a specific location ```bash theme={null} cpln workload cron run --location aws-eu-central-1 -- curl -s https://api.example.com ``` The specified location must be enabled in your GVC. You can check your GVC's locations with `cpln gvc get `. ### Add tags for tracking ```bash theme={null} cpln workload cron run \ --image node:20 \ --tag job=nightly-backup \ --tag env=prod \ -- node backup.js ``` ## Assign an identity Use `--identity` to assign a specific identity to the cron runner workload. If a runner with that identity already exists, it will be reused instead of creating a new one: ```bash theme={null} cpln workload cron run \ --identity my-service-identity \ --image node:20 \ -- node scripts/access-cloud-resources.js ``` This is useful when your command needs to access cloud resources or secrets that require a specific identity's permissions. ## Resource configuration Override CPU and memory for resource-intensive tasks: ```bash theme={null} cpln workload cron run \ --image python:3.11 \ --cpu 200m \ --memory 512Mi \ -- python heavy-computation.py ``` ## Troubleshooting The default timeout is 600 seconds. For long-running tasks, specify an image with the tools you need and increase the timeout: ```bash theme={null} cpln workload cron run --image python:3.11 --timeout 1800 -- python long-running-task.py ``` Alternatively, use `--background` for tasks that don't need to wait for completion. The default image may not have your tool. Use a custom image: ```bash theme={null} cpln workload cron run --image node:20 -- npm --version ``` Specify a different shell for interactive sessions: ```bash theme={null} cpln workload cron run --interactive --shell sh -- echo "done" ``` ## Next steps Create temporary standard workloads for one-off tasks Run commands in existing workloads Interactive shell in running workloads Full cron run command reference # Execute Commands in Workloads Source: https://docs.controlplane.com/guides/cli/workload/exec Run commands inside workload containers without opening an interactive session. The `cpln workload exec` command runs commands inside a container and returns the output. Unlike `connect`, it's designed for non-interactive commands and scripts, similar to `docker exec` or `kubectl exec`. ## When to use this Execute maintenance scripts, health checks, or one-off commands Run commands from CI/CD pipelines or monitoring systems Check environment variables, list files, or test connectivity Run diagnostic commands and capture output ## Prerequisites Install the Control Plane CLI. See [Installation](/cli-reference/installation). You need a running [workload](/reference/workload) in at least one location. See the [Create a Workload](/guides/create-workload) guide. You need `exec` permission on the workload. See [Workload Permissions](/reference/workload#permissions). ## Basic usage ```bash theme={null} cpln workload exec -- [args...] ``` The `--` separates the CLI options from the command to execute. ## Options | Option | Description | | ------------- | --------------------------------------------------------- | | `--location` | Target location (defaults to first available in GVC) | | `--replica` | Target a specific replica (defaults to first replica) | | `--container` | Target a specific container (defaults to first container) | | `-i, --stdin` | Pass stdin to the container | | `-t, --tty` | Allocate a TTY | | `-q, --quiet` | Only print output from the remote session | ## Run a simple command ```bash theme={null} cpln workload exec my-app -- echo "Hello, World!" ``` Output: ```text theme={null} Hello, World! ``` ## Run commands with arguments ```bash theme={null} cpln workload exec my-app -- ls -la ``` ## Target specific locations or replicas ```bash theme={null} cpln workload exec my-app --location aws-eu-central-1 -- cat /etc/hostname ``` ```bash theme={null} cpln workload exec my-app --replica my-app-7f8d9c-abc12 -- cat /etc/hostname ``` ```bash theme={null} cpln workload exec my-app --container sidecar -- ps aux ``` ## Interactive mode Combine `-i` (stdin) and `-t` (TTY) for interactive commands: ```bash theme={null} cpln workload exec my-app -it -- /bin/bash ``` For interactive sessions, consider using `cpln workload connect` which provides a simpler experience. ## Pipe input to commands Use `-i` to pipe data into a container: ```bash theme={null} # Pipe a SQL file into psql cat schema.sql | cpln workload exec postgres-db -i -- psql -U postgres # Pipe JSON data to a script echo '{"key": "value"}' | cpln workload exec my-app -i -- python process.py ``` ## Quiet mode Suppress CLI output and show only command output: ```bash theme={null} cpln workload exec my-app -q -- cat /app/version.txt ``` Useful for capturing output in scripts: ```bash theme={null} VERSION=$(cpln workload exec my-app -q -- cat /app/version.txt) echo "Deployed version: $VERSION" ``` ## Common workflows ### Check environment variables ```bash theme={null} cpln workload exec my-app -- env | grep -E "^(DATABASE|API)" ``` ### View logs ```bash theme={null} cpln workload exec my-app -- tail -100 /var/log/app.log ``` ### Check disk usage ```bash theme={null} cpln workload exec my-app -- df -h ``` ### Test network connectivity ```bash theme={null} cpln workload exec my-app -- curl -s http://api-service:8080/health ``` ### Run database queries ```bash theme={null} cpln workload exec postgres-db -- psql -U postgres -c "SELECT count(*) FROM users;" ``` ## Troubleshooting The command may not exist in the container. Check what's available: ```bash theme={null} cpln workload exec my-app -- which bash cpln workload exec my-app -- ls /bin /usr/bin ``` The command may require elevated permissions, or you lack `exec` permission on the workload. The workload may not be running: ```bash theme={null} cpln workload get my-app cpln workload replica get my-app ``` Ensure `--` separates CLI options from the command: ```bash theme={null} # Correct cpln workload exec my-app -- ls -la # Incorrect - CLI will try to parse -la cpln workload exec my-app ls -la ``` ## Next steps Interactive shell access Transfer files to/from workloads Access workload ports locally Full exec command reference # Run One-off Workloads Source: https://docs.controlplane.com/guides/cli/workload/run Create temporary workloads to run commands, scripts, or debugging sessions. The `cpln workload run` command creates a temporary workload, waits for it to become healthy, executes your command, and optionally cleans up. It's similar to `docker run` - perfect for one-off tasks, debugging, and running scripts in your cloud environment. **Recommendation:** Consider using [`cpln workload cron run`](/guides/cli/workload/cron-run) instead — it's significantly faster and the preferred way to run commands going forward. It uses a persistent cron workload runner that is reused across executions, eliminating the overhead of creating and tearing down a workload each time. ## When to use this Execute database migrations in your production environment Run maintenance scripts, data processing, or cleanup tasks Clone an existing workload to debug in an isolated environment Spin up temporary containers to test configurations ## Prerequisites Install the Control Plane CLI. See [Installation](/cli-reference/installation). You need `create` and `exec` permissions on workloads. See [Workload Permissions](/reference/workload#permissions). ## Basic usage ```bash theme={null} cpln workload run -- [args...] ``` By default, this: 1. Creates a temporary workload using `ubuntu:22.04` 2. Waits for it to become healthy 3. Executes the command 4. Exits (workload remains unless `--rm` is used) ## Options | Option | Description | | ------------------- | ------------------------------------------------------------- | | `--clone` | Clone an existing workload instead of using the default image | | `--image` | Use a custom image | | `--rm, --remove` | Delete the workload after the command completes | | `-i, --interactive` | Keep the session open after command execution | | `--container` | Target container (when cloning multi-container workloads) | | `--cpu` | CPU allocation (e.g., `50m`, `100m`) | | `--memory, --mem` | Memory allocation (e.g., `128Mi`, `256Mi`) | | `--shell, -s` | Shell to use for interactive mode (default: `bash`) | | `--location` | Target location | | `--tag` | Add tags to the temporary workload | | `-c, --command` | Override container command | | `-a, --arg` | Override container args | ## Run a simple command ```bash theme={null} cpln workload run -- echo "Hello from Control Plane!" ``` ## Use a custom image ```bash theme={null} cpln workload run --image python:3.11 -- python --version ``` ## Clone an existing workload Run commands in an environment identical to an existing workload: ```bash theme={null} cpln workload run --clone my-app -- npm run migrate ``` This clones `my-app` with all its configuration (env vars, secrets, identity) and runs the command. ### Target a specific container For multi-container workloads: ```bash theme={null} cpln workload run --clone my-app --container worker -- python process.py ``` ## Automatic cleanup Delete the temporary workload after the command completes: ```bash theme={null} cpln workload run --rm -- echo "Hello from Control Plane!" ``` Use `--rm` for one-off tasks to avoid accumulating temporary workloads. ## Interactive mode Stay in the container after the command runs: ```bash theme={null} cpln workload run --interactive -- ls -la ``` After the command executes, you'll have an interactive shell to continue working. ### Specify a shell ```bash theme={null} cpln workload run --interactive --shell sh -- cat /etc/os-release ``` ## Common workflows ### Run database migrations ```bash theme={null} cpln workload run --clone my-app --rm -- npm run db:migrate ``` ### Debug in a cloned environment ```bash theme={null} cpln workload run --clone production-api --interactive -- bash ``` ### Run data processing ```bash theme={null} cpln workload run \ --image python:3.11 \ --cpu 500m \ --memory 1Gi \ --rm \ -- python /scripts/process-data.py ``` ### Test in a specific location ```bash theme={null} cpln workload run --location aws-eu-central-1 -- curl -s https://api.example.com ``` ### Add tags for tracking ```bash theme={null} cpln workload run --tag job=nightly-backup --tag env=prod --rm -- ./backup.sh ``` ## Resource configuration Override CPU and memory for resource-intensive tasks: ```bash theme={null} cpln workload run \ --cpu 200m \ --memory 512Mi \ -- ./heavy-computation.sh ``` ## Override container command Replace the container's default command: ```bash theme={null} cpln workload run \ --image myapp:latest \ --command sleep \ --arg "infinity" \ --interactive \ -- bash ``` This starts the container with `sleep infinity` instead of its default command, then opens a bash session. ## Troubleshooting Run with `--verbose` to see what the platform reports about the deployment while the command waits: ```bash theme={null} cpln workload run --verbose -- echo hello ``` Each time the reported status changes, it is written to stderr — naming the containers that are not ready yet and any message the platform attached, such as a deployment paused because it cannot reveal a referenced secret, or an image that cannot be pulled. If the status shows nothing unusual, the location may not be available in your GVC: ```bash theme={null} cpln gvc get my-gvc ``` Verify the location is in the GVC's `staticPlacement.locationLinks`. The default Ubuntu image may not have your tool. Use a custom image: ```bash theme={null} cpln workload run --image node:20 -- npm --version ``` Specify a different shell: ```bash theme={null} cpln workload run --interactive --shell sh -- echo "done" ``` The workload may take longer to start. Check the GVC locations and image availability. The error names the last status the platform reported, which usually identifies the container that never became ready. Run with `--verbose` to follow that status from the start of the wait. ## Next steps Run commands in existing workloads Interactive shell in running workloads Create persistent workloads Full run command reference # Deploy Docker Compose Projects Source: https://docs.controlplane.com/guides/compose-deploy Convert and deploy Docker Compose projects to Control Plane with the cpln stack command. The `cpln stack` command deploys Docker Compose projects directly to Control Plane, automatically converting services, volumes, secrets, and networks to their Control Plane equivalents. ## When to use this Move existing Compose projects to Control Plane without rewriting configuration Develop locally with Compose, deploy to Control Plane for production Deploy interconnected services as a cohesive stack Generate and inspect Control Plane manifests before deploying ## Prerequisites Install the Control Plane CLI. See [Installation](/cli-reference/installation). You need a `docker-compose.yml` or `compose.yaml` file. You need permissions to create workloads, secrets, volumesets, identities, and push images. ## Deploy a project ```bash theme={null} cpln stack deploy ``` This reads your Compose file and deploys all services to Control Plane. See the [stack deploy](/cli-reference/commands/stack#stack-deploy) command reference for all options. ## Delete a project Remove all resources created by a Compose deployment: ```bash theme={null} cpln stack rm ``` See the [stack rm](/cli-reference/commands/stack#stack-rm) command reference for details. ## Preview the generated manifests Generate Control Plane specs without deploying: ```bash theme={null} cpln stack manifest ``` This outputs the converted YAML so you can inspect or modify it before deployment. ## Customize workloads with x-cpln Add an `x-cpln` block to any service to override the generated workload spec. Each top-level key in `x-cpln` **replaces** the corresponding section in the workload spec: ```yaml docker-compose.yml theme={null} services: api: image: ghcr.io/example/api:1.2.3 ports: - '8080:8080' x-cpln: type: standard # Override workload type containers: # Replace entire containers array - name: api cpu: 250m memory: 256Mi defaultOptions: # Replace defaultOptions capacityAI: false autoscaling: minScale: 0 maxScale: 3 metric: concurrency target: 50 firewallConfig: # Replace firewallConfig external: inboundAllowCIDR: - 0.0.0.0/0 internal: inboundAllowType: same-gvc ``` The `x-cpln` block **replaces** entire spec sections, and doesn't merge them. If you override `containers`, you must include all container configuration. ### Available overrides | Key | Description | | -------------------- | --------------------------------------------------- | | `type` | Workload type: `serverless`, `standard`, `stateful` | | `containers` | Complete container specifications | | `defaultOptions` | Autoscaling, capacity AI, timeouts, suspend | | `firewallConfig` | External and internal firewall rules | | `identityLink` | Link to a specific identity | | `supportDynamicTags` | Enable dynamic image tag detection | | `loadBalancer` | Load balancer configuration | | `rolloutOptions` | Deployment rollout strategy | | `securityOptions` | Security context settings | | `localOptions` | Location-specific overrides | ## Service-to-service communication Update service URLs to use the Control Plane local syntax: ```diff theme={null} - http://service2:8080 + http://service2.{GVC}.cpln.local:8080 ``` Replace `{GVC}` with your actual GVC name. See the [Service-to-Service](/guides/service-to-service#service-endpoint-syntax) guide for the full endpoint syntax. ## How workload type is determined The converter analyzes your service definition to select the appropriate workload type: | Condition | Workload Type | | ---------------------------- | ------------- | | Service has volumes attached | `stateful` | | No volumes attached | `standard` | Use `x-cpln` to override the automatically determined type if needed. ## Translation reference | Compose Feature | Control Plane Resource | | ---------------- | --------------------------------------------------------------------------- | | Services | Single-container workloads | | Networks | Internal firewall configuration | | Named volumes | Volumesets (10GB, ext4, general-purpose-ssd) | | Secrets | [Opaque secrets](/reference/secret#opaque) + policies + identities | | Configs | [Opaque secrets](/reference/secret#opaque) (treated identically to secrets) | | File bind mounts | [Opaque secrets](/reference/secret#opaque) | ### Resource mapping | Compose Field | Control Plane Equivalent | | -------------------------------- | ----------------------------------------------------------------------------------------------------------------- | | `deploy.resources.limits.cpus` | Container CPU (multiplied by 1000 for millicores) | | `deploy.resources.limits.memory` | Container memory | | `deploy.replicas` | `minScale` and `maxScale` (set to same value) | | `healthcheck` | Readiness probe | | `ports` / `expose` | Container ports | | `environment` / `env_file` | Environment variables (`environment` processed first; `env_file` entries added only for keys not already defined) | | `working_dir` | Container working directory | | `command` | Container args | | `entrypoint` | Container command | ### Port protocol Specify the protocol directly in the port string: ```yaml theme={null} ports: - "8080:80/http" # HTTP protocol - "50051:50051/grpc" # gRPC protocol - "9000:9000/http2" # HTTP/2 protocol - "5432:5432/tcp" # TCP protocol - "3000:3000" # No protocol set if not specified ``` ### Defaults | Property | Default Value | | ----------------- | ----------------------------------------------------------------- | | CPU | `42m` | | Memory | `128Mi` | | External inbound | Allowed if `ports` defined or `network_mode: host` | | External outbound | Allowed (unless `network_mode: none`) | | Capacity AI | Enabled only if: reservations \< limits, no GPU, and not stateful | ### Secrets and configs Both `secrets` and `configs` are converted to Control Plane secrets: ```yaml theme={null} services: api: image: myapp:latest secrets: - db_password # Simple reference - source: api_key # With custom target path target: /app/api.key secrets: db_password: file: ./secrets/db_password.txt api_key: file: ./secrets/api_key.txt ``` * Default mount path: `/run/secrets/{name}` (if no absolute path specified) * Identities and policies are automatically created for workloads using secrets ### Healthcheck conversion Docker healthchecks are converted to readiness probes: | Compose Field | Control Plane Field | | -------------- | --------------------- | | `test` | `exec.command` | | `interval` | `periodSeconds` | | `timeout` | `timeoutSeconds` | | `start_period` | `initialDelaySeconds` | | `retries` | `failureThreshold` | Supported test formats: * String: `"curl http://localhost/health"` → `/bin/sh -c` wrapper * CMD: `['CMD', 'curl', 'http://localhost']` → direct execution * CMD-SHELL: `['CMD-SHELL', 'curl http://localhost']` → `/bin/sh -c` wrapper * NONE: Disables the readiness probe ### Network modes | Mode | Behavior | | ----------------------------- | ------------------------------------------------- | | Default (no networks) | All services can reach each other | | Named networks | Only services in the same network can communicate | | `network_mode: host` | External inbound traffic allowed | | `network_mode: none` | No outbound traffic allowed | | `network_mode: service:other` | Shares network with another service | ### GPU support Services with GPU devices are automatically configured: ```yaml theme={null} deploy: resources: reservations: devices: - capabilities: ['gpu'] count: 1 ``` GPU workloads receive: * NVIDIA T4 GPU * Minimum CPU: `2000m` (overrides default) * Minimum Memory: `7168Mi` (overrides default) * Capacity AI: Disabled ### Service inheritance Extend services from the same or different compose files: ```yaml theme={null} services: api: extends: service: base file: base-compose.yml environment: - API_KEY=secret # Overrides parent ``` Child values take precedence over parent values. ## Limitations The following Docker Compose features are **not supported**: | Feature | Status | Alternative | | ------------------------------- | ------------- | ----------------------------------------------- | | Directory bind mounts | Not supported | Use named volumes or file bind mounts | | `depends_on` ordering | Ignored | Services start independently; use health checks | | `links` | Ignored | Use network-based service discovery | | Dynamic GPU model/quantity | Limited | Hardcoded to NVIDIA T4, quantity 1 | | Multiple containers per service | Not supported | Create separate services | | `privileged` mode | Not supported | Use `securityOptions` via x-cpln | ## Troubleshooting Update hostnames to use the Control Plane local syntax: ``` http://service-name.{GVC}.cpln.local:{port} ``` Ensure both services are in the same network or no networks are defined (global network). Ensure all ports are explicitly listed in `ports` or `expose` in your Compose file. Only services with ports defined get external inbound access. Secrets referenced in Compose are converted to Control Plane secrets. The converter automatically creates identities and policies. Check that: * The secret file exists at the specified path * You have permissions to create secrets, identities, and policies Directory bind mounts are not supported. Convert to either: * Named volumes (for persistent data) * File bind mounts (for configuration files → converted to secrets) A service cannot have both `image` and `build` specified. Use one or the other: * `image`: Pull from a registry * `build`: Build and push to Control Plane registry By default, all services can reach each other. If you're using named networks, ensure communicating services are in the same network. ## Next steps Deploy resources from YAML files Configure internal networking Understand workload configuration Full stack command reference # Configure a CDN Source: https://docs.controlplane.com/guides/configure-cdn Set up a Content Delivery Network (CDN) such as Cloudflare or Amazon CloudFront to protect and accelerate your workloads on Control Plane. A Content Delivery Network (CDN) such as [Cloudflare](https://cloudflare.com) can protect and accelerate your [workload](/reference/workload) running on Control Plane. The configuration of various CDN providers is similar in concept. In this guide, you will find the steps on how to configure a CDN with [Cloudflare](#cloudflare-configuration-steps) and [Amazon CloudFront](#amazon-cloudfront-configuration-steps). Configure your domain’s CNAME to target either a `GVC endpoint` or a `Workload endpoint`. A [GVC](/reference/gvc) endpoint routes traffic across all domains mapped to the same GVC and is useful when a single CDN route is configured for many domains or wildcard subdomains. Geo-routing still applies, but traffic will be sent to all locations even if a specific workload in one location is unavailable. A workload endpoint routes traffic to a specific workload within a GVC and provides more precise geo-routing and failover for that workload. If a workload in one location is down, traffic will fail over to other locations for just that workload. ## Cloudflare Configuration Steps ### Prerequisites * Review the [Configure a Domain](/guides/configure-domain) guide. * An account at Cloudflare. * Your domain's DNS is hosted at Cloudflare. * Your workload is configured and in a `Ready` state. ### Step One - Domain Set Up and Certificate Generation at Cloudflare **From the Cloudflare UI, perform the following:** #### Domain Set Up * From the DNS management page for your domain, click `DNS Records` in the top right and add a new `CNAME` record. * For the `Name` field, enter the desired target subdomain. * For the `Target` field, enter the `Canonical Endpoint` URL from the [workload](/reference/workload) Info page. * Toggle on the `Proxied` switch. #### Certificate Generation Set Up * From the SSL/TLS page, select the `Full (strict)` radio box. * Click on the `Origin Server` submenu link. * Create a new origin certificate for your domain with the following settings. This certificate will be added as a [TLS Secret](/reference/secret#tls) at Control Plane. * Select `Generate private key and CSR with Cloudflare`. * Select the private key type: `RSA (2048)`. * The default list of hostnames shouldn't have to be changed and will contain the `*.DOMAIN` and `DOMAIN` hostnames. * Choose a long Certificate Validity such as 15 years. * **NOTE: It is your responsibility to ensure the certificate mapped to your domain at Control Plane is valid.** * Click `Create`. The next page will display the certificate and private key. You may save these as separate text files or leave the page open and copy/paste the values when creating the [TLS Secret](/reference/secret#tls) at Control Plane in [Step Two](#step-two---certificate-set-up-at-control-plane). ### Step Two - Certificate Set Up at Control Plane * Using the certificate and private key generated in [Step One - Certificate Generation Set Up](#certificate-generation-set-up), create a new [TLS Secret](/reference/secret#tls) at Control Plane by performing the following: * Click `Secrets` from the left side menu. * Click the `New` button at the top. * Enter a `Name` for the secret and select the secret type `TLS`. * Click `Data` in the left pane. Either upload or paste the respective certificate and private key file in the proper textbox. The TLS Chain can be left empty since this certificate is self-signed. * Click `Create`. This secret will be used when configuring your domain in the next step. ### Step Three - Domain Set Up at Control Plane Follow the steps below to configure your domain at Control Plane. **Note: If a subdomain is being configured, the APEX domain will need to be [verified](/guides/configure-domain#step-one-apex-domain-verification).** 1. Click `Domains` from the left side menu. 2. Click the `New` button at the top. 3. Click `Advanced` in the left pane. Enter the `Fully Qualified Domain Name (FQDN)` of your domain. You will need to prove ownership of the domain before continuing. 4. Select `CNAME` for the `DNS Mode`. 5. Select and configure the desired `Routing Mode`. 6. Click the `Add +` tab, and toggle `Use Custom Server Certificate`. Set the `Server Certificate Authority PEM` to the [TLS Secret](/reference/secret#tls) created in the previous step, then click `Create`. After completing Steps One through Three, it will take a few minutes for the updates to propagate throughout the Internet. Once fully configured, your workload will be accessible via the CDN using the subdomain configured in [Step One - Domain Set Up](#domain-set-up). ## Amazon CloudFront Configuration Steps ### Prerequisites * An AWS account. * Access to edit DNS settings for your domain. * Your workload is configured and in a `Ready` state. ### Step One - Request a public certificate Request a public certificate with [AWS Certificate Manager (ACM)](https://us-east-1.console.aws.amazon.com/acm/home?region=us-east-1#/certificates/request) in `N. Virginia` region using the settings below. 1. A public TLS certificate is required for your domain. Use the following settings: * Domain Names: `subdomain.mydomain.com` or `*.mydomain.com` * Validation Method: `DNS Validation` * Key Algorithm: `RSA 2048` The certificate must be in the US East (N. Virginia) Region (us-east-1). 2. Access the newly created certificate on the [ACM](https://us-east-1.console.aws.amazon.com/acm/home?region=us-east-1#/certificates/list). Validate the certificate by creating the records in your DNS service as described. AWS ACM certificate validation page showing CNAME records required for domain verification ### Step Two - Create CloudFront distribution 1. Go to [CloudFront distributions page](https://us-east-1.console.aws.amazon.com/cloudfront/v4/home#/distributions) and click on `Create Distribution`. 2. Configure the `Origin Domain` to the public endpoint of your workload. Use one of the following methods, depending on whether you are using a [**BYOK location**](/byok/overview) or a **managed location (standard)**: * For **managed locations (standard)** only: Use the `Canonical Endpoint` URL from the [Workload Info page](/reference/workload) as `Origin Domain`, formatted as follows: `cloudfront-httpbin-0ac6x9wrgpj00.cpln.app`. Workload Info page showing the Canonical Endpoint URL to use as CloudFront origin domain * For [BYOK locations](/byok/overview) only: Locate the public endpoint on your Workload *Deployments* page. Use this address as the `Origin Domain` value in CloudFront, formatted as follows: `nginx3-7mhf5d3qcsrqt.eksctl-byok-aws-west2.controlplane.us`. Workload Deployments page showing the public endpoint URL for BYOK locations 3. Edit the `Alternate domain name` for your domain. In the format: `subdomain.mydomain.com`.\ Then select the `Custom SSL certificate` created in [Step One - Request a public certificate](#step-one---request-a-public-certificate) from the list. CloudFront settings showing alternate domain name and custom SSL certificate selection fields 4. You must select `Cache policy` and complete the rest of the configuration as needed. 5. Click on `Create distribution` and wait for a few minutes for the changes to apply. By now, you should have a CloudFront distribution ready. CloudFront distributions list showing the newly created distribution with its domain name and status ### Step Three - Configure DNS Create a CNAME record in your DNS service (such as Route53) that will match the *Alternate domain name* in the CloudFront distribution created in [Step Two - Create CloudFront distribution](#step-two---create-cloudfront-distribution): Use values that match your CloudFront distribution. For example: * Type: CNAME * Name: `your-subdomain` * Data: `your-distribution.cloudfront.net` ### Step Four - Configure firewall rules to restrict access via CloudFront * To prevent direct access to the workload endpoint, configure the firewall settings for the workload to allow ingress for CloudFront [list of IP ranges](https://d7uri8nf7uskq.cloudfront.net/tools/list-cloudfront-ips). You can refer to [this example workload YAML file](https://cpln-public-bucket.s3.amazonaws.com/nginx3-workload-cloudfront-example.yaml) and copy the CIDR range directly from this manifest. * **BYOK Only**: If you have created inbound rules on the Security Group of the Load Balancers, either directly or using the Actuator configuration [INGRESS\_FIREWALL\_CIDR\_LIST](/byok/settings/actuator), you will need to update the Security Group configuration with CloudFront CIDR ranges to enable CloudFront access to the workloads. **Important**: To support this setting, ensure that your quota for `Inbound or outbound rules per security group` under `Amazon Virtual Private Cloud (Amazon VPC)` values for at least **530** rules. Visit [Service Quotas](https://console.aws.amazon.com/servicequotas/home/dashboard) in the AWS console for your region to request a quota increase if necessary. # Configure a Domain Source: https://docs.controlplane.com/guides/configure-domain Configure custom domains for workloads with DNS verification, routing modes, TLS certificates, and path-based routing via Console UI and CLI manifests. ## Domain Configuration Overview Follow the steps below to configure a custom [domain](/reference/domain) within your [org](/reference/org). ## Domain Configuration Prerequisites * Review the [domain](/reference/domain) reference page. * Have [permissions](/reference/policy#permissions) to configure a [domain](/reference/domain). * Access to a domain name and the ability to update its DNS records. * Optional: * Install the [CLI](/cli-reference/installation). ## Step One - APEX Domain Verification Follow Step One in the [domain quick start](/quickstart/quick-start-2-custom-domain#step-one-register-your-apex-domain) to create and verify the apex domain name using the Console UI. The CLI can also be used to create the apex domain for verification and routing configuration. See the [sample manifest files](#apex-verification-only) below. Refer to [Domain Verification](/reference/domain#domain-verification) for additional details. ## Step Two - Create Domain A [domain](/reference/domain) can be created using the following methods: * [Console UI](#create-using-the-ui-console) * [CLI](#create-using-the-cli) * [Terraform Provider](https://registry.terraform.io/providers/controlplane-com/cpln/latest/docs/resources/domain) ### Create using the UI Console Follow Step Two in the [domain quick start](/quickstart/quick-start-2-custom-domain#step-two-review-routing-modes) for detailed instructions on how to create and configure your domain name using the console UI. ### Create using the CLI A domain can be created or updated using the [cpln apply](/guides/cpln-apply) command. Below are a few sample domain manifests (in YAML) that can be used as input to the [cpln apply](/guides/cpln-apply) command. After updating and saving the manifests to a local file, execute the following command to apply: ```bash theme={null} cpln apply -f FILE_NAME.yaml --org ORG_NAME ``` Before using the [CLI's apply](/guides/cpln-apply) command, the [TXT DNS records](#required-dns-records) must exist and be propagated **only** for the apex domain. The DNS entries for the subdomain will not be verified on creation. Routing and certificate generation will not occur until the records have been added, propagated, and the target workload is in a ready state and can serve requests. #### APEX Verification Only Configure a domain for APEX verification * Substitute `example.com` with your domain. ```yaml YAML theme={null} kind: domain name: example.com description: example.com spec: dnsMode: cname ``` #### APEX Verification and Path Based Routing Configure a domain for APEX verification and path-based routing. * Substitute `example.com` with your domain, `GVC_NAME`, and `WORKLOAD_NAME`. * The example YAML manifest below assumes there is a workload named `WORKLOAD_NAME` serving requests on port 4200 in the [GVC](/reference/gvc) named `GVC_NAME`. ```yaml YAML theme={null} kind: domain name: example.com description: example.com tags: {} spec: acceptAllHosts: false dnsMode: cname ports: - number: 443 protocol: http2 routes: - port: 4200 prefix: / workloadLink: //gvc/GVC_NAME/workload/WORKLOAD_NAME ``` #### APEX Verification and Subdomain Based Routing Configure an apex domain for subdomain-based routing, where each workload in the GVC is served at `.example.com`. * Substitute `example.com` with your domain and `GVC_NAME`. * `dnsMode` is `cname` and `gvcLink` selects the GVC. * With `certChallengeType: dns01`, Control Plane issues a wildcard certificate — no TLS secret is required. ```yaml YAML theme={null} kind: domain name: example.com description: example.com tags: {} spec: dnsMode: cname certChallengeType: dns01 gvcLink: //gvc/GVC_NAME ``` After the domain is created, view its `status.dnsConfig` (for example, `cpln domain get example.com -o yaml`) and create the records it lists: one `CNAME` per workload (`.example.com` → `.cpln.app`) plus the `_acme-challenge` CNAME used to issue the certificate. To supply your own certificate instead, use `certChallengeType: http01` and configure a `tls.serverCertificate.secretLink` on each TLS port: ```yaml YAML theme={null} kind: domain name: example.com description: example.com tags: {} spec: dnsMode: cname certChallengeType: http01 gvcLink: //gvc/GVC_NAME ports: - number: 443 tls: serverCertificate: secretLink: //secret/TLS_SECRET ``` #### Subdomain and Path Based Routing Configure a subdomain for path-based routing. * Substitute `sub.example.com` with your domain, `GVC_NAME`, and `WORKLOAD_NAME`. * The example YAML manifest below assumes there is a workload named `WORKLOAD_NAME` in the GVC named `GVC_NAME`. ```yaml YAML theme={null} kind: domain name: sub.example.com description: sub.example.com tags: {} spec: dnsMode: cname ports: - routes: - prefix: / workloadLink: //gvc/GVC_NAME/workload/WORKLOAD_NAME ``` #### Subdomain and Subdomain Based Routing Configure a subdomain for subdomain-based routing. * Substitute `sub.example.com` with your domain and `GVC_NAME`. * All workloads within the GVC will have endpoints created with the subdomain in the format: `https://WORKLOAD_NAME.sub.example.com/`. ```yaml YAML theme={null} kind: domain name: sub.example.com description: sub.example.com tags: {} spec: dnsMode: ns gvcLink: //gvc/GVC_NAME ``` #### Setting Custom Headers Set custom headers on any domain route. * Substitute `sub.example.com` with your domain, `GVC_NAME`, and `WORKLOAD_NAME`. * Substitute `HEADER` with your header name and `VALUE` with your header value. You can add additional headers. * The example YAML manifest below assumes there is a workload named `WORKLOAD_NAME` in the GVC named `GVC_NAME`. ```yaml YAML theme={null} kind: domain name: sub.example.com description: sub.example.com tags: {} spec: dnsMode: cname ports: - routes: - prefix: / workloadLink: //gvc/GVC_NAME/workload/WORKLOAD_NAME headers: request: set: HEADER: VALUE ``` #### Complete Domain YAML Manifest Use the manifest YAML templates below to create a domain per your requirements. The examples above use the minimum properties required to create or update a domain based on your requirements. The complete YAML manifest templates below contain both the required and optional properties. **Notes:** * `dnsMode` can either be `cname` (for path-based) or `ns` (for subdomain-based). For an apex domain, it must be `cname`. * `acceptAllHosts` will only be active if the dedicated load balancer is enabled on the GVC. * For the routes configured for path-based routing, the `hostPrefix` will only be active if the dedicated load balancer is enabled on the GVC **and** the `acceptAllHosts` property is set to `true`. * Path-based Routing ```yaml YAML theme={null} kind: domain name: sub.domain.com description: sub.domain.com tags: {} spec: acceptAllHosts: false dnsMode: cname ports: - cors: allowCredentials: true allowHeaders: - '*' allowMethods: - '*' allowOrigins: - exact: '*' exposeHeaders: - '*' maxAge: 24h number: 443 protocol: http2 routes: - hostPrefix: example port: 4200 prefix: /example replacePrefix: /v1/example workloadLink: //gvc/GVC_NAME/workload/WORKLOAD_NAME_01 headers: request: set: EXAMPLE_HEADER: EXAMPLE_VALUE - regex: /user/.*/profile workloadLink: //gvc/GVC_NAME/workload/WORKLOAD_NAME_02 - prefix: / workloadLink: //gvc/GVC_NAME/workload/WORKLOAD_NAME_03 tls: cipherSuites: - ECDHE-ECDSA-AES256-GCM-SHA384 - ECDHE-ECDSA-CHACHA20-POLY1305 - ECDHE-ECDSA-AES128-GCM-SHA256 - ECDHE-RSA-AES256-GCM-SHA384 - ECDHE-RSA-CHACHA20-POLY1305 - ECDHE-RSA-AES128-GCM-SHA256 - AES256-GCM-SHA384 - AES128-GCM-SHA256 clientCertificate: secretLink: //secret/TLS_SECRET_CLIENT_CERTIFICATE minProtocolVersion: TLSV1_2 serverCertificate: secretLink: //secret/TLS_SECRET_SERVER_CERTIFICATE ``` * Subdomain Based Routing ```yaml YAML theme={null} kind: domain name: sub.example.com description: sub.example.com tags: {} spec: acceptAllHosts: true dnsMode: ns gvcLink: //gvc/GVC_NAME ports: - cors: allowCredentials: false allowHeaders: - '*' allowMethods: - '*' allowOrigins: - exact: '*' exposeHeaders: - '*' maxAge: 24h number: 443 protocol: http2 tls: cipherSuites: - ECDHE-ECDSA-AES256-GCM-SHA384 - ECDHE-ECDSA-CHACHA20-POLY1305 - ECDHE-ECDSA-AES128-GCM-SHA256 - ECDHE-RSA-AES256-GCM-SHA384 - ECDHE-RSA-CHACHA20-POLY1305 - ECDHE-RSA-AES128-GCM-SHA256 - AES256-GCM-SHA384 - AES128-GCM-SHA256 clientCertificate: secretLink: //secret/TLS_SECRET_CLIENT_CERTIFICATE minProtocolVersion: TLSV1_2 serverCertificate: secretLink: //secret/TLS_SECRET_SERVER_CERTIFICATE ``` ## Required DNS Records To successfully add a [domain](/reference/domain) to your [org](/reference/org), DNS records must be added. The values in the sample below might not correspond to the entries necessary for your domain. Use the values shown during domain creation. When the domain is created in the console UI, the final step will display the DNS records that need to be added. These records should be sent to the network administrator in charge of handling the domain's DNS configuration. **Examples:** | RECORD/HOST | TYPE | TTL | VALUE | | :---------- | :--- | :-- | :------------ | | \_cpln | TXT | 600 | ORG\_ID\_GUID | | RECORD/HOST | TYPE | TTL | VALUE | | :------------ | :---- | :-- | :------------------ | | \_cpln-sample | TXT | 600 | ORG\_ID\_GUID | | sample | CNAME | 600 | GVC\_ALIAS.cpln.app | **Note:** The GVC\_ALIAS can be obtained from the console UI by clicking on the GVC and it will be displayed on the `Info` page. Using the CLI, executing the command `cpln gvc get GVC_NAME` will output the alias. | RECORD/HOST | TYPE | TTL | VALUE | | :------------ | :--- | :--- | :------------- | | \_cpln-sample | TXT | 600 | ORG\_ID\_GUID | | sample | NS | 1800 | ns1.cpln.cloud | | sample | NS | 1800 | ns2.cpln.cloud | | sample | NS | 1800 | ns1.cpln.live | | sample | NS | 1800 | ns2.cpln.live | In the examples above, the TXT record for the subdomain is **only** required if the domain is created in an org that doesn't contain the apex domain. `ORG_ID_GUID` should be set to the `id` value of the org object. The record `_cpln-sample` is generated from the last segment of the domain, `_cpln-${lastSegment}`. In the case of an apex domain, the TXT record to add is `_cpln`. The TXT record can include the ID of multiple orgs in cases where the domain is allowed to be used in multiple orgs. The verification record can exist at any segment of the domain. For example, the subdomain `two.sample.domain.com` can be verified using **any** of the following TXT records: * \_cpln-two.sample.domain.com * \_cpln-sample.domain.com * \_cpln.domain.com To obtain the ORG\_ID\_GUID, run the CLI command: `cpln org get ORG_NAME --output json`. The output of the command will display all the properties of the [org](/reference/org) object. Use the value of the `id` key for the TXT value. After the DNS records have been created, the propagation time for the changes to take effect depends on the cache setting of your domain's DNS Start of Authority (SOA) record. Once the records are fully propagated, any DNS changes to your subdomain will be reflected within a few seconds. ## Configure a Content Delivery Network (CDN) Domain Refer to the [Configure a CDN](/guides/configure-cdn) guide. ## Advanced Mode When editing a domain within the console UI, the following options are available when clicking the `Advanced` button: ### Dedicated Load Balancing Refer to the following pages for details on enabling dedicated load balancing: * Configuring the [GVC for dedicated load balancing.](/reference/gvc#dedicated-load-balancer) * After enabling, these [additional domain configuration options](/reference/domain#dedicated-load-balancer-options) will be available for configuration. ### Domain Additional Settings * The [accept all hosts](/reference/domain#accept-all-hosts) or [accept all subdomains](/reference/domain#accept-all-subdomains) settings (`acceptAllHosts` in the above example manifest) can be enabled when dedicated load balancing is enabled. * [Add additional ports](/reference/domain#external-port) configured with other supported protocols (TCP, etc.) when using a dedicated load balancer. * For each port, configure [TLS Settings](/reference/domain#tls-settings) which includes setting the TLS version, forwarding client certificates, using custom server certificates, and allowed cipher suites. * For each port, configure custom [CORS](/reference/domain#cors-settings) settings. * When using Path Based Routing: * Additional [routes](/reference/domain#path-based-routing) can be added with unique prefixes and, optionally, a replace prefix. * When dedicated load balancing is enabled, a [host prefix](/reference/domain#host-prefix) (`hostPrefix` in the above example manifest) can be optionally defined. # Copy Images Between Organizations Source: https://docs.controlplane.com/guides/copy-image Copy container images from one Control Plane organization to another using the CLI. Copy container images between Control Plane organizations. The CLI handles authentication to both registries automatically, pulling, retagging, and pushing the image in one command. ## When to use this Share images between teams in different organizations Promote images from dev to staging to production orgs Migrate images when reorganizing or splitting organizations Copy images with new names or tags ## Prerequisites Install the Control Plane CLI. See [Installation](/cli-reference/installation). Install [Docker](https://www.docker.com). The CLI uses Docker to pull, tag, and push images. You need: * Pull permission on images in the source org * Push permission on images in the destination org See [Image Permissions](/reference/image#minimum-policy). If copying between orgs that use different credentials, create a profile for each org: ```bash theme={null} # Profile for source org and mark it as default cpln profile create source-profile --login --org source-org --default # Profile for destination org cpln profile create dest-profile --login --org dest-org ``` See [Profiles](/cli-reference/get-started/profiles) for details. ## Basic usage Copy an image to another organization: ```bash theme={null} cpln image copy my-app:v1 --to-org destination-org ``` This command: 1. Authenticates Docker to both organization registries 2. Pulls the image from your current org 3. Tags it for the destination org 4. Pushes it to the destination org ## Copy with a different name Use `--to-name` to rename the image during copy: ```bash theme={null} cpln image copy my-app:v1 --to-org destination-org --to-name renamed-app:v1 ``` This copies `my-app:v1` to `destination-org` as `renamed-app:v1`. ## Copy between orgs with different credentials When organizations use different authentication contexts, specify a profile for the destination org: ```bash theme={null} cpln image copy my-app:v1 \ --to-org destination-org \ --to-profile dest-profile ``` Your default profile (or `CPLN_PROFILE`) is used for the source org. The `--to-profile` flag specifies credentials for the destination. ## Clean up local images By default, the CLI leaves the pulled and retagged images on your local machine. Use `--cleanup` to remove them after a successful copy: ```bash theme={null} cpln image copy my-app:v1 --to-org destination-org --cleanup ``` This is useful in CI/CD pipelines to avoid filling up disk space. ## Command reference | Option | Description | | -------------- | ------------------------------------------------------------ | | `` | Source image reference (e.g., `my-app:v1`) | | `--to-org` | Target organization to copy the image to | | `--to-name` | New name and tag for the image (optional) | | `--to-profile` | Profile for authenticating to the destination org (optional) | | `--cleanup` | Remove local images after successful copy (default: false) | ## Common workflows ### Environment promotion Promote an image from development to production: ```bash theme={null} # Using default profile for dev org cpln image copy my-app:v1.2.3 \ --to-org my-org-production \ --to-profile production-profile ``` ### CI/CD pipeline Copy and clean up in automated pipelines: ```bash theme={null} # Copy with cleanup to save disk space cpln image copy my-app:$CI_COMMIT_SHA \ --to-org production-org \ --to-profile prod-profile \ --cleanup ``` ## Troubleshooting Verify you're logged in to the source org: ```bash theme={null} cpln profile get ``` Re-authenticate if needed: ```bash theme={null} cpln login ``` Check the destination profile has valid credentials: ```bash theme={null} cpln profile get dest-profile ``` Re-authenticate the profile: ```bash theme={null} cpln login dest-profile ``` Verify you have push permission on images in the destination org. Check your policies or ask an org admin to grant access. Also check the org name for any typos. Verify the source image exists: ```bash theme={null} cpln image get my-app:v1 ``` Check the exact image name and tag. Start Docker Desktop or the Docker daemon. ## Next steps Build and push new images to your registry Configure workloads to pull images Manage multiple org contexts Image configuration details # Apply YAML Manifests Source: https://docs.controlplane.com/guides/cpln-apply Deploy and manage Control Plane resources using JSON or YAML files for GitOps, scripting, and automation. The `cpln apply` command creates or updates Control Plane resources from JSON or YAML files, enabling infrastructure-as-code workflows. ## When to use this Store resource definitions in Git and apply them in CI/CD pipelines Apply the same configuration across multiple environments Create or update multiple resources in a single command Automate resource management in scripts and pipelines The `cpln apply` command is idempotent - running it multiple times with the same input produces the same result. ## Basic usage ```bash theme={null} cpln apply --file ``` The file can be JSON or YAML containing one or more resource definitions. ## Specifying the GVC For resources that belong to a GVC ([Identity](/reference/identity), [Volume Set](/reference/volumeset), [Workload](/reference/workload)), specify the GVC using one of these methods: Set a default GVC in your profile: ```bash theme={null} cpln profile update --gvc ``` All subsequent commands will use this GVC. Pass the GVC as a flag (overrides profile default): ```bash theme={null} cpln apply --file --gvc ``` Include the GVC in the resource definition: ```yaml theme={null} kind: identity name: example-identity gvc: example-gvc ``` You can specify either a `gvc` property in the file or use the `--gvc` flag, but not both. ## Apply Kubernetes manifests Convert and apply Kubernetes resources directly: ```bash theme={null} cpln apply --file --k8s true ``` This uses the [convert](/guides/cli/cpln-convert) logic to transform K8s resources before applying. Conversion warnings (e.g., `envFrom` sources that could not be expanded) are printed to stderr. If a re-apply fails with `Workload type may not be changed` (405), the inferred workload type differs from the existing workload's immutable type. Pin the type in the K8s manifest with the [`cpln.io/workload-type` annotation](/guides/cli/cpln-convert#conversion-annotations). ## Waiting for readiness By default, `cpln apply` returns as soon as every resource is created or updated — it does **not** wait for workloads to roll out. Pass `--ready` to block until all applied workloads report ready (up to 5 minutes), which is useful in CI/CD pipelines that gate later steps on a healthy deployment: ```bash theme={null} cpln apply --file resources.yaml --ready ``` With `--ready`, workloads that cannot become healthy — for example, because a dependency is unreachable or an image cannot be pulled — cause the command to fail after the timeout. Apply firewall and configuration changes the workloads depend on before, or in the same file as, the workloads themselves. ## Apply from stdin Pipe resource definitions from another command: ```bash theme={null} cat resources.yaml | cpln apply --file - ``` This is useful for dynamically generating resources or chaining commands. ## Using the console The console also supports applying resources: 1. Click the **cpln apply** button in the upper right corner 2. Upload a JSON/YAML file or paste the resource definition 3. Select the target org and GVC 4. Click **Apply** ## Multiple resources Apply multiple resources in a single file by separating them with `---`: ```yaml theme={null} kind: gvc name: my-gvc spec: staticPlacement: locationLinks: - /org/my-org/location/aws-us-west-2 --- kind: workload name: my-app gvc: my-gvc spec: containers: - name: main image: nginx:latest ``` If a resource references another resource (e.g., a workload references a GVC), the referenced resource must be defined in the same file unless it already exists. ## Resource ordering The `cpln apply` command handles resource ordering automatically — you do not need to worry about the order of resources in your files or directories. The CLI resolves dependencies internally. ## Renaming resources Changing a resource's `name` in the file creates a new resource. The original resource remains and must be deleted manually: ```bash theme={null} cpln delete ``` ## Limitations To create an [agent](/reference/agent), use the console or CLI `agent` command to obtain the bootstrap config data. The `cpln apply` command does not output config data. Before creating a [cloud account](/reference/cloudaccount), additional configuration is required at the cloud provider. See the [Create Cloud Account](/guides/create-cloud-account) guide. Before creating a [domain](/reference/domain), the required DNS entries must exist. See the [Configure a Domain](/guides/configure-domain#required-dns-records) guide. ## Generate sample input Export existing resources as templates: 1. Select a resource and click **Actions** → **Export** 2. Choose **JSON Slim** or **YAML Slim** 3. Download the file ```bash theme={null} cpln gvc get -o yaml-slim > gvc.yaml cpln workload get --gvc -o yaml-slim > workload.yaml cpln secret reveal -o yaml-slim > secret.yaml ``` The `json-slim` and `yaml-slim` formats output only the values needed for `cpln apply`, removing IDs, timestamps, and other metadata. ## Use in GitOps The `apply` command integrates with CI/CD pipelines for GitOps workflows. See the [CI/CD Usage](/cli-reference/ci-cd-development/ci-cd) guides for details. ## Example templates Use these templates as starting points for your resource definitions. Download all examples: [cpln-apply-examples.zip](https://github.com/controlplane-com/examples/raw/main/examples/cpln-apply/cpln-apply-examples.zip) [GVC Reference Page](/reference/gvc) ```json JSON theme={null} { "kind": "gvc", "name": "example-gvc", "description": "example-gvc description", "tags": { "tag1": "value1" }, "spec": { "pullSecretLinks": ["//secret/SECRET_NAME"], "staticPlacement": { "locationLinks": [ "//location/aws-eu-central-1", "//location/aws-us-west-2", "//location/azure-eastus2", "//location/gcp-us-east1" ] } } } ``` ```yaml YAML theme={null} kind: gvc name: example-gvc description: example-gvc description tags: tag1: value1 spec: pullSecretLinks: - //secret/SECRET_NAME staticPlacement: locationLinks: - //location/aws-eu-central-1 - //location/aws-us-west-2 - //location/azure-eastus2 - //location/gcp-us-east1 ``` [Agent Reference Page](/reference/agent) ```json JSON theme={null} { "kind": "agent", "name": "example-agent", "description": "example-agent description" } ``` [Cloud Accounts Reference Page](/reference/cloudaccount) ```json JSON theme={null} { "kind": "cloudaccount", "name": "example-aws-cloud-account", "description": "example-aws-cloud-account description", "tags": {}, "provider": "aws", "data": { "roleArn": "ROLE_ARN" } } ``` ```yaml YAML theme={null} kind: cloudaccount name: example-aws-cloud-account description: example-aws-cloud-account description tags: {} provider: aws data: roleArn: 'ROLE_ARN' ``` ```json JSON theme={null} { "kind": "cloudaccount", "name": "example-azure-cloud-account", "description": "example-azure-cloud-account description", "tags": {}, "provider": "azure", "data": { "secretLink": "//secret/AZURE_SECRET" } } ``` ```yaml YAML theme={null} kind: cloudaccount name: example-azure-cloud-account description: example-azure-cloud-account description tags: {} provider: azure data: secretLink: //secret/AZURE_SECRET ``` ```json JSON theme={null} { "kind": "cloudaccount", "name": "example-gcp-cloud-account", "description": "example-gcp-cloud-account description", "tags": {}, "provider": "gcp", "data": { "projectId": "PROJECT_ID" } } ``` ```yaml YAML theme={null} kind: cloudaccount name: example-gcp-cloud-account description: example-gcp-cloud-account description tags: {} provider: gcp data: projectId: PROJECT_ID ``` ```json JSON theme={null} { "kind": "cloudaccount", "name": "example-ngs-cloud-account", "description": "example-ngs-cloud-account description", "provider": "ngs", "data": { "secretLink": "//secret/NATS_SECRET" } } ``` ```yaml YAML theme={null} kind: cloudaccount name: example-ngs-cloud-account description: example-ngs-cloud-account description tags: {} provider: ngs data: secretLink: //secret/NATS_SECRET ``` [Domain Reference Page](/reference/domain) ```json JSON theme={null} { "kind": "domain", "name": "sub.example.com", "description": "domain description", "tags": {} } ``` ```yaml YAML theme={null} kind: domain name: sub.example.com description: domain description tags: {} ``` [Secrets Reference Page](/reference/secret) ```json JSON theme={null} { "kind": "secret", "name": "example-aws-secret", "description": "example-aws-secret description", "tags": {}, "type": "aws", "data": { "accessKey": "AKIAIOSFODNN7EXAMPLE", "roleArn": "arn:awskey", "secretKey": "AKIAwJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", "externalId": "EXTERNAL_ID" } } ``` ```yaml YAML theme={null} kind: secret name: example-aws-secret description: example-aws-secret description tags: {} type: aws data: accessKey: AKIAIOSFODNN7EXAMPLE roleArn: 'arn:awskey' secretKey: AKIAwJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY externalId: EXTERNAL_ID ``` ```json JSON theme={null} { "kind": "secret", "name": "example-azure-connector-secret", "description": "example-azure-connector-secret description", "tags": {}, "type": "azure-connector", "data": { "code": "CODE", "url": "URL" } } ``` ```yaml YAML theme={null} kind: secret name: example-azure-connector-secret description: example-azure-connector-secret description tags: {} type: azure-connector data: code: CODE url: 'URL' ``` ```json JSON theme={null} { "kind": "secret", "name": "example-azure-sdk-secret", "description": "example-azure-sdk-secret", "tags": {}, "type": "azure-sdk", "data": "{\"subscriptionId\":\"2cd8674e-4f89-4a1f-b420-7a1361b46ef7\",\"tenantId\":\"292f5674-c8b0-488b-9ff8-6d30d77f38d9\",\"clientId\":\"649846ce-d862-49d5-a5eb-7d5aad90f54e\",\"clientSecret\":\"cpln\"}" } ``` ```yaml YAML theme={null} kind: secret name: example-azure-sdk-secret description: example-azure-sdk-secret tags: {} type: azure-sdk data: >- {"subscriptionId":"2cd8674e-4f89-4a1f-b420-7a1361b46ef7","tenantId":"292f5674-c8b0-488b-9ff8-6d30d77f38d9","clientId":"649846ce-d862-49d5-a5eb-7d5aad90f54e","clientSecret":"cpln"} ``` ```json JSON theme={null} { "kind": "secret", "name": "example-dictionary-secret", "description": "example-dictionary-secret description", "tags": {}, "type": "dictionary", "data": { "key01": "value01", "key02": "value02" } } ``` ```yaml YAML theme={null} kind: secret name: example-dictionary-secret description: example-dictionary-secret description tags: {} type: dictionary data: key01: value01 key02: value02 ``` ```json JSON theme={null} { "kind": "secret", "name": "example-docker-secret", "description": "example-docker-secret description", "tags": {}, "type": "docker", "data": "{\"auths\":{\"https://index.docker.io/v1/\":{\"username\":\"USERNAME\",\"password\":\"PASSWORD\"}}}" } ``` ```yaml YAML theme={null} kind: secret name: example-docker-secret description: example-docker-secret description tags: {} type: docker data: >- {"auths":{"https://index.docker.io/v1/":{"username":"USERNAME","password":"PASSWORD"}}} ``` ```json JSON theme={null} { "kind": "secret", "name": "example-ecr-secret", "description": "example-ecr-secret description", "tags": {}, "type": "ecr", "data": { "accessKey": "AKIA_ACCESS_KEY", "repos": ["015716931711.dkr.ecr.us-west-2.amazonaws.com/repo"], "secretKey": "SECRET_KEY", "externalId": "EXTERNAL_ID" } } ``` ```yaml YAML theme={null} kind: secret name: example-ecr-secret description: example-ecr-secret description tags: {} data: accessKey: AKIA_ACCESS_KEY repos: - 015716931711.dkr.ecr.us-west-2.amazonaws.com/repo secretKey: SECRET_KEY externalId: EXTERNAL_ID ``` ```json JSON theme={null} { "kind": "secret", "name": "example-gcp-secret", "description": "example-gcp-secret description", "tags": {}, "type": "gcp", "data": "{\"type\":\"gcp\",\"project_id\":\"cpln12345\",\"private_key_id\":\"pvt_key\",\"private_key\":\"key\",\"client_email\":\"support@cpln.io\",\"client_id\":\"12744\",\"auth_uri\":\"cloud.google.com\",\"token_uri\":\"token.cloud.google.com\",\"auth_provider_x509_cert_url\":\"cert.google.com\",\"client_x509_cert_url\":\"cert.google.com\"}" } ``` ```yaml YAML theme={null} kind: secret name: example-gcp-secret description: example-gcp-secret description tags: {} type: gcp data: >- {"type":"gcp","project_id":"cpln12345","private_key_id":"pvt_key","private_key":"key","client_email":"support@cpln.io","client_id":"12744","auth_uri":"cloud.google.com","token_uri":"token.cloud.google.com","auth_provider_x509_cert_url":"cert.google.com","client_x509_cert_url":"cert.google.com"} ``` The example below uses a self-signed certificate. Do not use for production. ```json JSON theme={null} { "kind": "secret", "name": "example-keypair-secret", "description": "example-keypair-secret description", "tags": {}, "type": "keypair", "data": { "passphrase": "cpln", "publicKey": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----\n", "secretKey": "-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----" } } ``` ```yaml YAML theme={null} kind: secret name: example-keypair-secret description: example-keypair-secret secret tags: {} type: keypair data: passphrase: cpln publicKey: | -----BEGIN PUBLIC KEY----- ... -----END PUBLIC KEY----- secretKey: | -----BEGIN RSA PRIVATE KEY----- ... -----END RSA PRIVATE KEY----- ``` ```json JSON theme={null} { "kind": "secret", "name": "example-opaque-secret", "description": "example-opaque-secret", "tags": {}, "type": "opaque", "data": { "encoding": "plain", "payload": "sample payload" } } ``` ```yaml YAML theme={null} kind: secret name: example-opaque-secret description: example-opaque-secret description tags: {} type: opaque data: encoding: plain payload: sample payload ``` ```json JSON theme={null} { "kind": "secret", "name": "example-tls-secret", "description": "example-tls-secret description", "tags": {}, "type": "tls", "data": { "cert": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----", "chain": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----", "key": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----" } } ``` ```yaml YAML theme={null} kind: secret name: example-tls-secret description: example-tls-secret description tags: {} type: tls data: cert: | -----BEGIN CERTIFICATE----- ... -----END CERTIFICATE----- chain: | -----BEGIN CERTIFICATE----- ... -----END CERTIFICATE----- key: | -----BEGIN PRIVATE KEY----- ... -----END PRIVATE KEY----- ``` ```json JSON theme={null} { "kind": "secret", "name": "example-username-secret", "description": "example-username-secret description", "tags": {}, "type": "userpass", "data": { "encoding": "plain", "password": "PASSWORD", "username": "USERNAME" } } ``` ```yaml YAML theme={null} kind: secret name: sample-username description: sample-username tags: {} type: userpass data: encoding: plain password: PASSWORD username: USERNAME ``` [Groups Reference Page](/reference/group) See the [Group Query Rules](/reference/group#query-rules) reference page for details on how to create a query. ```json JSON theme={null} { "kind": "group", "name": "example-group", "description": "example-group description", "tags": {}, "memberLinks": [ "//serviceaccount/SERVICE_ACCOUNT_NAME", "//user/USER_EMAIL" ], "memberQuery": { "kind": "user", "fetch": "items", "spec": { "match": "all", "terms": [ { "op": "=", "tag": "test-tag", "value": "test-value" } ] } } } ``` ```yaml YAML theme={null} kind: group name: example-group description: example-group description tags: {} memberLinks: - //serviceaccount/SERVICE_ACCOUNT_NAME - //user/USER_EMAIL memberQuery: kind: user fetch: items spec: match: all terms: - op: '=' tag: test value: '1234' ``` [Policies Reference Page](/reference/policy) Each `targetKind` has its own set of permissions. Get them via: * CLI: `cpln secret permissions` * Reference: [Secret Permissions](/reference/secret#permissions) ```json JSON theme={null} { "kind": "policy", "name": "example-policy-explicit", "description": "example-policy description", "tags": {}, "targetKind": "secret", "bindings": [ { "permissions": ["edit", "manage"], "principalLinks": [ "//group/GROUP_NAME", "//gvc/GVC_NAME/identity/IDENTITY_NAME", "//serviceaccount/SERVICE_ACCOUNT_NAME", "//user/USER_EMAIL" ] } ], "targetLinks": ["//secret/SECRET_NAME"] } ``` ```yaml YAML theme={null} kind: policy name: example-policy-explicit description: example-policy description tags: {} bindings: - permissions: - edit - manage principalLinks: - //group/GROUP_NAME - //gvc/GVC_NAME/identity/IDENTITY_NAME - //serviceaccount/SERVICE_ACCOUNT_NAME - //user/USER_EMAIL targetKind: secret targetLinks: - //secret/SECRET_NAME ``` ```json JSON theme={null} { "kind": "policy", "name": "example-policy-all", "description": "example-policy-all description", "tags": {}, "targetKind": "secret", "target": "all", "bindings": [ { "permissions": ["edit", "manage"], "principalLinks": [ "//group/GROUP_NAME", "//serviceaccount/SERVICE_ACCOUNT_NAME", "//user/USER_EMAIL" ] } ] } ``` ```yaml YAML theme={null} kind: policy name: example-policy-all description: example-policy-all description tags: {} targetKind: secret target: all bindings: - permissions: - edit - manage principalLinks: - //group/GROUP_NAME - //serviceaccount/SERVICE_ACCOUNT_NAME - //user/USER_EMAIL ``` [Identity Reference Page](/reference/identity) ```json JSON theme={null} { "kind": "identity", "name": "example-identity", "description": "example-identity description", "tags": {}, "gvc": "example-gvc" } ``` ```yaml YAML theme={null} kind: identity name: example-identity description: example-identity description tags: {} gvc: example-gvc ``` [Workload Reference Page](/reference/workload/general) ```json JSON theme={null} { "kind": "workload", "name": "example-workload", "description": "example-workload description", "tags": {}, "gvc": "example-gvc", "spec": { "type": "serverless", "containers": [ { "name": "main", "image": "nginx:latest", "port": 80, "memory": "128Mi", "cpu": "50m" } ], "defaultOptions": { "autoscaling": { "minScale": 1, "maxScale": 5 } } } } ``` ```yaml YAML theme={null} kind: workload name: example-workload description: example-workload description tags: {} gvc: example-gvc spec: type: serverless containers: - name: main image: nginx:latest port: 80 memory: 128Mi cpu: 50m defaultOptions: autoscaling: minScale: 1 maxScale: 5 ``` ## Next steps Automate deployments in CI/CD pipelines Convert Kubernetes resources to Control Plane Infrastructure as code workflows Full apply command reference # Delete Resources from Manifests Source: https://docs.controlplane.com/guides/cpln-delete Remove Control Plane resources using the same YAML/JSON files used to create them. The `cpln delete` command removes resources defined in YAML or JSON files, the inverse of `cpln apply`. Use the same manifest files to both create and tear down infrastructure. ## When to use this Remove entire environments using the same manifests that created them Delete resources tracked in version control Delete resources originally created from Kubernetes manifests Remove multiple related resources in one command ## Basic usage ```bash theme={null} cpln delete --file ``` This deletes all resources defined in the file. ## Options | Option | Description | | -------- | ------------------------------------------------------------ | | `--file` | Path to JSON/YAML file. Use `--file -` for stdin | | `--gvc` | GVC for scoped resources (workloads, identities, volumesets) | | `--k8s` | Convert K8s manifest before deleting (`true`/`false`) | ## Specify the GVC Workloads, identities, and volumesets are scoped to a GVC. Specify it using one of these methods: Pass the GVC with each command: ```bash theme={null} cpln delete --file workload.yaml --gvc my-gvc ``` Include the GVC in the resource definition: ```yaml workload.yaml theme={null} kind: workload name: my-app gvc: my-gvc spec: type: serverless containers: - name: main image: nginx:latest ``` ```bash theme={null} cpln delete --file workload.yaml ``` Set a default GVC in your profile: ```bash theme={null} cpln profile update default --gvc my-gvc cpln delete --file workload.yaml ``` You can either specify the GVC in the manifest or via `--gvc`, but not both. ## Delete Kubernetes conversions Delete resources that were originally applied from Kubernetes manifests: ```bash theme={null} cpln delete --file k8s-deployment.yaml --k8s true ``` This converts the K8s manifest to Control Plane format, then deletes the corresponding resources. ## Delete from stdin Pipe resources from another command: ```bash theme={null} # Delete resources from a converted K8s manifest cpln convert --file k8s.yaml | cpln delete --file - # Delete resources from a remote file curl -s https://example.com/resources.yaml | cpln delete --file - ``` ## Multiple resources Delete multiple resources from a single file using YAML document separators (`---`): ```yaml resources.yaml theme={null} kind: workload name: my-app gvc: production spec: type: serverless containers: - name: main image: nginx:latest --- kind: identity name: my-identity gvc: production --- kind: secret name: my-secret type: opaque ``` ```bash theme={null} cpln delete --file resources.yaml ``` ## Using the console Delete resources via the console: 1. Click **cpln apply** in the upper right corner 2. Enable the **Use as Delete** switch 3. Upload or paste your manifest 4. Select the target org and GVC 5. Click **Delete** ## Common workflows ### Tear down a complete environment ```bash theme={null} # Delete all resources in an environment manifest cpln delete --file production-env.yaml ``` ### Remove a feature branch environment ```bash theme={null} # Clean up after merging cpln delete --file feature-branch.yaml --gvc feature-123 ``` ## Troubleshooting The resource may have already been deleted or never existed. This is not an error - the command is idempotent. For workloads, identities, and volumesets, specify the GVC: ```bash theme={null} cpln delete --file workload.yaml --gvc my-gvc ``` Some resources can't be deleted if other resources depend on them. Delete dependent resources first, or delete the GVC containing all resources. ## Next steps Create resources from manifests Convert Kubernetes resources Full delete command reference Automate deletions in pipelines # Manage Helm Releases Source: https://docs.controlplane.com/guides/cpln-helm Deploy and manage Control Plane resources using Helm charts with the cpln helm command. The `cpln helm` command lets you manage Control Plane resources using [Helm charts](https://helm.sh/docs/topics/charts/). Define your infrastructure as Helm templates and deploy them as versioned releases with full rollback support. ## When to use this Use Helm's templating engine for complex, parameterized configurations Deploy the same chart with different values for dev, staging, and production Track deployment history and rollback to previous versions Leverage existing Helm workflows and chart repositories ## Prerequisites Install the Control Plane CLI. See [Installation](/cli-reference/installation). Install [Helm](https://helm.sh/docs/intro/install/) (v3 or later). You need [policies](/reference/policy) to create the resources defined in your chart, plus `reveal` permission on secrets to manage release state. ## Core concepts ### Releases A **release** is a deployed instance of a Helm chart. Each release: * Has a unique name within your organization * Stores its state in an [opaque secret](/reference/secret#opaque) * Tracks revisions for rollback (10 by default, configurable with [`--history-limit`](#manage-revision-history)) * Tags resources with `cpln/release` for tracking #### Release name constraints Release names must follow these rules: | Rule | Detail | | -------------- | ------------------------------------------------------------------- | | **Format** | DNS-1123 label — lowercase alphanumeric characters and hyphens only | | **Pattern** | Must match `^[a-z0-9]([-a-z0-9]*[a-z0-9])?$` | | **Max length** | 53 characters | | **Uniqueness** | Must be unique within the same organization | Alternatively, use `--generate-name` (or `-g`) to auto-generate a name in the format `-`. ### Release state The release state tracks all resources deployed by the release, enabling: * Detection of added, modified, or removed resources * Automatic cleanup of deleted resources on upgrade * Rollback to any previous revision ### Supported resource types Charts must contain **only Control Plane resource definitions**. Standard Kubernetes manifests (objects with `apiVersion` or `metadata` fields) are not supported and will cause the operation to fail. If your chart contains any resource with an `apiVersion` or `metadata` field, or an unsupported `kind`, the install/upgrade will abort with: ```text theme={null} ERROR: Some resources in the rendered template are not CPLN resources. ``` Remove any Kubernetes manifests and ensure every resource kind is a supported Control Plane kind. ### GVC-scoped resources The following resource kinds require a GVC context (via `--gvc` or your default profile context). The GVC must already exist or be created earlier in the same chart: `workload`, `identity`, `volumeset` ## Quick start Generate a new Helm chart: ```bash theme={null} helm create my-chart ``` Replace the contents of `my-chart/templates/` with Control Plane resources: ```yaml my-chart/templates/resources.yaml theme={null} kind: gvc name: {{ .Values.gvc.name }} spec: staticPlacement: locationLinks: - //location/aws-eu-central-1 --- kind: workload name: {{ .Values.workload.name }} gvc: {{ .Values.gvc.name }} spec: type: serverless containers: - name: main image: {{ .Values.workload.image }} ports: - number: 80 protocol: http ``` Update `my-chart/values.yaml`: ```yaml my-chart/values.yaml theme={null} gvc: name: my-gvc workload: name: my-app image: nginx:latest ``` Deploy the chart: ```bash theme={null} cpln helm install my-release ./my-chart ``` ## Install a release ```bash theme={null} cpln helm install ``` Install from different sources: ```bash theme={null} cpln helm install my-release ./my-chart ``` ```bash theme={null} cpln helm install my-release postgres \ --repo https://controlplane-com.github.io/helm-packages \ --version 2.1.0 ``` ```bash theme={null} cpln helm install my-release \ \ --version ``` ```bash theme={null} cpln helm install --generate-name ./my-chart ``` Creates a release named `-` (e.g., `my-chart-1709913600`). ### Wait for readiness Wait for workloads to become ready: ```bash theme={null} cpln helm install my-release ./my-chart --wait --timeout 600 ``` ### Handle chart dependencies If your chart has dependencies defined in `Chart.yaml`, use `--dependency-update` to fetch them automatically: ```bash theme={null} cpln helm install my-release ./my-chart --dependency-update ``` Without this flag, missing dependencies will cause an error prompting you to re-run with the flag. ## Upgrade a release Update a release after modifying the chart: ```bash theme={null} cpln helm upgrade my-release ./my-chart ``` The CLI: 1. Renders the updated chart 2. Applies new and modified resources 3. Deletes resources removed from the chart (unless they have `helm.sh/resource-policy: keep`) 4. Updates the release state ### Install or upgrade Use `--install` to create the release if it does not exist, or upgrade it if it does: ```bash theme={null} cpln helm upgrade my-release ./my-chart --install ``` This is useful in CI/CD pipelines where you don't want to check whether the release already exists. Use the same release name **and chart name** to ensure proper tracking of resources. Upgrading with a different chart name than the original install will fail with: ```text theme={null} ERROR: UPGRADE FAILED: cannot upgrade with different chart name ``` ## Manage revision history By default, each release retains up to **10 revisions** for rollback. Use `--history-limit` on `upgrade` to adjust this: ```bash theme={null} # Keep 20 revisions cpln helm upgrade my-release ./my-chart --history-limit 20 # Keep unlimited revisions cpln helm upgrade my-release ./my-chart --history-limit 0 ``` The `--history-limit` flag must be provided on **every upgrade**. If omitted, it resets to the default of 10 revisions. For consistent behavior, always include `--history-limit` in your upgrade commands or CI/CD pipelines. You can also set the `HELM_MAX_HISTORY` environment variable as an alternative to the `--history-limit` flag. The CLI flag takes precedence over the environment variable. | Value | Behavior | | -------------- | ---------------------------------- | | `10` (default) | Keeps the 10 most recent revisions | | `0` | Keeps all revisions indefinitely | | `N` | Keeps the N most recent revisions | Setting `--history-limit 0` is useful for audit compliance or when you need the ability to rollback to any previous state. ## Release description Use `--description` (or `--desc`) to attach a custom description to a release revision. It appears in `cpln helm history` output: ```bash theme={null} cpln helm install my-release ./my-chart --description "Initial deployment" cpln helm upgrade my-release ./my-chart --description "Added caching layer" ``` On rollback, if no description is provided, it auto-generates `"Rollback to "`. ## Rollback a release Revert to a previous revision: ```bash theme={null} # Rollback to previous revision cpln helm rollback my-release # Rollback to specific revision cpln helm rollback my-release 2 ``` ### Clean up on failure Use `--cleanup-on-fail` to automatically delete resources created during a failed rollback: ```bash theme={null} cpln helm rollback my-release --cleanup-on-fail ``` ## View release information ```bash theme={null} cpln helm list ``` Lists all releases across all GVCs in the organization. ``` +------------+--------+---------+---------------+---------+--------+ |NAME |GVC |REVISION |UPDATED |STATUS |CHART | +------------+--------+---------+---------------+---------+--------+ |my-release |my-gvc |3 |5 minutes ago |deployed |my-chart| +------------+--------+---------+---------------+---------+--------+ ``` ```bash theme={null} cpln helm history my-release ``` ``` +---------+--------------+-----------+----------+----------------------+ |REVISION |UPDATED |STATUS |CHART |DESCRIPTION | +---------+--------------+-----------+----------+----------------------+ |1 |1 hour ago |superseded |my-chart |Install complete | |2 |30 min ago |superseded |my-chart |Upgrade complete | |3 |5 min ago |deployed |my-chart |Upgrade complete | +---------+--------------+-----------+----------+----------------------+ ``` ```bash theme={null} cpln helm get manifest my-release ``` Shows the rendered YAML for the current release. ```bash theme={null} cpln helm get all my-release ``` Shows combined release info, values, and manifest. ```bash theme={null} # User-provided values only cpln helm get values my-release # All values (chart defaults + user overrides) cpln helm get values my-release --all ``` ```bash theme={null} cpln helm get notes my-release ``` Shows the chart notes (from `NOTES.txt`) for the current release. All `helm get` subcommands accept `--revision ` to view data from a specific historical revision: ```bash theme={null} cpln helm get values my-release --revision 2 cpln helm get manifest my-release --revision 2 ``` ## Uninstall a release Remove all resources and delete the release: ```bash theme={null} cpln helm uninstall my-release ``` This deletes: * All resources tracked by the release * The release state secret ## Template preview Preview the rendered output without deploying: ```bash theme={null} cpln helm template my-release ./my-chart ``` ## Injected values The CLI automatically injects these values into your chart, making them available in templates without requiring you to define them: | Key | Description | | ---------- | ------------------------------------------- | | `cpln.org` | Current organization name | | `cpln.gvc` | Current GVC name (when `--gvc` is provided) | Use in templates: ```yaml theme={null} gvc: {{ .Values.cpln.gvc }} ``` Avoid defining a top-level `cpln` key in your `values.yaml` as it will be overwritten by the injected values. `--set` and `--values` are **not carried over** between operations. Each upgrade generates a fresh config from the chart defaults plus whatever you pass in that command. CI/CD pipelines must pass the full set of values on every upgrade. ## Tags and labels ### Tagging resources Use `--tag` to attach custom tags to **all resources** deployed by the release: ```bash theme={null} cpln helm install my-release ./my-chart \ --tag environment=production \ --tag team=backend ``` Tags defined in your chart templates are also applied. The precedence order (highest wins): 1. **Helm metadata tags** (`cpln/release`) — always applied, cannot be overridden 2. **Template-level tags** — tags defined on individual resources in your chart YAML 3. **Release-level tags** — tags from `--tag` and `--state-tag` flags This means tags set on a specific resource in your template override the same key passed via `--tag`. Use `--remove-tag` on upgrade to remove tags from resources: ```bash theme={null} cpln helm upgrade my-release ./my-chart --remove-tag environment ``` ### Tagging the release secret Use `--state-tag` to attach tags to the **release state secret** itself (not the deployed resources): ```bash theme={null} cpln helm install my-release ./my-chart \ --state-tag managed-by=ci \ --state-tag pipeline-id=12345 ``` Use `--remove-state-tag` on upgrade to remove state tags: ```bash theme={null} cpln helm upgrade my-release ./my-chart --remove-state-tag pipeline-id ``` ### Tag behavior across revisions Unlike values, **tags carry over** between revisions. You don't need to re-specify `--tag` or `--state-tag` on every upgrade — previous tags are used as a base and new ones merge on top. Use `--remove-tag` or `--remove-state-tag` to explicitly drop a tag. ## Secret change detection When a chart contains both secrets and workloads that reference those secrets, the CLI automatically tags workloads with a hash of each referenced secret's content: ```text theme={null} cpln/secret: = ``` When you upgrade a release and a secret's content changes, the hash changes, triggering a workload redeployment. This ensures workloads always run with the latest secret values. ## Protect resources from deletion Prevent a resource from being deleted during upgrades or uninstalls: ```yaml theme={null} kind: volumeset name: persistent-data tags: helm.sh/resource-policy: keep ``` Resources with this tag are skipped during cleanup operations. The CLI will print a message confirming the resource was preserved: ```text theme={null} Skipped deletion of /org/{org}/gvc/{gvc}/volumeset/persistent-data because it has resource-policy=keep ``` ## Chart notes Create `templates/NOTES.txt` to display messages after install/upgrade: ```text theme={null} Release {{ .Release.Name }} deployed! Access your app at: https://{{ .Values.workload.name }}.cpln.app ``` ## Environment-specific values Use different values files for each environment: ```bash theme={null} # Development cpln helm install dev-release ./my-chart -f values-dev.yaml # Production cpln helm install prod-release ./my-chart -f values-prod.yaml ``` ## Troubleshooting Use `upgrade` instead of `install` for existing releases: ```bash theme={null} cpln helm upgrade my-release ./my-chart ``` Or use `upgrade --install` to handle both cases: ```bash theme={null} cpln helm upgrade my-release ./my-chart --install ``` If a previous operation was interrupted, the release may be stuck in `pending-install`, `pending-upgrade`, or `pending-rollback` status. The CLI will show an error that includes the exact secret name and the command to delete it: ``` ERROR: Release 'my-release' is 'pending-install', please wait for the install or upgrade to finish. If this release revision is still pending or stuck, you will need to manually delete the secret 'cpln-helm-release-my-release-abc12xyz-v1'. Use the 'cpln secret delete cpln-helm-release-my-release-abc12xyz-v1' command to do so. ``` Run the `cpln secret delete` command shown in the error message to unblock the release. If your chart contains Kubernetes manifests (resources with `apiVersion` or `metadata` fields), you'll see: ``` ERROR: Some resources in the rendered template are not CPLN resources. ``` Remove any standard Kubernetes resources from your templates. Charts must contain only Control Plane resource definitions. When upgrading a release, the chart name must match the one used during install: ``` ERROR: UPGRADE FAILED: cannot upgrade with different chart name ``` If you need to switch charts, uninstall the release first, then install with the new chart. Ensure you're using the same release name. Resources are only tracked within a single release. Also check if the resources have the `helm.sh/resource-policy: keep` tag, which prevents deletion. Check available revisions with `cpln helm history`. Rollback can only target revisions with `deployed` or `superseded` status. By default, only 10 revisions are retained. See [Manage revision history](#manage-revision-history) to adjust this limit. If your chart has dependencies in `Chart.yaml` that haven't been fetched: ``` ERROR: Helm chart dependencies are missing. ``` Re-run with the `--dependency-update` flag: ```bash theme={null} cpln helm install my-release ./my-chart --dependency-update ``` GVC-scoped resources (workload, identity, volumeset) require the GVC to exist. Either create the GVC in a separate step before installing, or include the GVC as a resource in your chart so it's created first. Ensure you have `reveal` permission on secrets to manage release state. Release names must be DNS-1123 compliant: lowercase alphanumeric and hyphens only, max 53 characters, cannot start or end with a hyphen. Use `--generate-name` if you don't want to manage names manually. ## Next steps Apply resources without Helm Automate Helm deployments Understand workload configuration Full helm command reference # Create an Audit Context Source: https://docs.controlplane.com/guides/create-audit-context Create an audit context within your org using the Console UI or CLI to track and manage audit events. ## Overview Follow the steps below to create an [audit context](/reference/auditctx) within your [org](/reference/org). ## Prerequisites * Review the [audit context](/reference/auditctx) reference page. * Have [permissions](/reference/policy#permissions) to create an [audit context](/reference/auditctx). * Optional: Install the [CLI](/cli-reference/installation). ## Create using the UI Console 1. Create a new audit context using one of the following methods: * Click `Audit Contexts` in the left menu, then click `New`, or * Click the `Create` dropdown in the upper right corner and select `Audit Context`. 2. Enter a unique name and optional description. 3. Optional: Click `Tags` and enter any [tags](/core/misc#tags). 4. Click `Create`. ## Create using the CLI Refer to the [auditctx create](/cli-reference/commands/auditctx#auditctx-create) command for details and examples on how to create an audit context using the CLI. # Create a Cloud Account Source: https://docs.controlplane.com/guides/create-cloud-account Create a cloud account in your org and configure permissions at AWS, Azure, GCP, or NGS to grant Control Plane access. ## Overview Creating a [cloud account](/reference/cloudaccount) involves two steps: * Create the [cloud account](/reference/cloudaccount) in Control Plane. * Configure permissions in the cloud provider to grant Control Plane access. Follow the steps below to create a [cloud account](/reference/cloudaccount) within your [org](/reference/org). ## Prerequisites * Review the [cloud account](/reference/cloudaccount) reference page. * Have [permissions](/reference/policy#permissions) to create a [cloud account](/reference/cloudaccount). * Optional: Install the [CLI](/cli-reference/installation). * For Azure cloud accounts: the `az` CLI is required to run the setup commands. Install it locally, or use [Azure Cloud Shell](https://shell.azure.com/bash) which has `az` preinstalled. ## Create using the Console 1. Create a new cloud account using one of the following methods: * Click `Cloud Accounts` in the left menu, then click `New`, or * Click the `Create` dropdown in the upper right corner and select `Cloud Account`. 2. Enter a unique name and optional description. 3. Under `Cloud Provider`, choose the provider you are targeting. 4. Click `Provider` and follow the instructions shown to complete the provider-specific cloud account setup. 5. Optionally, click `Tags` and enter any [tags](/core/misc#tags). 6. Click `Create` to add the cloud account and connect it to the cloud provider. ## Create using the CLI The CLI offers three commands to create a cloud account corresponding to each cloud provider: | Cloud Provider | CLI Command | | :------------- | :----------------------------------------------------------------------------------------------- | | AWS | [cpln cloudaccount create-aws](/cli-reference/commands/cloudaccount#cloudaccount-create-aws) | | Azure | [cpln cloudaccount create-azure](/cli-reference/commands/cloudaccount#cloudaccount-create-azure) | | GCP | [cpln cloudaccount create-gcp](/cli-reference/commands/cloudaccount#cloudaccount-create-gcp) | Running the following command for each provider shows detailed instructions on how to create a cloud account: ```bash AWS theme={null} cpln cloudaccount create-aws --org ORG_NAME --how ``` ```bash Azure theme={null} cpln cloudaccount create-azure --org ORG_NAME --how ``` ```bash GCP theme={null} cpln cloudaccount create-gcp --org ORG_NAME --how ``` The instructions shown from the `--how` option will include steps for both the cloud provider UI and CLI. ## Reference Refer to the [cloud account](/reference/cloudaccount) reference page for additional details. # Create a Group Source: https://docs.controlplane.com/guides/create-group Create a group within your org to manage user and service account membership, including direct and tag-based dynamic assignment. ## Overview Follow the steps below to create a [group](/reference/group) within your [org](/reference/org). User membership in a group can be assigned directly or dynamically by using a [tag](/core/misc#tags)-based query. Service account membership in a group can only be assigned directly. ## Prerequisites * Review the [group](/reference/group) reference page. * Have [permissions](/reference/policy#permissions) to create a [group](/reference/group). * Optional: Install the [CLI](/cli-reference/installation). ## Create using the Console 1. Create a new group using one of the following methods: * Click `Groups` in the left menu, then click `New`, or * Click the `Create` dropdown in the upper right corner and select `Group`. 2. Enter a unique name and optional description. 3. To add users, click `Users`, then click `Add User`. * The list of [users](/reference/user) is displayed and can be selected. * A [query](/reference/group#query-rules) can also be defined to dynamically assign users to the group. * Click `Ok` to continue. 4. To add service accounts, click `Service Accounts`, then click `Add Service Accounts`. * The list of [service accounts](/reference/serviceaccount) is displayed and can be selected. 5. Optionally, click `Tags` and enter any [tags](/core/misc#tags). 6. Click `Create`. ## Create using the CLI Refer to the [group create](/cli-reference/commands/group#group-create) command for details and examples on how to create a group using the CLI. ## Next Steps Groups can be used by [policies](/reference/policy) to grant permissions to the group members. # Create a GVC Source: https://docs.controlplane.com/guides/create-gvc Step-by-step guide to create Global Virtual Clouds via Console UI and CLI. Covers naming, cloud provider selection, pull secrets, and tags. ## Overview Follow the steps below to create a [GVC](/reference/gvc) within your [org](/reference/org). ## Prerequisites * Review the [GVC](/reference/gvc) reference page. * Have [permissions](/reference/policy#permissions) to create a [GVC](/reference/gvc). * Optional: Install the [CLI](/cli-reference/installation). ## Create using the Console 1. Create a new GVC using one of the following methods: * Click `GVCs` in the left menu, then click `New`, or * Click the `Create` dropdown in the upper right corner and select `GVC`. 2. Enter a unique name and an optional description. 3. Click `Locations`, then click `Add Location` to view a list of available locations. Select at least one location. 4. Optionally, click `Tags` and enter any [tags](/core/misc#tags). 5. Click `Create`. These are the minimum properties required to create a GVC. Refer to the [GVC](/reference/gvc) reference page for details on the remaining properties that can be configured. ## Create using the CLI Refer to the [gvc create](/cli-reference/commands/gvc#gvc-create) command for details and examples. ## Next Steps Your GVC is ready to be configured with a [workload](/guides/create-workload). # Create an Identity Source: https://docs.controlplane.com/guides/create-identity Create an identity within a GVC to configure cloud access rules, cloud wormholes, and native networking for your workloads. ## Overview Follow the steps below to create an [identity](/reference/identity) within your [GVC](/reference/gvc). ## Prerequisites * Review the [identity](/reference/identity) reference page. * Have [permissions](/reference/identity#permissions) to create an [identity](/reference/identity). * Optional: Install the [CLI](/cli-reference/installation). ## Create using the Console 1. Create a new identity using one of the following methods: * Click `Identities` in the left menu, then click `New`, or * Click the `Create` dropdown in the upper right corner and select `Identity`. 2. Enter a unique name, an optional description, and select the GVC where the identity will be created. 3. Under `Cloud Access`, select the cloud provider (`AWS`, `Azure`, `GCP`, or `NGS`) to configure a cloud access rule. * Requires at least one [cloud account](/reference/cloudaccount) for the chosen provider to be defined. * Depending on the use case of this identity, creating a cloud access rule is optional. * See [Cloud Access](#cloud-access) for additional details. 4. Under `Cloud Wormhole`, configure private network connectivity. * Select `FQDN Resources` and click `Add FQDN` to add resources by domain name. * Select `IP Resources` and click `Add IP` to add resources by IP address. * Depending on the use case of this identity, creating a cloud wormhole is optional. * See [Cloud Wormhole](#cloud-wormhole) for additional details. 5. Under `Native Networking`, configure cloud-native private connectivity. * Select `AWS PrivateLink` and click `Add AWS Resource` to configure an AWS PrivateLink endpoint. * Select `GCP Service Connect` and click `Add GCP Resource` to configure a GCP Private Service Connect endpoint. * Depending on the use case of this identity, creating a native networking rule is optional. * See [Native Networking](#native-networking) for details. 6. Optionally, click `Tags` and enter any [tags](/core/misc#tags). 7. Click `Create` to create the identity. The identity info page is then shown. This identity is now available for use in the [workload identity](/reference/workload#identity) setting. ## Cloud Access The cloud access portion of an identity defines cloud resource access rules across one account in each of AWS, Azure, GCP, and NGS. In other words, you can create an identity that allows access to several resources in a particular AWS account and a particular Azure account, but not in two separate Azure accounts. When defining the rule for a particular cloud provider, Control Plane creates and manages (using the registered [cloud account](/reference/cloudaccount)) the following object at each cloud provider which acts as a "synthetic identity": * AWS * Role * Azure * App registration * GCP * Service Account The minimum set of permissions required by the [workload](/reference/workload) to call the target cloud resources should be assigned to the cloud access rule. When [workloads](/reference/workload) call the cloud resource, they call the services by impersonating the **"synthetic identity"**. This **"synthetic identity"** will only have the permissions that were assigned to it. Having multiple cloud providers configured on an [identity](/reference/identity) using cloud access rules grants the [workload](/reference/workload) the ability to call cloud resources at any cloud provider seamlessly and transparently regardless of where it is running. Below are instructions on how to set up cloud access rules using the console for: * [AWS](#aws) * [Azure](#azure) * [GCP](#gcp) * [NGS](#ngs) ### AWS To set up an AWS cloud access rule, select `AWS` under `Cloud Access`. 1. Click the `Configure` button. 2. Select one of the registered AWS [cloud accounts](/reference/cloudaccount). 3. Select **one** of the following methods: * `Use an Existing AWS Role`: * A list of roles is shown. Select a role from the list and verify that the role name is correct. * `Select Existing AWS Policies`: * A list of available policies is shown. Select at least one policy from the list. Verify that the roles or policies selected are correct and click `Create`. If a new AWS role was selected, Control Plane will provision a new role in AWS that will be named the same as the `Object Name` shown in the `Info` page of the [identity](/reference/identity). ### Azure To set up an Azure cloud access rule, select `Azure` under `Cloud Access` in the left pane. 1. Click the `Configure` button. 2. Select one of the registered Azure [cloud accounts](/reference/cloudaccount). 3. Click `Add Role Assignment` to construct the role assignments: * Click the `Browse` button next to `Scope` to show the scope selection wizard. Choose the service, region, type, and item. Click `Confirm`. * Click the `Browse` button next to `Roles` to show the list of available roles for the selected scope. Select one or more roles. Click `Confirm`. * If additional role assignments are needed, click `Add Role Assignment` and repeat the first two steps. Verify that the roles selected are correct and click `Create`. Control Plane will provision a new App registration in Azure that will be named the same as the `Object Name` shown in the `Info` page of the [identity](/reference/identity). ### GCP To set up a GCP cloud access rule, select `GCP` under `Cloud Access` in the left pane. 1. Click the `Configure` button. 2. Select one of the registered GCP [cloud accounts](/reference/cloudaccount). 3. Select one of the following methods: * `Use an Existing GCP Service Account`: * A list of service accounts is shown. Verify that the service account name is correct. * `Configure Service Account Bindings`: * Click `Add Binding` to construct a new binding: * Click the `Browse` button next to `Resource` to show the resource selection wizard. Choose the service, region, type, and item. Click `Confirm`. * Click the `Browse` button next to `Roles` to show the list of available roles for the selected resource. Select one or more roles. Click `Confirm`. * To manually add a role, click the `Add` button and enter the role name in the empty textbox. * Click `Add`. * If additional bindings are needed, click `Add Binding`. Repeat the first two bullets. Verify that the roles selected are correct and click `Create`. If a new service account was selected, Control Plane will provision the new service account in GCP that will be named the same as the `Object Name` shown in the `Info` page of the [identity](/reference/identity). ### NGS Documentation coming soon. ## Cloud Wormhole The cloud wormhole portion of an [identity](/reference/identity) defines network traversal rules from [workloads](/reference/workload) to specific endpoints in private networks (e.g., a VPC). Tunneling network traffic from [workloads](/reference/workload) to specific TCP hosts and ports is facilitated using [agents](/reference/agent) deployed within the private network. Under `Cloud Wormhole` in the left pane, choose the resource type: ### FQDN Resources Select `FQDN Resources` and click `Add FQDN` to add a resource by domain name. 1. Enter the Fully Qualified Domain Name (FQDN) of the internal resource. 2. Enter a unique `name` for this resource. 3. Select a registered [agent](/reference/agent) matching the environment you want to access. 4. Optionally, enter the internal IP address that the FQDN will resolve to. 5. Under `Ports`, click the `Add` button and enter at least one port that the resource exposes. 6. Click `Add`. Verify that the FQDN resources are correct and click `Create`. The internal resource can be called by the workload using either the FQDN or the `name` entered in step 2. If the internal resource is configured with TLS, the FQDN must be used. ### IP Resources Select `IP Resources` and click `Add IP` to add a resource by IP address. 1. Enter a unique `name` for this resource. This `name` will be the hostname your [workload](/reference/workload) will use when calling this resource. 2. Select a registered [agent](/reference/agent) matching the environment you want to access. 3. Under `IPs`, click `Add` and enter at least one IP address. 4. Under `Ports`, click `Add` and enter at least one port. 5. Click `Add`. A maximum of **5** ports can be added per resource. Verify that the IP resources are correct and click `Create`. ## Native Networking Refer to the [Native Networking Setup](/guides/native-networking/native-networking-setup) guide for details. ## Create using the CLI Refer to the [identity create](/cli-reference/commands/identity#identity-create) command for details and examples. # Create an Org Source: https://docs.controlplane.com/guides/create-org Create a new organization using the Console or CLI, including prerequisites for the org_creator or billing_admin role. ## Prerequisites * Review the [org](/reference/org) reference page. * Have [permissions](#permissions) to create an [org](/reference/org). * Optional: Install the [CLI](/cli-reference/installation). ## Permissions To create an org, the `org_creator` or `billing_admin` role must be assigned by a billing admin from the `Billing Accounts` dashboard. Follow the steps below to access the `Billing Accounts` dashboard and assign the role: 1. Access the `Billing Accounts` dashboard using one of the following methods: * Click the profile icon in the upper right corner and select `Billing Accounts`, or * Click `Billing Accounts` in the left menu (within the `Management` section). 2. Select the billing account. 3. Click `Users`. 4. If the user is not shown in the current users list, click `Add`, enter their email address, select the `org_creator` or `billing_admin` role. 5. If the user is already on the current users list and they do not have the `org_creator` or `billing_admin` role, click the dropdown for the corresponding user, select the `org_creator` or `billing_admin` role. 6. Click `Update`. ## Create using the Console Follow the steps below to create an org: 1. Click the `Create` button in the upper right corner and select `Org`. 2. Select a `Billing Account ID`. 3. Enter a unique org name, an optional description, and add any additional org admin email addresses. Orgs are immutable and cannot be renamed or deleted. 4. Click `Create Org`. The user creating the org, and any additional org admins added, will automatically be assigned to the `superusers` group. ## Create using the CLI Refer to the [org create](/cli-reference/commands/org#org-create) command for details and examples. # AWS Source: https://docs.controlplane.com/guides/create-secret/aws Create an AWS secret to store IAM credentials for authenticating workloads with AWS services like S3, DynamoDB, and SQS. AWS secrets store IAM credentials that allow your workloads to authenticate with AWS services. Use them to access S3, DynamoDB, SQS, or any other AWS service directly from your applications. ## Use Cases * **S3 Access**: Read/write files to S3 buckets * **Database Connections**: Connect to RDS, DynamoDB, or ElastiCache * **Message Queues**: Send/receive messages from SQS or SNS * **AWS SDK Integration**: Any application using the AWS SDK * **Cross-Account Access**: Access resources in other AWS accounts via role assumption ## Configuration Options | Field | Description | Required | | :----------- | :---------------------------------------------------- | :------- | | `accessKey` | AWS Access Key ID (starts with `AKIA...`) | Yes | | `secretKey` | AWS Secret Access Key | Yes | | `roleArn` | IAM Role ARN to assume for cross-account access | No | | `externalId` | External ID for role assumption (additional security) | No | For enhanced security, use role assumption with `roleArn` instead of long-lived access keys. This allows you to grant temporary, scoped access to AWS resources. *** ## Create an AWS Secret In the Console, navigate to **Secrets** and click **New**, or use the **Create** dropdown in the top-right corner and select **Secret**. Enter a **Name** and optional **Description**. Select **AWS** as the secret type. Click **Data** in the left pane. Enter the **Access Key** and **Secret Key**. Optionally enter a **Role ARN** and **External ID** for role assumption. Click **Create**. Basic credentials: ```bash theme={null} cpln secret create-aws \ --name aws-s3-access \ --access-key AKIAIOSFODNN7EXAMPLE \ --secret-key wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY \ --org my-org ``` With role assumption: ```bash theme={null} cpln secret create-aws \ --name aws-cross-account \ --access-key AKIAIOSFODNN7EXAMPLE \ --secret-key wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY \ --role-arn arn:aws:iam::123456789012:role/CrossAccountRole \ --external-id my-external-id \ --org my-org ``` ```hcl theme={null} resource "cpln_secret" "aws_s3" { name = "aws-s3-access" description = "AWS credentials for S3 access" aws { access_key = "AKIAIOSFODNN7EXAMPLE" secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" } } ``` With role assumption: ```hcl theme={null} resource "cpln_secret" "aws_cross_account" { name = "aws-cross-account" description = "AWS credentials with role assumption" aws { access_key = "AKIAIOSFODNN7EXAMPLE" secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" role_arn = "arn:aws:iam::123456789012:role/CrossAccountRole" external_id = "my-external-id" } } ``` This example uses hardcoded credentials for testing. In production, use Terraform variables or integrate with AWS Secrets Manager. ```typescript theme={null} import * as cpln from "@pulumiverse/cpln"; const awsSecret = new cpln.Secret("aws-s3-access", { name: "aws-s3-access", description: "AWS credentials for S3 access", aws: { accessKey: "AKIAIOSFODNN7EXAMPLE", secretKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", roleArn: "arn:aws:iam::123456789012:role/CrossAccountRole", externalId: "my-external-id", }, }); ``` ```python theme={null} import pulumiverse_cpln as cpln aws_secret = cpln.Secret("aws-s3-access", name="aws-s3-access", description="AWS credentials for S3 access", aws={ "access_key": "AKIAIOSFODNN7EXAMPLE", "secret_key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", "role_arn": "arn:aws:iam::123456789012:role/CrossAccountRole", "external_id": "my-external-id", }) ``` ```go theme={null} package main import ( "github.com/pulumi/pulumi/sdk/v3/go/pulumi" "github.com/pulumiverse/pulumi-cpln/sdk/go/cpln" ) func main() { pulumi.Run(func(ctx *pulumi.Context) error { _, err := cpln.NewSecret(ctx, "aws-s3-access", &cpln.SecretArgs{ Name: pulumi.String("aws-s3-access"), Description: pulumi.String("AWS credentials for S3 access"), Aws: &cpln.SecretAwsArgs{ AccessKey: pulumi.String("AKIAIOSFODNN7EXAMPLE"), SecretKey: pulumi.String("wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"), RoleArn: pulumi.String("arn:aws:iam::123456789012:role/CrossAccountRole"), ExternalId: pulumi.String("my-external-id"), }, }) return err }) } ``` ```csharp theme={null} using Pulumi; using Pulumiverse.Cpln; using Pulumiverse.Cpln.Inputs; return await Deployment.RunAsync(() => { var awsSecret = new Secret("aws-s3-access", new SecretArgs { Name = "aws-s3-access", Description = "AWS credentials for S3 access", Aws = new SecretAwsArgs { AccessKey = "AKIAIOSFODNN7EXAMPLE", SecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", RoleArn = "arn:aws:iam::123456789012:role/CrossAccountRole", ExternalId = "my-external-id", }, }); }); ``` *** ## Best Practices Create IAM users or roles with only the permissions your workload needs. Avoid using root credentials or overly permissive policies. Set up a rotation schedule for your AWS access keys. Control Plane makes it easy to update secrets without redeploying workloads. When accessing cross-account resources, use `roleArn` with temporary credentials instead of sharing long-lived access keys. *** ## Next Steps Learn how to grant access and inject secrets Set up AWS cloud account integration # Azure Connector Source: https://docs.controlplane.com/guides/create-secret/azure-connector Create an Azure Connector secret to store credentials for connecting to Azure Function Apps from Control Plane workloads. Azure Connector secrets store credentials for connecting to Azure Function Apps, enabling Control Plane to integrate with serverless Azure functions for custom processing or webhooks. ## Use Cases * **Webhook Processing**: Trigger Azure Functions from Control Plane events * **Custom Integrations**: Connect to Azure-hosted business logic * **Serverless Workflows**: Integrate with Azure Durable Functions * **Event Processing**: Route events to Azure Function endpoints ## Configuration Options | Field | Description | Required | | :----- | :----------------------------------------------------- | :------- | | `url` | Azure Function App deployment URL | Yes | | `code` | Function authentication key (host key or function key) | Yes | The `code` is the function key that authenticates requests to your Azure Function. You can find it in the Azure Portal under your Function App's **App keys** or **Function keys** section. *** ## Create an Azure Connector Secret In the Console, navigate to **Secrets** and click **New**, or use the **Create** dropdown in the top-right corner and select **Secret**. Enter a **Name** and optional **Description**. Select **Azure Connector** as the secret type. Click **Data** in the left pane. Enter the **URL** (Azure Function App deployment URL) and the **Code** (authentication key). Click **Create**. ```bash theme={null} cpln secret create-azure-connector \ --name azure-function-connector \ --url https://my-function-app.azurewebsites.net/api/webhook \ --code your-function-key-here \ --org my-org ``` ```hcl theme={null} resource "cpln_secret" "azure_connector" { name = "azure-function-connector" description = "Azure Function App webhook connector" azure_connector { url = "https://my-function-app.azurewebsites.net/api/webhook" code = "your-function-key-here" } } ``` This example uses a placeholder function key for testing. In production, use Terraform variables or a secrets manager. ```typescript theme={null} import * as cpln from "@pulumiverse/cpln"; const azureConnectorSecret = new cpln.Secret("azure-function-connector", { name: "azure-function-connector", description: "Azure Function App webhook connector", azureConnector: { url: "https://my-function-app.azurewebsites.net/api/webhook", code: "your-function-key-here", }, }); ``` ```python theme={null} import pulumiverse_cpln as cpln azure_connector_secret = cpln.Secret("azure-function-connector", name="azure-function-connector", description="Azure Function App webhook connector", azure_connector={ "url": "https://my-function-app.azurewebsites.net/api/webhook", "code": "your-function-key-here", }) ``` ```go theme={null} package main import ( "github.com/pulumi/pulumi/sdk/v3/go/pulumi" "github.com/pulumiverse/pulumi-cpln/sdk/go/cpln" ) func main() { pulumi.Run(func(ctx *pulumi.Context) error { _, err := cpln.NewSecret(ctx, "azure-function-connector", &cpln.SecretArgs{ Name: pulumi.String("azure-function-connector"), Description: pulumi.String("Azure Function App webhook connector"), AzureConnector: &cpln.SecretAzureConnectorArgs{ Url: pulumi.String("https://my-function-app.azurewebsites.net/api/webhook"), Code: pulumi.String("your-function-key-here"), }, }) return err }) } ``` ```csharp theme={null} using Pulumi; using Pulumiverse.Cpln; using Pulumiverse.Cpln.Inputs; return await Deployment.RunAsync(() => { var azureConnectorSecret = new Secret("azure-function-connector", new SecretArgs { Name = "azure-function-connector", Description = "Azure Function App webhook connector", AzureConnector = new SecretAzureConnectorArgs { Url = "https://my-function-app.azurewebsites.net/api/webhook", Code = "your-function-key-here", }, }); }); ``` *** ## Finding Your Function Key Sign in to the [Azure Portal](https://portal.azure.com). Search for and select **Function App**, then select your function app from the list. In the left menu, expand **Functions**, then select **App keys** for host-level keys. Alternatively, click on a specific function and select **Function Keys** for function-specific keys. Copy the **default** key or create a new one for use in your Control Plane secret. Use function-level keys when possible to limit access to specific functions. Use host keys only when you need to access multiple functions. *** ## URL Format Azure Function URLs follow this pattern: ```text theme={null} https://.azurewebsites.net/api/ ``` For custom domains: ```text theme={null} https://api.example.com/api/ ``` *** ## Next Steps Authenticate with Azure services using SDK Learn how to grant access and inject secrets # Azure SDK Source: https://docs.controlplane.com/guides/create-secret/azure-sdk Create an Azure SDK secret to store service principal credentials for authenticating workloads with Azure services. Azure SDK secrets store Azure service principal credentials, enabling your workloads to authenticate with Azure services using the Azure SDK. Use them for accessing Azure Storage, Key Vault, Cosmos DB, or any Azure resource. ## Use Cases * **Azure Storage**: Access Blob Storage, Files, Queues, or Tables * **Azure Key Vault**: Retrieve secrets, keys, and certificates * **Azure Cosmos DB**: Connect to Cosmos DB databases * **Azure Service Bus**: Send and receive messages * **Any Azure Service**: Authenticate with Azure Resource Manager APIs ## Configuration Options | Field | Description | Required | | :--------------- | :-------------------------------------- | :------- | | `subscriptionId` | Azure subscription ID (GUID) | Yes | | `tenantId` | Azure AD tenant ID (GUID) | Yes | | `clientId` | Service principal application ID (GUID) | Yes | | `clientSecret` | Service principal client secret | Yes | Create a service principal using the Azure CLI: `az ad sp create-for-rbac --name "my-app" --role contributor --scopes /subscriptions/{subscription-id}` If you don't have the Azure CLI installed locally, you can run the command in [Azure Cloud Shell](https://shell.azure.com/bash) — a browser-based shell with `az` preinstalled and authenticated against your signed-in account. *** ## Create an Azure SDK Secret In the Console, navigate to **Secrets** and click **New**, or use the **Create** dropdown in the top-right corner and select **Secret**. Enter a **Name** and optional **Description**. Select **Azure-SDK** as the secret type. Click **Data** in the left pane. Enter the **Azure SDK Secret Data** (JSON format), or drag and drop a file / click to import. Click **Create**. Create a file named `azure-credentials.json`: ```json theme={null} { "subscriptionId": "00000000-0000-0000-0000-000000000000", "tenantId": "00000000-0000-0000-0000-000000000000", "clientId": "00000000-0000-0000-0000-000000000000", "clientSecret": "your-client-secret-value" } ``` Then create the secret: ```bash theme={null} cpln secret create-azure-sdk \ --name azure-sdk-credentials \ --file azure-credentials.json \ --org my-org ``` ```hcl theme={null} resource "cpln_secret" "azure_sdk" { name = "azure-sdk-credentials" description = "Azure service principal credentials" azure_sdk = jsonencode({ subscriptionId = "00000000-0000-0000-0000-000000000000" tenantId = "00000000-0000-0000-0000-000000000000" clientId = "00000000-0000-0000-0000-000000000000" clientSecret = "your-client-secret-value" }) } ``` This example uses placeholder credentials for testing. In production, use Terraform variables or integrate with Azure Key Vault. ```typescript theme={null} import * as cpln from "@pulumiverse/cpln"; const azureSdkSecret = new cpln.Secret("azure-sdk-credentials", { name: "azure-sdk-credentials", description: "Azure service principal credentials", azureSdk: JSON.stringify({ subscriptionId: "00000000-0000-0000-0000-000000000000", tenantId: "00000000-0000-0000-0000-000000000000", clientId: "00000000-0000-0000-0000-000000000000", clientSecret: "your-client-secret-value", }), }); ``` ```python theme={null} import json import pulumiverse_cpln as cpln azure_sdk_secret = cpln.Secret("azure-sdk-credentials", name="azure-sdk-credentials", description="Azure service principal credentials", azure_sdk=json.dumps({ "subscriptionId": "00000000-0000-0000-0000-000000000000", "tenantId": "00000000-0000-0000-0000-000000000000", "clientId": "00000000-0000-0000-0000-000000000000", "clientSecret": "your-client-secret-value", })) ``` ```go theme={null} package main import ( "encoding/json" "github.com/pulumi/pulumi/sdk/v3/go/pulumi" "github.com/pulumiverse/pulumi-cpln/sdk/go/cpln" ) func main() { pulumi.Run(func(ctx *pulumi.Context) error { azureConfig, _ := json.Marshal(map[string]string{ "subscriptionId": "00000000-0000-0000-0000-000000000000", "tenantId": "00000000-0000-0000-0000-000000000000", "clientId": "00000000-0000-0000-0000-000000000000", "clientSecret": "your-client-secret-value", }) _, err := cpln.NewSecret(ctx, "azure-sdk-credentials", &cpln.SecretArgs{ Name: pulumi.String("azure-sdk-credentials"), Description: pulumi.String("Azure service principal credentials"), AzureSdk: pulumi.String(string(azureConfig)), }) return err }) } ``` ```csharp theme={null} using System.Text.Json; using Pulumi; using Pulumiverse.Cpln; using Pulumiverse.Cpln.Inputs; return await Deployment.RunAsync(() => { var azureConfig = JsonSerializer.Serialize(new { subscriptionId = "00000000-0000-0000-0000-000000000000", tenantId = "00000000-0000-0000-0000-000000000000", clientId = "00000000-0000-0000-0000-000000000000", clientSecret = "your-client-secret-value", }); var azureSdkSecret = new Secret("azure-sdk-credentials", new SecretArgs { Name = "azure-sdk-credentials", Description = "Azure service principal credentials", AzureSdk = azureConfig, }); }); ``` *** ## Creating a Service Principal Use the Azure CLI to create a service principal. If you don't have the Azure CLI installed locally, you can run the command in [Azure Cloud Shell](https://shell.azure.com/bash) without a local install: ```bash theme={null} # Create service principal with Contributor role az ad sp create-for-rbac \ --name "my-control-plane-app" \ --role contributor \ --scopes /subscriptions/{subscription-id} # Output includes: # { # "appId": "00000000-0000-0000-0000-000000000000", <- clientId # "displayName": "my-control-plane-app", # "password": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", <- clientSecret # "tenant": "00000000-0000-0000-0000-000000000000" <- tenantId # } ``` Use the principle of least privilege. Assign only the roles your workload needs, scoped to specific resources when possible. *** ## Next Steps Learn how to grant access and inject secrets Connect to Azure Function Apps # Dictionary Source: https://docs.controlplane.com/guides/create-secret/dictionary Create a dictionary secret to store multiple key-value pairs in a single secret, ideal for grouping related configuration values. Dictionary secrets store multiple key-value pairs in a single secret, making them ideal for grouping related configuration values. Each key can be accessed individually when injecting into workloads. ## Use Cases * **Database Configuration**: Store host, port, username, and database name together * **API Configuration**: Group related API settings (endpoint, version, timeout) * **Feature Flags**: Store multiple feature toggles in one secret * **Environment Variables**: Bundle related environment variables * **Application Settings**: Group configuration that belongs together logically Use dictionary secrets when you have multiple related values that you want to manage as a single unit but access individually in your workload. ## Configuration Options Dictionary secrets store key-value pairs where: | Component | Description | | :-------- | :--------------------------------------------------------- | | Key | The identifier for each value (e.g., `DB_HOST`, `DB_PORT`) | | Value | The secret value associated with the key | All values in a dictionary secret are stored as strings. If you need to store JSON or complex data, use an [Opaque secret](/guides/create-secret/opaque) instead. *** ## Create a Dictionary Secret In the Console, navigate to **Secrets** and click **New**, or use the **Create** dropdown in the top-right corner and select **Secret**. Enter a **Name** and optional **Description**. Select **Dictionary** as the secret type. Click **Data** in the left pane. Click **Add** to add key-value pairs, or drag and drop a file / click to import. Click **Create**. ```bash theme={null} cpln secret create-dictionary \ --name database-config \ --entry DB_HOST=db.example.com \ --entry DB_PORT=5432 \ --entry DB_NAME=myapp_production \ --entry DB_SSL_MODE=require \ --org my-org ``` ```hcl theme={null} resource "cpln_secret" "database_config" { name = "database-config" description = "Database connection configuration" dictionary = { DB_HOST = "db.example.com" DB_PORT = "5432" DB_NAME = "myapp_production" DB_SSL_MODE = "require" } } ``` Access individual keys using the `dictionary_as_envs` output: ```hcl theme={null} # Reference in workload environment variables resource "cpln_workload" "my_app" { # ... other config ... container { name = "main" image = "my-app:latest" env = { DB_HOST = cpln_secret.database_config.dictionary_as_envs["DB_HOST"] DB_PORT = cpln_secret.database_config.dictionary_as_envs["DB_PORT"] } } } ``` ```typescript theme={null} import * as cpln from "@pulumiverse/cpln"; const databaseConfig = new cpln.Secret("database-config", { name: "database-config", description: "Database connection configuration", dictionary: { DB_HOST: "db.example.com", DB_PORT: "5432", DB_NAME: "myapp_production", DB_SSL_MODE: "require", }, }); // Export for use in workloads export const dbHostEnv = databaseConfig.dictionaryAsEnvs.apply( envs => envs["DB_HOST"] ); ``` ```python theme={null} import pulumiverse_cpln as cpln database_config = cpln.Secret("database-config", name="database-config", description="Database connection configuration", dictionary={ "DB_HOST": "db.example.com", "DB_PORT": "5432", "DB_NAME": "myapp_production", "DB_SSL_MODE": "require", }) # Export for use in workloads pulumi.export("db_host_env", database_config.dictionary_as_envs["DB_HOST"]) ``` ```go theme={null} package main import ( "github.com/pulumi/pulumi/sdk/v3/go/pulumi" "github.com/pulumiverse/pulumi-cpln/sdk/go/cpln" ) func main() { pulumi.Run(func(ctx *pulumi.Context) error { databaseConfig, err := cpln.NewSecret(ctx, "database-config", &cpln.SecretArgs{ Name: pulumi.String("database-config"), Description: pulumi.String("Database connection configuration"), Dictionary: pulumi.StringMap{ "DB_HOST": pulumi.String("db.example.com"), "DB_PORT": pulumi.String("5432"), "DB_NAME": pulumi.String("myapp_production"), "DB_SSL_MODE": pulumi.String("require"), }, }) if err != nil { return err } ctx.Export("secretLink", databaseConfig.SecretLink) return nil }) } ``` ```csharp theme={null} using System.Collections.Generic; using Pulumi; using Pulumiverse.Cpln; using Pulumiverse.Cpln.Inputs; return await Deployment.RunAsync(() => { var databaseConfig = new Secret("database-config", new SecretArgs { Name = "database-config", Description = "Database connection configuration", Dictionary = { { "DB_HOST", "db.example.com" }, { "DB_PORT", "5432" }, { "DB_NAME", "myapp_production" }, { "DB_SSL_MODE", "require" }, }, }); return new Dictionary { ["secretLink"] = databaseConfig.SecretLink, }; }); ``` *** ## Injecting into Workloads ### Individual Keys as Environment Variables Reference specific keys from the dictionary: ```yaml theme={null} env: - name: DATABASE_HOST value: "cpln://secret/database-config.DB_HOST" - name: DATABASE_PORT value: "cpln://secret/database-config.DB_PORT" - name: DATABASE_NAME value: "cpln://secret/database-config.DB_NAME" ``` ### Mount as Directory When mounted as a volume, each key becomes a file: ```yaml theme={null} volumes: - uri: "cpln://secret/database-config" path: /etc/config ``` This creates: * `/etc/config/DB_HOST` containing `db.example.com` * `/etc/config/DB_PORT` containing `5432` * `/etc/config/DB_NAME` containing `myapp_production` * `/etc/config/DB_SSL_MODE` containing `require` *** ## Common Patterns ### Database Configuration ```yaml theme={null} kind: secret name: postgres-config type: dictionary data: PGHOST: db.example.com PGPORT: "5432" PGDATABASE: myapp PGUSER: app_user PGSSLMODE: require ``` ### Redis Configuration ```yaml theme={null} kind: secret name: redis-config type: dictionary data: REDIS_HOST: redis.example.com REDIS_PORT: "6379" REDIS_DB: "0" REDIS_TLS: "true" ``` ### External API Settings ```yaml theme={null} kind: secret name: stripe-config type: dictionary data: STRIPE_API_VERSION: "2023-10-16" STRIPE_WEBHOOK_TOLERANCE: "300" STRIPE_MAX_RETRIES: "3" ``` *** ## Next Steps Learn how to grant access and inject secrets Store single values or complex data # Docker Source: https://docs.controlplane.com/guides/create-secret/docker Create a Docker secret to store container registry credentials for pulling images from private registries like Docker Hub, GHCR, or ACR. Docker secrets store container registry credentials, enabling your workloads to pull images from private registries like Docker Hub, GitHub Container Registry, Azure Container Registry, or any Docker-compatible registry. ## Use Cases * **Private Docker Hub**: Pull images from private Docker Hub repositories * **GitHub Container Registry**: Access private packages hosted on GHCR * **Azure Container Registry**: Pull images from ACR * **Google Artifact Registry**: Access private Google container images * **Self-Hosted Registries**: Connect to private/enterprise registries * **Cross-Org Images**: Pull images from another Control Plane org Images stored in your current Control Plane org don't require a pull secret. Reference them directly using the `//image/IMAGE:TAG` format. ## Configuration Options The Docker secret uses the standard Docker config.json format: ```json theme={null} { "auths": { "": { "username": "", "password": "" } } } ``` You can include credentials for multiple registries in a single secret. *** ## Create a Docker Secret In the Console, navigate to **Secrets** and click **New**, or use the **Create** dropdown in the top-right corner and select **Secret**. Enter a **Name** and optional **Description**. Select **Docker** as the secret type. Click **Data** in the left pane. Enter the **Docker Secret Data** (JSON format), or drag and drop a file / click to import. Click **Create**. **Option 1:** Use your existing Docker config file: ```bash theme={null} cpln secret create-docker \ --name dockerhub-credentials \ --file ~/.docker/config.json \ --org my-org ``` **Option 2:** Create a dedicated config file: ```json title="docker-config.json" theme={null} { "auths": { "https://index.docker.io/v1/": { "username": "my-username", "password": "my-access-token" } } } ``` ```bash theme={null} cpln secret create-docker \ --name dockerhub-credentials \ --file docker-config.json \ --org my-org ``` For Docker Hub, use a [Personal Access Token](https://hub.docker.com/settings/security) instead of your password for better security and easier revocation. Docker Hub example: ```hcl theme={null} resource "cpln_secret" "dockerhub" { name = "dockerhub-credentials" description = "Docker Hub private registry access" docker = jsonencode({ auths = { "https://index.docker.io/v1/" = { username = "my-username" password = "my-access-token" } } }) } ``` GitHub Container Registry example: ```hcl theme={null} resource "cpln_secret" "ghcr" { name = "ghcr-credentials" description = "GitHub Container Registry access" docker = jsonencode({ auths = { "ghcr.io" = { username = "my-github-username" password = "ghp_xxxxxxxxxxxx" # PAT with read:packages scope } } }) } ``` Multiple registries in one secret: ```hcl theme={null} resource "cpln_secret" "multi_registry" { name = "multi-registry-credentials" description = "Access to multiple container registries" docker = jsonencode({ auths = { "https://index.docker.io/v1/" = { username = "my-username" password = "my-access-token" } "ghcr.io" = { username = "my-github-username" password = "ghp_xxxxxxxxxxxx" } } }) } ``` ```typescript theme={null} import * as cpln from "@pulumiverse/cpln"; const dockerSecret = new cpln.Secret("dockerhub-credentials", { name: "dockerhub-credentials", description: "Docker Hub private registry access", docker: JSON.stringify({ auths: { "https://index.docker.io/v1/": { username: "my-username", password: "my-access-token", }, }, }), }); ``` ```python theme={null} import json import pulumiverse_cpln as cpln docker_secret = cpln.Secret("dockerhub-credentials", name="dockerhub-credentials", description="Docker Hub private registry access", docker=json.dumps({ "auths": { "https://index.docker.io/v1/": { "username": "my-username", "password": "my-access-token", } } })) ``` ```go theme={null} package main import ( "encoding/json" "github.com/pulumi/pulumi/sdk/v3/go/pulumi" "github.com/pulumiverse/pulumi-cpln/sdk/go/cpln" ) func main() { pulumi.Run(func(ctx *pulumi.Context) error { dockerConfig, _ := json.Marshal(map[string]interface{}{ "auths": map[string]interface{}{ "https://index.docker.io/v1/": map[string]string{ "username": "my-username", "password": "my-access-token", }, }, }) _, err := cpln.NewSecret(ctx, "dockerhub-credentials", &cpln.SecretArgs{ Name: pulumi.String("dockerhub-credentials"), Description: pulumi.String("Docker Hub private registry access"), Docker: pulumi.String(string(dockerConfig)), }) return err }) } ``` ```csharp theme={null} using System.Collections.Generic; using System.Text.Json; using Pulumi; using Pulumiverse.Cpln; using Pulumiverse.Cpln.Inputs; return await Deployment.RunAsync(() => { var dockerConfig = JsonSerializer.Serialize(new { auths = new Dictionary { ["https://index.docker.io/v1/"] = new { username = "my-username", password = "my-access-token", } } }); var dockerSecret = new Secret("dockerhub-credentials", new SecretArgs { Name = "dockerhub-credentials", Description = "Docker Hub private registry access", Docker = dockerConfig, }); }); ``` *** ## Common Registry URLs | Registry | URL | Notes | | :------------------------ | :------------------------------------------ | :------------------------------ | | Docker Hub | `https://index.docker.io/v1/` | Use access token, not password | | GitHub Container Registry | `ghcr.io` | PAT with `read:packages` scope | | Azure Container Registry | `.azurecr.io` | Service principal or admin user | | Google Artifact Registry | `-docker.pkg.dev` | Service account JSON key | | AWS ECR | Use [ECR secret](/guides/create-secret/ecr) | Dedicated secret type available | | Control Plane (other org) | `.registry.cpln.io` | Service account token | *** ## Using the Secret After creating the Docker secret, add it as a **pull secret** to your GVC: Open your GVC in the Console. Click **Pull Secrets** in the left pane. Click **Add** and select your Docker secret. Click **Save**. ```bash theme={null} cpln gvc update my-gvc \ --set spec.pullSecretLinks+="dockerhub-credentials" \ --org my-org ``` ```hcl theme={null} resource "cpln_gvc" "my_gvc" { name = "my-gvc" pull_secrets = [ cpln_secret.dockerhub.name ] } ``` ```typescript theme={null} import * as cpln from "@pulumiverse/cpln"; const gvc = new cpln.Gvc("my-gvc", { name: "my-gvc", pullSecrets: [dockerSecret.name], }); ``` ```python theme={null} import pulumiverse_cpln as cpln gvc = cpln.Gvc("my-gvc", name="my-gvc", pull_secrets=[docker_secret.name]) ``` ```go theme={null} gvc, err := cpln.NewGvc(ctx, "my-gvc", &cpln.GvcArgs{ Name: pulumi.String("my-gvc"), PullSecrets: pulumi.StringArray{dockerSecret.Name}, }) ``` ```csharp theme={null} var gvc = new Gvc("my-gvc", new GvcArgs { Name = "my-gvc", PullSecrets = new[] { dockerSecret.Name }, }); ``` *** ## Next Steps Configure pull secrets at the GVC level Use the dedicated ECR secret type for AWS # ECR Source: https://docs.controlplane.com/guides/create-secret/ecr Create an ECR secret to pull container images from AWS Elastic Container Registry with automatic token refresh and cross-account support. ECR secrets provide credentials for pulling container images from AWS Elastic Container Registry. Unlike generic Docker secrets, ECR secrets handle AWS's token-based authentication automatically, including cross-account access via IAM role assumption. ## Use Cases * **Private ECR Repositories**: Pull images from your AWS ECR repositories * **Cross-Account Access**: Access ECR in different AWS accounts * **Multi-Region Deployments**: Pull images from ECR across AWS regions * **CI/CD Pipelines**: Authenticate build systems with ECR Use ECR secrets instead of Docker secrets for AWS ECR. They handle AWS token refresh automatically and support IAM role assumption for cross-account access. ## Configuration Options | Field | Description | Required | | :----------- | :------------------------------------ | :------- | | `accessKey` | AWS Access Key ID | Yes | | `secretKey` | AWS Secret Access Key | Yes | | `repos` | List of ECR repository URIs to access | Yes | | `roleArn` | IAM Role ARN for cross-account access | No | | `externalId` | External ID for role assumption | No | *** ## Create an ECR Secret In the Console, navigate to **Secrets** and click **New**, or use the **Create** dropdown in the top-right corner and select **Secret**. Enter a **Name** and optional **Description**. Select **ECR** as the secret type. Click **Data** in the left pane. Enter the **Access Key** and **Secret Key**. Click **Add** under **Repos** to add repository URIs. Optionally enter a **Role ARN** and **External ID** for cross-account access. Click **Create**. ```bash theme={null} cpln secret create-ecr \ --name ecr-pull-secret \ --access-key AKIAIOSFODNN7EXAMPLE \ --secret-key wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY \ --repo 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app \ --repo 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-api \ --org my-org ``` With cross-account role assumption: ```bash theme={null} cpln secret create-ecr \ --name ecr-cross-account \ --access-key AKIAIOSFODNN7EXAMPLE \ --secret-key wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY \ --repo 987654321098.dkr.ecr.us-west-2.amazonaws.com/shared-images \ --role-arn arn:aws:iam::987654321098:role/ECRPullRole \ --external-id my-external-id \ --org my-org ``` ```hcl theme={null} resource "cpln_secret" "ecr" { name = "ecr-pull-secret" description = "ECR repository access" ecr { access_key = "AKIAIOSFODNN7EXAMPLE" secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" repos = [ "123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app", "123456789012.dkr.ecr.us-east-1.amazonaws.com/my-api" ] } } ``` Cross-account access: ```hcl theme={null} resource "cpln_secret" "ecr_cross_account" { name = "ecr-cross-account" description = "Cross-account ECR access" ecr { access_key = "AKIAIOSFODNN7EXAMPLE" secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" repos = ["987654321098.dkr.ecr.us-west-2.amazonaws.com/shared-images"] role_arn = "arn:aws:iam::987654321098:role/ECRPullRole" external_id = "my-external-id" } } ``` This example uses hardcoded credentials for testing. In production, use Terraform variables or a secrets manager. ```typescript theme={null} import * as cpln from "@pulumiverse/cpln"; const ecrSecret = new cpln.Secret("ecr-pull-secret", { name: "ecr-pull-secret", description: "ECR repository access", ecr: { accessKey: "AKIAIOSFODNN7EXAMPLE", secretKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", repos: [ "123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app", "123456789012.dkr.ecr.us-east-1.amazonaws.com/my-api", ], }, }); ``` ```python theme={null} import pulumiverse_cpln as cpln ecr_secret = cpln.Secret("ecr-pull-secret", name="ecr-pull-secret", description="ECR repository access", ecr={ "access_key": "AKIAIOSFODNN7EXAMPLE", "secret_key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", "repos": [ "123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app", "123456789012.dkr.ecr.us-east-1.amazonaws.com/my-api", ], }) ``` ```go theme={null} package main import ( "github.com/pulumi/pulumi/sdk/v3/go/pulumi" "github.com/pulumiverse/pulumi-cpln/sdk/go/cpln" ) func main() { pulumi.Run(func(ctx *pulumi.Context) error { _, err := cpln.NewSecret(ctx, "ecr-pull-secret", &cpln.SecretArgs{ Name: pulumi.String("ecr-pull-secret"), Description: pulumi.String("ECR repository access"), Ecr: &cpln.SecretEcrArgs{ AccessKey: pulumi.String("AKIAIOSFODNN7EXAMPLE"), SecretKey: pulumi.String("wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"), Repos: pulumi.StringArray{ pulumi.String("123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app"), pulumi.String("123456789012.dkr.ecr.us-east-1.amazonaws.com/my-api"), }, }, }) return err }) } ``` ```csharp theme={null} using Pulumi; using Pulumiverse.Cpln; using Pulumiverse.Cpln.Inputs; return await Deployment.RunAsync(() => { var ecrSecret = new Secret("ecr-pull-secret", new SecretArgs { Name = "ecr-pull-secret", Description = "ECR repository access", Ecr = new SecretEcrArgs { AccessKey = "AKIAIOSFODNN7EXAMPLE", SecretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", Repos = { "123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app", "123456789012.dkr.ecr.us-east-1.amazonaws.com/my-api", }, }, }); }); ``` *** ## ECR Repository URL Format ECR repository URLs follow this pattern: ```text theme={null} .dkr.ecr..amazonaws.com/ ``` Example: ```text theme={null} 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-application ``` Include the full repository path, not just the registry URL. Each repository you need to access should be listed separately. *** ## Using the Secret After creating the ECR secret, add it as a **pull secret** to your GVC: Open your GVC in the Console. Click **Pull Secrets** in the left pane. Click **Add** and select your ECR secret. Click **Save**. ```bash theme={null} cpln gvc update my-gvc \ --set spec.pullSecretLinks+="ecr-pull-secret" \ --org my-org ``` ```hcl theme={null} resource "cpln_gvc" "my_gvc" { name = "my-gvc" pull_secrets = [ cpln_secret.ecr.name ] } ``` ```typescript theme={null} import * as cpln from "@pulumiverse/cpln"; const gvc = new cpln.Gvc("my-gvc", { name: "my-gvc", pullSecrets: [ecrSecret.name], }); ``` ```python theme={null} import pulumiverse_cpln as cpln gvc = cpln.Gvc("my-gvc", name="my-gvc", pull_secrets=[ecr_secret.name]) ``` ```go theme={null} gvc, err := cpln.NewGvc(ctx, "my-gvc", &cpln.GvcArgs{ Name: pulumi.String("my-gvc"), PullSecrets: pulumi.StringArray{ecrSecret.Name}, }) ``` ```csharp theme={null} var gvc = new Gvc("my-gvc", new GvcArgs { Name = "my-gvc", PullSecrets = new[] { ecrSecret.Name }, }); ``` *** ## Next Steps Configure pull secrets at the GVC level Set up AWS cloud account integration # GCP Source: https://docs.controlplane.com/guides/create-secret/gcp Create a GCP secret to store Google Cloud service account credentials for authenticating with GCP services or pulling images from Artifact Registry. GCP secrets store Google Cloud service account credentials, enabling your workloads to authenticate with Google Cloud services or pull images from Google Artifact Registry. ## Use Cases * **Artifact Registry**: Pull container images from Google Artifact Registry * **Cloud Storage**: Access files in Google Cloud Storage buckets * **BigQuery**: Query data from BigQuery datasets * **Pub/Sub**: Publish and subscribe to Pub/Sub topics * **Any GCP Service**: Authenticate with any Google Cloud API For Google Artifact Registry, you can also use a [Docker secret](/guides/create-secret/docker) with base64-encoded service account credentials. The GCP secret type is recommended for direct GCP API access. ## Configuration Options The GCP secret stores a complete service account JSON key file: | Field | Description | | :--------------- | :------------------------------------ | | `type` | Always `service_account` | | `project_id` | Your GCP project ID | | `private_key_id` | Unique identifier for the private key | | `private_key` | RSA private key in PEM format | | `client_email` | Service account email address | | `client_id` | Numeric client ID | | `auth_uri` | OAuth2 authorization endpoint | | `token_uri` | OAuth2 token endpoint | *** ## Create a GCP Secret In the Console, navigate to **Secrets** and click **New**, or use the **Create** dropdown in the top-right corner and select **Secret**. Enter a **Name** and optional **Description**. Select **GCP** as the secret type. Click **Data** in the left pane. Drag and drop your service account JSON file, or click to import. Click **Create**. Download your service account key from the Google Cloud Console, then create the secret: ```bash theme={null} cpln secret create-gcp \ --name gcp-service-account \ --file service-account.json \ --org my-org ``` Generate service account keys from the [Google Cloud Console](https://console.cloud.google.com/iam-admin/serviceaccounts) under **IAM & Admin > Service Accounts > Keys**. ```hcl theme={null} resource "cpln_secret" "gcp" { name = "gcp-service-account" description = "GCP service account for Cloud Storage access" gcp = jsonencode({ type = "service_account" project_id = "my-project-123" private_key_id = "abc123def456" private_key = "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n" client_email = "my-service-account@my-project-123.iam.gserviceaccount.com" client_id = "123456789012345678901" auth_uri = "https://accounts.google.com/o/oauth2/auth" token_uri = "https://oauth2.googleapis.com/token" auth_provider_x509_cert_url = "https://www.googleapis.com/oauth2/v1/certs" client_x509_cert_url = "https://www.googleapis.com/robot/v1/metadata/x509/my-service-account%40my-project-123.iam.gserviceaccount.com" }) } ``` This example uses placeholder credentials for testing. In production, use `file("service-account.json")` or Terraform variables. ```typescript theme={null} import * as cpln from "@pulumiverse/cpln"; const gcpSecret = new cpln.Secret("gcp-service-account", { name: "gcp-service-account", description: "GCP service account credentials", gcp: JSON.stringify({ type: "service_account", project_id: "my-project-123", private_key_id: "abc123def456", private_key: "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n", client_email: "my-service-account@my-project-123.iam.gserviceaccount.com", client_id: "123456789012345678901", auth_uri: "https://accounts.google.com/o/oauth2/auth", token_uri: "https://oauth2.googleapis.com/token", }), }); ``` ```python theme={null} import json import pulumiverse_cpln as cpln gcp_secret = cpln.Secret("gcp-service-account", name="gcp-service-account", description="GCP service account credentials", gcp=json.dumps({ "type": "service_account", "project_id": "my-project-123", "private_key_id": "abc123def456", "private_key": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n", "client_email": "my-service-account@my-project-123.iam.gserviceaccount.com", "client_id": "123456789012345678901", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token", })) ``` ```go theme={null} package main import ( "encoding/json" "github.com/pulumi/pulumi/sdk/v3/go/pulumi" "github.com/pulumiverse/pulumi-cpln/sdk/go/cpln" ) func main() { pulumi.Run(func(ctx *pulumi.Context) error { gcpConfig, _ := json.Marshal(map[string]string{ "type": "service_account", "project_id": "my-project-123", "private_key_id": "abc123def456", "private_key": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n", "client_email": "my-service-account@my-project-123.iam.gserviceaccount.com", "client_id": "123456789012345678901", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token", }) _, err := cpln.NewSecret(ctx, "gcp-service-account", &cpln.SecretArgs{ Name: pulumi.String("gcp-service-account"), Description: pulumi.String("GCP service account credentials"), Gcp: pulumi.String(string(gcpConfig)), }) return err }) } ``` ```csharp theme={null} using System.Text.Json; using Pulumi; using Pulumiverse.Cpln; using Pulumiverse.Cpln.Inputs; return await Deployment.RunAsync(() => { var gcpConfig = JsonSerializer.Serialize(new { type = "service_account", project_id = "my-project-123", private_key_id = "abc123def456", private_key = "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n", client_email = "my-service-account@my-project-123.iam.gserviceaccount.com", client_id = "123456789012345678901", auth_uri = "https://accounts.google.com/o/oauth2/auth", token_uri = "https://oauth2.googleapis.com/token", }); var gcpSecret = new Secret("gcp-service-account", new SecretArgs { Name = "gcp-service-account", Description = "GCP service account credentials", Gcp = gcpConfig, }); }); ``` *** ## Best Practices Create dedicated service accounts with only the IAM roles your workload needs. Avoid using default service accounts or overly permissive roles. Set up a key rotation schedule. Delete old keys after deploying new ones to Control Plane. For GKE deployments, consider using [Workload Identity](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity) instead of service account keys. *** ## Using for Artifact Registry To use a GCP secret for pulling images from Artifact Registry, add it as a **pull secret** to your GVC: Open your GVC in the Console. Click **Pull Secrets** in the left pane. Click **Add** and select your GCP secret. Click **Save**. ```bash theme={null} cpln gvc update my-gvc \ --set spec.pullSecretLinks+="gcp-service-account" \ --org my-org ``` ```hcl theme={null} resource "cpln_gvc" "my_gvc" { name = "my-gvc" pull_secrets = [ cpln_secret.gcp.name ] } ``` ```typescript theme={null} import * as cpln from "@pulumiverse/cpln"; const gvc = new cpln.Gvc("my-gvc", { name: "my-gvc", pullSecrets: [gcpSecret.name], }); ``` ```python theme={null} import pulumiverse_cpln as cpln gvc = cpln.Gvc("my-gvc", name="my-gvc", pull_secrets=[gcp_secret.name]) ``` ```go theme={null} gvc, err := cpln.NewGvc(ctx, "my-gvc", &cpln.GvcArgs{ Name: pulumi.String("my-gvc"), PullSecrets: pulumi.StringArray{gcpSecret.Name}, }) ``` ```csharp theme={null} var gvc = new Gvc("my-gvc", new GvcArgs { Name = "my-gvc", PullSecrets = new[] { gcpSecret.Name }, }); ``` The service account must have the `roles/artifactregistry.reader` role on the Artifact Registry repository. *** ## Next Steps Learn how to grant access and inject secrets Set up GCP cloud account integration # Keypair Source: https://docs.controlplane.com/guides/create-secret/keypair Create a keypair secret to store asymmetric public and private keys for SSH authentication, JWT signing, or encryption. Keypair secrets store asymmetric key pairs (public and private keys) with optional passphrase protection. Use them for SSH authentication, JWT signing, encryption, or any cryptographic operation requiring public-key infrastructure. ## Use Cases * **SSH Authentication**: Store SSH keys for accessing remote servers or Git repositories * **JWT Signing**: Sign and verify JSON Web Tokens for API authentication * **Encryption/Decryption**: Encrypt data with the public key, decrypt with the private key * **Code Signing**: Sign artifacts to verify authenticity and integrity * **Secure Communication**: Establish encrypted channels between services ## Configuration Options | Field | Description | Required | | :----------- | :------------------------------------ | :------- | | `secretKey` | Private key in PEM format | Yes | | `publicKey` | Public key in PEM format | Yes | | `passphrase` | Passphrase for encrypted private keys | No | Both keys must be PEM-encoded. The private key can be encrypted with a passphrase—if so, provide the passphrase so your workload can use the key. *** ## Create a Keypair Secret In the Console, navigate to **Secrets** and click **New**, or use the **Create** dropdown in the top-right corner and select **Secret**. Enter a **Name** and optional **Description**. Select **Keypair** as the secret type. Click **Data** in the left pane. For **Secret Key**, drag and drop your private key file or click to import. For **Public Key**, drag and drop your public key file or click to import. Enter the **Passphrase** if the private key is encrypted (optional). Click **Create**. ```bash theme={null} cpln secret create-keypair \ --name my-keypair \ --secret private_key.pem \ --public public_key.pem \ --passphrase 'my-passphrase' \ --org my-org ``` Without a passphrase (for unencrypted private keys): ```bash theme={null} cpln secret create-keypair \ --name my-keypair \ --secret private_key.pem \ --public public_key.pem \ --org my-org ``` ```hcl theme={null} resource "cpln_secret" "keypair" { name = "my-keypair" description = "RSA keypair for JWT signing" keypair { secret_key = file("private_key.pem") public_key = file("public_key.pem") passphrase = "my-passphrase" # Optional } } ``` This example uses a hardcoded passphrase for testing. In production, use Terraform variables or integrate with a secrets manager. Never commit private keys to version control. ```typescript theme={null} import * as cpln from "@pulumiverse/cpln"; import * as fs from "fs"; const keypairSecret = new cpln.Secret("my-keypair", { name: "my-keypair", description: "RSA keypair for JWT signing", keypair: { secretKey: fs.readFileSync("private_key.pem", "utf8"), publicKey: fs.readFileSync("public_key.pem", "utf8"), passphrase: "my-passphrase", // Optional }, }); ``` ```python theme={null} import pulumiverse_cpln as cpln with open("private_key.pem") as f: secret_key = f.read() with open("public_key.pem") as f: public_key = f.read() keypair_secret = cpln.Secret("my-keypair", name="my-keypair", description="RSA keypair for JWT signing", keypair={ "secret_key": secret_key, "public_key": public_key, "passphrase": "my-passphrase", # Optional }) ``` ```go theme={null} package main import ( "os" "github.com/pulumi/pulumi/sdk/v3/go/pulumi" "github.com/pulumiverse/pulumi-cpln/sdk/go/cpln" ) func main() { pulumi.Run(func(ctx *pulumi.Context) error { secretKey, _ := os.ReadFile("private_key.pem") publicKey, _ := os.ReadFile("public_key.pem") _, err := cpln.NewSecret(ctx, "my-keypair", &cpln.SecretArgs{ Name: pulumi.String("my-keypair"), Description: pulumi.String("RSA keypair for JWT signing"), Keypair: &cpln.SecretKeypairArgs{ SecretKey: pulumi.String(string(secretKey)), PublicKey: pulumi.String(string(publicKey)), Passphrase: pulumi.String("my-passphrase"), // Optional }, }) return err }) } ``` ```csharp theme={null} using System.IO; using Pulumi; using Pulumiverse.Cpln; using Pulumiverse.Cpln.Inputs; return await Deployment.RunAsync(() => { var keypairSecret = new Secret("my-keypair", new SecretArgs { Name = "my-keypair", Description = "RSA keypair for JWT signing", Keypair = new SecretKeypairArgs { SecretKey = File.ReadAllText("private_key.pem"), PublicKey = File.ReadAllText("public_key.pem"), Passphrase = "my-passphrase", // Optional }, }); }); ``` *** ## Generating Key Pairs ### RSA Keys ```bash theme={null} # Generate a 4096-bit RSA private key openssl genrsa -out private_key.pem 4096 # Extract the public key openssl rsa -in private_key.pem -pubout -out public_key.pem # With passphrase protection openssl genrsa -aes256 -out private_key.pem 4096 ``` ### Ed25519 Keys (for SSH) ```bash theme={null} # Generate an Ed25519 keypair ssh-keygen -t ed25519 -f my_key -C "my-workload" # Convert to PEM format if needed ssh-keygen -p -m PEM -f my_key ``` ### ECDSA Keys ```bash theme={null} # Generate an ECDSA private key (P-256 curve) openssl ecparam -genkey -name prime256v1 -out private_key.pem # Extract the public key openssl ec -in private_key.pem -pubout -out public_key.pem ``` For JWT signing, RSA with 2048 or 4096 bits and ECDSA P-256 are common choices. Ed25519 offers excellent security with smaller key sizes. *** ## Best Practices For RSA, use at least 2048 bits (4096 recommended). For ECDSA, use P-256 or higher curves. Always encrypt private keys with a strong passphrase when storing them, adding an extra layer of protection. Establish a key rotation schedule. When rotating, update the secret and ensure workloads can handle the transition. Use Control Plane policies to restrict which workloads can access the keypair. Only services that need to sign or decrypt should have access. *** ## Next Steps Store SSL/TLS certificates and keys Learn how to grant access and inject secrets # NATS Account Source: https://docs.controlplane.com/guides/create-secret/nats-account Create a NATS Account secret to store NKey-based credentials for authenticating workloads with NATS clusters or NGS. NATS Account secrets store credentials for authenticating with NATS messaging systems, including [NGS (NATS Global Service)](https://synadia.com/ngs). Use them for connecting workloads to NATS clusters using NKey-based authentication. ## Use Cases * **NGS Cloud Access**: Connect to Synadia's global NATS service * **NATS Cluster Authentication**: Authenticate with self-hosted NATS clusters * **Microservice Messaging**: Enable pub/sub communication between services * **Event-Driven Architecture**: Connect event producers and consumers * **IoT Message Brokers**: Handle high-throughput device messaging ## Configuration Options | Field | Description | Required | | :----------- | :----------------------------------------- | :------- | | `accountId` | NATS account public key (starts with `A`) | Yes | | `privateKey` | Account private/seed key (starts with `S`) | Yes | NATS NKeys contain embedded checksums and must be generated using official NATS tools. Random strings will not work. *** ## Create a NATS Account Secret In the Console, navigate to **Secrets** and click **New**, or use the **Create** dropdown in the top-right corner and select **Secret**. Enter a **Name** and optional **Description**. Select **NATS Account** as the secret type. Click **Data** in the left pane. Enter the **Account ID** (starts with `A`) and the **Private Key** (starts with `S`). Click **Create**. ```bash theme={null} cpln secret create-nats \ --name my-nats-account \ --account-id AXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX \ --private-key SXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX \ --org my-org ``` ```hcl theme={null} resource "cpln_secret" "nats_account" { name = "my-nats-account" description = "NATS account for messaging service" nats_account { account_id = "AXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" private_key = "SXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" } } ``` This example uses placeholder NKeys for testing. In production, use Terraform variables or a secrets manager. ```typescript theme={null} import * as cpln from "@pulumiverse/cpln"; const natsSecret = new cpln.Secret("my-nats-account", { name: "my-nats-account", description: "NATS account for messaging service", natsAccount: { accountId: "AXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", privateKey: "SXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", }, }); ``` ```python theme={null} import pulumiverse_cpln as cpln nats_secret = cpln.Secret("my-nats-account", name="my-nats-account", description="NATS account for messaging service", nats_account={ "account_id": "AXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "private_key": "SXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", }) ``` ```go theme={null} package main import ( "github.com/pulumi/pulumi/sdk/v3/go/pulumi" "github.com/pulumiverse/pulumi-cpln/sdk/go/cpln" ) func main() { pulumi.Run(func(ctx *pulumi.Context) error { _, err := cpln.NewSecret(ctx, "my-nats-account", &cpln.SecretArgs{ Name: pulumi.String("my-nats-account"), Description: pulumi.String("NATS account for messaging service"), NatsAccount: &cpln.SecretNatsAccountArgs{ AccountId: pulumi.String("AXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"), PrivateKey: pulumi.String("SXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"), }, }) return err }) } ``` ```csharp theme={null} using Pulumi; using Pulumiverse.Cpln; using Pulumiverse.Cpln.Inputs; return await Deployment.RunAsync(() => { var natsSecret = new Secret("my-nats-account", new SecretArgs { Name = "my-nats-account", Description = "NATS account for messaging service", NatsAccount = new SecretNatsAccountArgs { AccountId = "AXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", PrivateKey = "SXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", }, }); }); ``` *** ## Using with NGS Cloud Account For NGS integration, you can also create an NGS [Cloud Account](/reference/cloudaccount) which provides managed connectivity. The NATS Account secret type is used for the underlying authentication. In the Console, navigate to **Cloud Accounts** and click **New**, or use the **Create** dropdown in the top-right corner and select **Cloud Account**. Enter a **Name** and optional **Description**. Select **NGS** as the cloud provider. Select your NATS Account secret from the dropdown. Click **Create**. ```bash theme={null} cpln cloudaccount create-ngs --name ngs-connection \ --secret my-nats-account \ --org my-org ``` ```hcl theme={null} resource "cpln_cloud_account" "ngs" { name = "ngs-connection" description = "NGS cloud account" ngs { secret_link = cpln_secret.nats_account.self_link } } ``` ```typescript theme={null} import * as cpln from "@pulumiverse/cpln"; const ngsCloudAccount = new cpln.CloudAccount("ngs-connection", { name: "ngs-connection", description: "NGS cloud account", ngs: { secretLink: natsSecret.selfLink, }, }); ``` ```python theme={null} import pulumiverse_cpln as cpln ngs_cloud_account = cpln.CloudAccount("ngs-connection", name="ngs-connection", description="NGS cloud account", ngs={ "secret_link": nats_secret.self_link, }) ``` ```go theme={null} ngsCloudAccount, err := cpln.NewCloudAccount(ctx, "ngs-connection", &cpln.CloudAccountArgs{ Name: pulumi.String("ngs-connection"), Description: pulumi.String("NGS cloud account"), Ngs: &cpln.CloudAccountNgsArgs{ SecretLink: natsSecret.SelfLink, }, }) ``` ```csharp theme={null} var ngsCloudAccount = new CloudAccount("ngs-connection", new CloudAccountArgs { Name = "ngs-connection", Description = "NGS cloud account", Ngs = new CloudAccountNgsArgs { SecretLink = natsSecret.SelfLink, }, }); ``` When using NGS Cloud Accounts, the NATS Account secret provides the authentication credentials, while the Cloud Account resource manages the connection to NGS infrastructure. *** ## Best Practices Create distinct NATS accounts for development, staging, and production to isolate message traffic and credentials. Regularly rotate NATS keys. Generate new keypairs, update the secret, and phase out old keys. Use NATS account permissions to limit which subjects each account can publish to or subscribe from. Track message rates and connection counts per account to detect anomalies or unauthorized access. *** ## Next Steps Set up NGS cloud account integration Learn how to grant access and inject secrets # Opaque Source: https://docs.controlplane.com/guides/create-secret/opaque Create an opaque secret to store any text-based sensitive data such as API keys, tokens, or configuration values. Opaque secrets are the most flexible secret type, allowing you to store any text-based data. Use them for API keys, tokens, configuration values, or any sensitive string that doesn't fit into a more specific secret type. ## Use Cases * **API Keys**: Store third-party API keys (Stripe, Twilio, SendGrid) * **Tokens**: JWT secrets, session tokens, webhook signing secrets * **License Keys**: Software license keys and activation codes * **Configuration Values**: Sensitive configuration that doesn't fit other types * **Custom Credentials**: Any text-based secret data * **Build Credentials**: A private registry token an image build needs, mounted with [`cpln image build --secret`](/cli-reference/get-started/images#build-secrets) so it never lands in the image ## Configuration Options | Field | Description | Required | | :--------- | :---------------------------------------------- | :------- | | `payload` | The secret value to store | Yes | | `encoding` | How the payload is encoded: `plain` or `base64` | Yes | When `encoding` is set to `base64`, the payload will be automatically decoded when accessed by workloads. This is useful for storing binary data or pre-encoded values. *** ## Create an Opaque Secret In the [Control Plane Console](https://console.cpln.io), navigate to **Secrets** and click **New**, or use the **Create** dropdown in the top-right corner and select **Secret**. Enter a **Name** and optional **Description**. Select **Opaque** as the secret type. Click **Data** in the left pane. Paste the secret content, drag and drop a file, or click to import. If your data is base64 encoded, enable **Base64 Decode at Runtime** to decode it when accessed. Click **Create**. Create a file named `secret.txt` with your secret content: ```text theme={null} sk_live_abc123xyz789 ``` Then create the secret: ```bash theme={null} cpln secret create-opaque \ --name my-api-key \ --file secret.txt \ --encoding plain \ --org my-org ``` You can also pass the payload directly using `--payload` instead of a file for simple values. ```hcl theme={null} resource "cpln_secret" "api_key" { name = "my-api-key" description = "Third-party API key" opaque { payload = "sk_live_abc123xyz789" encoding = "plain" } } ``` For base64-encoded data: ```hcl theme={null} resource "cpln_secret" "encoded_secret" { name = "my-encoded-secret" description = "Base64 encoded secret" opaque { payload = base64encode("my-secret-value") encoding = "base64" } } ``` Avoid hardcoding secrets in Terraform files. Use variables, environment variables, or integrate with a secrets manager like HashiCorp Vault. ```typescript theme={null} import * as cpln from "@pulumiverse/cpln"; const apiKeySecret = new cpln.Secret("my-api-key", { name: "my-api-key", description: "Third-party API key", opaque: { payload: "sk_live_abc123xyz789", encoding: "plain", }, }); ``` ```python theme={null} import pulumiverse_cpln as cpln api_key_secret = cpln.Secret("my-api-key", name="my-api-key", description="Third-party API key", opaque={ "payload": "sk_live_abc123xyz789", "encoding": "plain", }) ``` ```go theme={null} package main import ( "github.com/pulumi/pulumi/sdk/v3/go/pulumi" "github.com/pulumiverse/pulumi-cpln/sdk/go/cpln" ) func main() { pulumi.Run(func(ctx *pulumi.Context) error { _, err := cpln.NewSecret(ctx, "my-api-key", &cpln.SecretArgs{ Name: pulumi.String("my-api-key"), Description: pulumi.String("Third-party API key"), Opaque: &cpln.SecretOpaqueArgs{ Payload: pulumi.String("sk_live_abc123xyz789"), Encoding: pulumi.String("plain"), }, }) return err }) } ``` ```csharp theme={null} using Pulumi; using Pulumiverse.Cpln; using Pulumiverse.Cpln.Inputs; return await Deployment.RunAsync(() => { var apiKeySecret = new Secret("my-api-key", new SecretArgs { Name = "my-api-key", Description = "Third-party API key", Opaque = new SecretOpaqueArgs { Payload = "sk_live_abc123xyz789", Encoding = "plain", }, }); }); ``` *** ## Injecting into Workloads Reference the secret in your workload as an environment variable: ```yaml theme={null} env: - name: API_KEY value: "cpln://secret/my-api-key" ``` Or mount it as a file: ```yaml theme={null} volumes: - uri: "cpln://secret/my-api-key" path: /secrets/api-key.txt ``` *** ## Next Steps Learn how to grant access and inject secrets Store multiple related values in one secret # Create a Secret Source: https://docs.controlplane.com/guides/create-secret/overview Create and manage encrypted secrets for workloads. Covers secret types, workload injection methods, pull secret configuration, and CLI/Console workflows. Secrets provide secure, centralized storage for sensitive data like credentials, API keys, certificates, and configuration values. Control Plane encrypts all secrets at rest and in transit, with fine-grained access control through policies ensuring only authorized workloads can access the data they need. All secrets are encrypted using industry-standard algorithms Fine-grained control over which workloads can access secrets Inject secrets as environment variables, mount as files, or use as pull secrets for private container registries *** ## How Secrets Work Secrets can be used in two primary ways: **injected into workloads** at runtime, or as **pull secrets** for accessing private container registries. ### Injecting Secrets into Workloads Store your sensitive data in Control Plane using the Console, CLI, Terraform, or Pulumi. Choose the appropriate secret type for your use case. Create a [workload identity](/reference/identity) that will be used to authenticate your workload and request access to secrets. Create a [policy](/reference/policy) that grants the `reveal` permission on your secret to the workload identity. Associate the identity with your workload so it inherits the policy permissions. Use the `cpln://secret/SECRET_NAME` URI in your workload's environment variables or volume mounts. A [workload identity](/reference/identity) **must** be granted the [reveal permission](/reference/secret#permissions) on a secret to have its value injected at runtime. ### Using Secrets as Pull Secrets For pulling container images from private registries (Docker Hub, ECR, GCP Artifact Registry, etc.), you can attach registry credentials directly to your GVC as **pull secrets**. This approach doesn't require identity or policy setup. Create a [Docker](/guides/create-secret/docker), [ECR](/guides/create-secret/ecr), or [GCP](/guides/create-secret/gcp) secret with your registry credentials. Add the secret to your GVC's `pullSecretLinks` so all workloads in that GVC can pull from the private registry. ```yaml theme={null} kind: gvc name: my-gvc spec: pullSecretLinks: - //secret/my-docker-registry - //secret/my-ecr-credentials ``` Pull secrets are configured at the GVC level and automatically available to all workloads within that GVC. No identity or policy configuration is needed for pull secrets. *** ## Choosing the Right Secret Type ### Cloud Provider Credentials For authenticating with cloud services and pulling container images: Access key credentials for AWS services Pull images from Elastic Container Registry Service account keys for Google Cloud Service principal for Azure services Connect to Azure Function Apps Pull from any container registry ### Application Secrets For storing application configuration and credentials: API keys, tokens, and general secrets Multiple related key-value pairs Database and service credentials ### Certificates & Keys For cryptographic operations and secure communication: SSL/TLS certificates for HTTPS SSH keys and signing keys NATS messaging authentication *** ## Best Practices Choose the most specific secret type for your use case. For example, use **AWS** secrets for AWS credentials rather than storing them as **Opaque**. Specific types provide better validation and structured access. Grant `reveal` permission only to identities that need it. Create separate policies for different secrets rather than granting broad access. For API keys and tokens, environment variables are the simplest approach. Use volume mounts for certificates or when your application expects files. Establish a rotation schedule for credentials. Update secrets in Control Plane and redeploy workloads to pick up new values. Never commit secret values to version control. Use Terraform variables, Pulumi config secrets, or external secret management tools. Group related configuration values (like database host, port, and name) in a **Dictionary** secret. This makes management easier while still allowing individual key access. Use caution when handling sensitive values in IaC scripts. Best practices recommend using variables, encrypted state files, or external secret management rather than hard-coding sensitive values. *** ## Using Secrets in Workloads Once created, secrets can be injected into workloads as environment variables or mounted as files. This guide assumes you already have a GVC, workload, and secret. If you haven't created these yet, see [Create a GVC](/guides/create-gvc), [Create a Workload](/guides/create-workload), and the secret type guides above. ### Step 1: Create an Identity Navigate to **Identities** and click **New**, or click **Create** in the top-right corner and select **Identity**. Ensure the correct **GVC** is selected in the GVC dropdown. Enter a **Name** (e.g., `my-workload-identity`). Click **Create**. ### Step 2: Link Identity to Workload Navigate to your workload. Click **Identity** in the left pane. Select the identity you created (e.g., `my-workload-identity`). Click **Save**. ### Step 3: Create a Policy for Secret Access Navigate to **Policies** and click **New**, or click **Create** in the top-right corner and select **Policy**. Enter a **Name** (e.g., `workload-secret-access`). Click **Target** in the left pane. Under **Kind**, select **Secret**. Click **Items** in the left pane. Click **Add** and select the secret you want to grant access to. Click **Bindings** in the left pane. Click **Add**. In the **Permissions** tab, select **reveal**. In the **Identities** tab, select the identity linked to your workload. Click **OK**. Click **Create**. ### Step 4: Inject Secret into Workload **Option 1: Environment Variables** Navigate to your workload. Click **Containers** in the left pane, then select the **Env Vars** tab. Click **Add**. Enter the variable **Name** (e.g., `MY_SECRET`). In the **Value** dropdown, change **Literal Value** to **CPLN Secret**. In the dropdown next to it, find and select your secret by name. Click **Update**. **Option 2: Volume Mount** Navigate to your workload. Click **Containers** in the left pane, then select the **Volumes** tab. Click **Add**. Ensure **CPLN Secret** is selected for the URI type. Select the secret from the dropdown by name. Enter the **Path** where the secret will be mounted (e.g., `/secrets/my-secret.txt`). Click **Update**. This guide assumes you already have a GVC, workload, and secret. If you haven't created these yet, see [Create a GVC](/guides/create-gvc), [Create a Workload](/guides/create-workload), and the secret type guides above. ### Step 1: Create an Identity ```bash theme={null} cpln identity create \ --name my-workload-identity \ --gvc my-gvc \ --org my-org ``` ### Step 2: Link Identity to Workload ```bash theme={null} cpln workload update my-workload \ --set spec.identityLink=my-workload-identity \ --gvc my-gvc \ --org my-org ``` ### Step 3: Create a Policy for Secret Access Create the policy targeting your secret: ```bash theme={null} cpln policy create \ --name workload-secret-access \ --target-kind secret \ --resource my-secret \ --org my-org ``` Add a binding to grant the identity the `reveal` permission: ```bash theme={null} cpln policy add-binding workload-secret-access \ --permission reveal \ --identity my-workload-identity \ --gvc my-gvc \ --org my-org ``` ### Step 4: Inject Secret into Workload Update your workload to reference the secret as an environment variable: ```bash theme={null} cpln workload update my-workload \ --set spec.containers..env.MY_SECRET.value=cpln://secret/my-secret \ --gvc my-gvc \ --org my-org ``` Or mount as a volume by exporting your workload, editing it, and applying: ```bash theme={null} cpln workload get my-workload \ -o yaml-slim \ --gvc my-gvc \ --org my-org > my-workload.yaml ``` Edit `my-workload.yaml` to add a volume to your container: ```yaml theme={null} spec: containers: - name: main # ... existing config ... volumes: - uri: "cpln://secret/my-secret" path: /secrets/my-secret.txt ``` Apply the updated workload: ```bash theme={null} cpln apply --file my-workload.yaml --gvc my-gvc --org my-org ``` The following example shows a complete Terraform configuration that creates all the resources needed to inject a secret into a workload. Comments highlight each resource and the key configurations for secret access. ```hcl theme={null} # Create a GVC resource "cpln_gvc" "my_gvc" { name = "my-gvc" description = "My GVC" locations = ["aws-us-west-2", "gcp-us-east1"] } # Create a Secret resource "cpln_secret" "my_secret" { name = "my-secret" description = "Third-party API key" opaque { payload = "my-secret-value" encoding = "plain" } } # Create an Identity for the workload resource "cpln_identity" "workload_identity" { gvc = cpln_gvc.my_gvc.name name = "my-workload-identity" description = "Identity for workload secret access" } # Create the Workload with identity linked resource "cpln_workload" "my_workload" { gvc = cpln_gvc.my_gvc.name name = "my-workload" type = "standard" # Link the identity to the workload identity_link = cpln_identity.workload_identity.self_link container { name = "main" image = "kennethreitz/httpbin" cpu = "50m" memory = "128Mi" ports { number = 80 protocol = "http" } # Inject secret as environment variable env = { MY_SECRET = cpln_secret.my_secret.secret_link } # Mount secret as a file volume { uri = cpln_secret.my_secret.secret_link path = "/secrets/my-secret.txt" } } options { capacity_ai = true timeout_seconds = 5 autoscaling { metric = "disabled" target = 95 min_scale = 1 max_scale = 1 } } firewall_spec { external { inbound_allow_cidr = ["0.0.0.0/0"] } } } # Create a Policy granting the identity reveal permission on the secret resource "cpln_policy" "secret_access" { name = "workload-secret-access" description = "Allow workload to reveal secret" target_kind = "secret" target_links = [cpln_secret.my_secret.self_link] binding { permissions = ["reveal"] principal_links = [cpln_identity.workload_identity.self_link] } } ``` This example uses a hardcoded secret value for testing. In production, use Terraform variables with `sensitive = true` or integrate with a secrets manager. The following examples show complete Pulumi programs that create all the resources needed to inject a secret into a workload. Comments highlight each resource and the key configurations for secret access. ```typescript theme={null} import * as cpln from "@pulumiverse/cpln"; // Create a GVC const gvc = new cpln.Gvc("my-gvc", { name: "my-gvc", description: "My GVC", locations: ["aws-us-west-2", "gcp-us-east1"], }); // Create a Secret const secret = new cpln.Secret("my-secret", { name: "my-secret", description: "Third-party API key", opaque: { payload: "my-secret-value", encoding: "plain", }, }); // Create an Identity for the workload const workloadIdentity = new cpln.Identity("my-workload-identity", { gvc: gvc.name, name: "my-workload-identity", description: "Identity for workload secret access", }); // Create the Workload with identity linked const workload = new cpln.Workload("my-workload", { gvc: gvc.name, name: "my-workload", type: "standard", // Link the identity to the workload identityLink: workloadIdentity.selfLink, containers: [ { name: "main", image: "kennethreitz/httpbin", cpu: "50m", memory: "128Mi", ports: [ { number: 80, protocol: "http", }, ], // Inject secret as environment variable env: { MY_SECRET: secret.secretLink, }, // Mount secret as a file volumes: [ { uri: secret.secretLink, path: "/secrets/my-secret.txt", }, ], }, ], options: { capacityAi: true, timeoutSeconds: 5, autoscaling: { metric: "disabled", target: 95, minScale: 1, maxScale: 1, }, }, firewallSpec: { external: { inboundAllowCidrs: ["0.0.0.0/0"], }, }, }); // Create a Policy granting the identity reveal permission on the secret const secretAccessPolicy = new cpln.Policy("workload-secret-access", { name: "workload-secret-access", description: "Allow workload to reveal secret", targetKind: "secret", targetLinks: [secret.selfLink], bindings: [ { permissions: ["reveal"], principalLinks: [workloadIdentity.selfLink], }, ], }); ``` ```python theme={null} import pulumiverse_cpln as cpln # Create a GVC gvc = cpln.Gvc("my-gvc", name="my-gvc", description="My GVC", locations=["aws-us-west-2", "gcp-us-east1"]) # Create a Secret secret = cpln.Secret("my-secret", name="my-secret", description="Third-party API key", opaque={ "payload": "my-secret-value", "encoding": "plain", }) # Create an Identity for the workload workload_identity = cpln.Identity("my-workload-identity", gvc=gvc.name, name="my-workload-identity", description="Identity for workload secret access") # Create the Workload with identity linked workload = cpln.Workload("my-workload", gvc=gvc.name, name="my-workload", type="standard", # Link the identity to the workload identity_link=workload_identity.self_link, containers=[{ "name": "main", "image": "kennethreitz/httpbin", "cpu": "50m", "memory": "128Mi", "ports": [{ "number": 80, "protocol": "http", }], # Inject secret as environment variable "env": { "MY_SECRET": secret.secret_link, }, # Mount secret as a file "volumes": [{ "uri": secret.secret_link, "path": "/secrets/my-secret.txt", }], }], options={ "capacity_ai": True, "timeout_seconds": 5, "autoscaling": { "metric": "disabled", "target": 95, "min_scale": 1, "max_scale": 1, }, }, firewall_spec={ "external": { "inbound_allow_cidrs": ["0.0.0.0/0"], }, }) # Create a Policy granting the identity reveal permission on the secret secret_access_policy = cpln.Policy("workload-secret-access", name="workload-secret-access", description="Allow workload to reveal secret", target_kind="secret", target_links=[secret.self_link], bindings=[{ "permissions": ["reveal"], "principal_links": [workload_identity.self_link], }]) ``` ```go theme={null} package main import ( "github.com/pulumi/pulumi/sdk/v3/go/pulumi" "github.com/pulumiverse/pulumi-cpln/sdk/go/cpln" ) func main() { pulumi.Run(func(ctx *pulumi.Context) error { // Create a GVC gvc, err := cpln.NewGvc(ctx, "my-gvc", &cpln.GvcArgs{ Name: pulumi.String("my-gvc"), Description: pulumi.String("My GVC"), Locations: pulumi.StringArray{pulumi.String("aws-us-west-2"), pulumi.String("gcp-us-east1")}, }) if err != nil { return err } // Create a Secret secret, err := cpln.NewSecret(ctx, "my-secret", &cpln.SecretArgs{ Name: pulumi.String("my-secret"), Description: pulumi.String("Third-party API key"), Opaque: &cpln.SecretOpaqueArgs{ Payload: pulumi.String("my-secret-value"), Encoding: pulumi.String("plain"), }, }) if err != nil { return err } // Create an Identity for the workload workloadIdentity, err := cpln.NewIdentity(ctx, "my-workload-identity", &cpln.IdentityArgs{ Gvc: gvc.Name, Name: pulumi.String("my-workload-identity"), Description: pulumi.String("Identity for workload secret access"), }) if err != nil { return err } // Create the Workload with identity linked _, err = cpln.NewWorkload(ctx, "my-workload", &cpln.WorkloadArgs{ Gvc: gvc.Name, Name: pulumi.String("my-workload"), Type: pulumi.String("standard"), // Link the identity to the workload IdentityLink: workloadIdentity.SelfLink, Containers: cpln.WorkloadContainerArray{ &cpln.WorkloadContainerArgs{ Name: pulumi.String("main"), Image: pulumi.String("kennethreitz/httpbin"), Cpu: pulumi.String("50m"), Memory: pulumi.String("128Mi"), Ports: cpln.WorkloadContainerPortArray{ &cpln.WorkloadContainerPortArgs{ Number: pulumi.Int(80), Protocol: pulumi.String("http"), }, }, // Inject secret as environment variable Env: pulumi.StringMap{ "MY_SECRET": secret.SecretLink, }, // Mount secret as a file Volumes: cpln.WorkloadContainerVolumeArray{ &cpln.WorkloadContainerVolumeArgs{ Uri: secret.SecretLink, Path: pulumi.String("/secrets/my-secret.txt"), }, }, }, }, Options: &cpln.WorkloadOptionsArgs{ CapacityAi: pulumi.Bool(true), TimeoutSeconds: pulumi.Int(5), Autoscaling: &cpln.WorkloadOptionsAutoscalingArgs{ Metric: pulumi.String("disabled"), Target: pulumi.Int(95), MinScale: pulumi.Int(1), MaxScale: pulumi.Int(1), }, }, FirewallSpec: &cpln.WorkloadFirewallSpecArgs{ External: &cpln.WorkloadFirewallSpecExternalArgs{ InboundAllowCidrs: pulumi.StringArray{pulumi.String("0.0.0.0/0")}, }, }, }) if err != nil { return err } // Create a Policy granting the identity reveal permission on the secret _, err = cpln.NewPolicy(ctx, "workload-secret-access", &cpln.PolicyArgs{ Name: pulumi.String("workload-secret-access"), Description: pulumi.String("Allow workload to reveal secret"), TargetKind: pulumi.String("secret"), TargetLinks: pulumi.StringArray{secret.SelfLink}, Bindings: cpln.PolicyBindingArray{ &cpln.PolicyBindingArgs{ Permissions: pulumi.StringArray{pulumi.String("reveal")}, PrincipalLinks: pulumi.StringArray{workloadIdentity.SelfLink}, }, }, }) if err != nil { return err } return nil }) } ``` ```csharp theme={null} using Pulumi; using Pulumiverse.Cpln; using Pulumiverse.Cpln.Inputs; return await Deployment.RunAsync(() => { // Create a GVC var gvc = new Gvc("my-gvc", new GvcArgs { Name = "my-gvc", Description = "My GVC", Locations = { "aws-us-west-2", "gcp-us-east1" }, }); // Create a Secret var secret = new Secret("my-secret", new SecretArgs { Name = "my-secret", Description = "Third-party API key", Opaque = new SecretOpaqueArgs { Payload = "my-secret-value", Encoding = "plain", }, }); // Create an Identity for the workload var workloadIdentity = new Identity("my-workload-identity", new IdentityArgs { Gvc = gvc.Name, Name = "my-workload-identity", Description = "Identity for workload secret access", }); // Create the Workload with identity linked var workload = new Workload("my-workload", new WorkloadArgs { Gvc = gvc.Name, Name = "my-workload", Type = "standard", // Link the identity to the workload IdentityLink = workloadIdentity.SelfLink, Containers = new[] { new WorkloadContainerArgs { Name = "main", Image = "kennethreitz/httpbin", Cpu = "50m", Memory = "128Mi", Ports = new[] { new WorkloadContainerPortArgs { Number = 80, Protocol = "http", }, }, // Inject secret as environment variable Env = { { "MY_SECRET", secret.SecretLink }, }, // Mount secret as a file Volumes = new[] { new WorkloadContainerVolumeArgs { Uri = secret.SecretLink, Path = "/secrets/my-secret.txt", }, }, }, }, Options = new WorkloadOptionsArgs { CapacityAi = true, TimeoutSeconds = 5, Autoscaling = new WorkloadOptionsAutoscalingArgs { Metric = "disabled", Target = 95, MinScale = 1, MaxScale = 1, }, }, FirewallSpec = new WorkloadFirewallSpecArgs { External = new WorkloadFirewallSpecExternalArgs { InboundAllowCidrs = { "0.0.0.0/0" }, }, }, }); // Create a Policy granting the identity reveal permission on the secret var secretAccessPolicy = new Policy("workload-secret-access", new PolicyArgs { Name = "workload-secret-access", Description = "Allow workload to reveal secret", TargetKind = "secret", TargetLinks = { secret.SelfLink }, Bindings = new[] { new PolicyBindingArgs { Permissions = { "reveal" }, PrincipalLinks = { workloadIdentity.SelfLink }, }, }, }); }); ``` This example uses a hardcoded secret value for testing. In production, use Pulumi config secrets (`pulumi config set --secret`) to manage sensitive values securely. ### Secret Reference Formats When referencing secrets, use the `cpln://secret/` URI format: | Format | Description | Example | | :------------------------------ | :------------------------------------------------------ | :------------------------------------ | | `cpln://secret/SECRET_NAME` | Reference entire secret payload | `cpln://secret/my-secret` | | `cpln://secret/SECRET_NAME.KEY` | Reference specific key in dictionary/structured secrets | `cpln://secret/my-config.DB_PASSWORD` | The mount behavior varies by secret type: * **Opaque**: The payload is mounted as a file at the specified path * **Dictionary and other structured secrets**: A directory is created containing files for each key/property * **Docker, Azure SDK, and GCP**: The secret is mounted as a file named `___cpln___.secret` in the specified directory For more details, see the [Workload Volumes Reference](/reference/workload/volumes). *** ## Next Steps Complete API reference for secrets Learn more about workload identities Understand policy-based access control Volume mount options and behaviors # TLS Source: https://docs.controlplane.com/guides/create-secret/tls Create a TLS secret to store SSL/TLS certificates and private keys for custom domains, mTLS authentication, or secure communication. TLS secrets store SSL/TLS certificates and private keys for secure communication. Use them for custom domains, mTLS authentication, or any scenario requiring certificate-based security. ## Use Cases * **Custom Domains**: Provide TLS certificates for custom domain names * **mTLS Authentication**: Mutual TLS for service-to-service authentication * **Internal Services**: Secure communication between workloads * **Legacy Integrations**: Connect to systems requiring client certificates ## Configuration Options | Field | Description | Required | | :------ | :------------------------------------------------- | :------- | | `key` | Private key in PEM format | Yes | | `cert` | Certificate in PEM format | Yes | | `chain` | Certificate chain/intermediate certs in PEM format | No | The private key must match the certificate's public key. For production, use certificates from a trusted Certificate Authority (CA). *** ## Create a TLS Secret In the Console, navigate to **Secrets** and click **New**, or use the **Create** dropdown in the top-right corner and select **Secret**. Enter a **Name** and optional **Description**. Select **TLS** as the secret type. Click **Data** in the left pane. For **TLS Key**, drag and drop your private key file or click to import. For **TLS Cert**, drag and drop your certificate file or click to import. For **TLS Chain** (optional), drag and drop your certificate chain file or click to import. Click **Create**. Prepare your certificate files, then create the secret: ```bash theme={null} cpln secret create-tls \ --name my-domain-tls \ --key private.key \ --cert certificate.crt \ --chain chain.crt \ --org my-org ``` Without a certificate chain (for self-signed certs): ```bash theme={null} cpln secret create-tls \ --name self-signed-tls \ --key private.key \ --cert certificate.crt \ --org my-org ``` ```hcl theme={null} resource "cpln_secret" "tls" { name = "my-domain-tls" description = "TLS certificate for api.example.com" tls { key = file("private.key") cert = file("certificate.crt") chain = file("chain.crt") } } ``` Using Let's Encrypt with Terraform: ```hcl theme={null} resource "tls_private_key" "example" { algorithm = "RSA" rsa_bits = 2048 } resource "acme_certificate" "example" { # ... ACME configuration ... } resource "cpln_secret" "tls" { name = "letsencrypt-tls" description = "Let's Encrypt certificate" tls { key = tls_private_key.example.private_key_pem cert = acme_certificate.example.certificate_pem chain = acme_certificate.example.issuer_pem } } ``` Never commit private keys to version control. Use Terraform state encryption and secure variable handling. ```typescript theme={null} import * as cpln from "@pulumiverse/cpln"; import * as pulumi from "@pulumi/pulumi"; import * as fs from "fs"; const tlsSecret = new cpln.Secret("my-domain-tls", { name: "my-domain-tls", description: "TLS certificate for api.example.com", tls: { key: fs.readFileSync("private.key", "utf8"), cert: fs.readFileSync("certificate.crt", "utf8"), chain: fs.readFileSync("chain.crt", "utf8"), }, }); ``` ```python theme={null} import pulumiverse_cpln as cpln with open("private.key") as f: private_key = f.read() with open("certificate.crt") as f: certificate = f.read() with open("chain.crt") as f: chain = f.read() tls_secret = cpln.Secret("my-domain-tls", name="my-domain-tls", description="TLS certificate for api.example.com", tls={ "key": private_key, "cert": certificate, "chain": chain, }) ``` ```go theme={null} package main import ( "os" "github.com/pulumi/pulumi/sdk/v3/go/pulumi" "github.com/pulumiverse/pulumi-cpln/sdk/go/cpln" ) func main() { pulumi.Run(func(ctx *pulumi.Context) error { key, _ := os.ReadFile("private.key") cert, _ := os.ReadFile("certificate.crt") chain, _ := os.ReadFile("chain.crt") _, err := cpln.NewSecret(ctx, "my-domain-tls", &cpln.SecretArgs{ Name: pulumi.String("my-domain-tls"), Description: pulumi.String("TLS certificate for api.example.com"), Tls: &cpln.SecretTlsArgs{ Key: pulumi.String(string(key)), Cert: pulumi.String(string(cert)), Chain: pulumi.String(string(chain)), }, }) return err }) } ``` ```csharp theme={null} using System.IO; using Pulumi; using Pulumiverse.Cpln; using Pulumiverse.Cpln.Inputs; return await Deployment.RunAsync(() => { var tlsSecret = new Secret("my-domain-tls", new SecretArgs { Name = "my-domain-tls", Description = "TLS certificate for api.example.com", Tls = new SecretTlsArgs { Key = File.ReadAllText("private.key"), Cert = File.ReadAllText("certificate.crt"), Chain = File.ReadAllText("chain.crt"), }, }); }); ``` *** ## Certificate Formats TLS secrets expect PEM-encoded files: **Private Key:** ```text theme={null} -----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEA... -----END RSA PRIVATE KEY----- ``` **Certificate:** ```text theme={null} -----BEGIN CERTIFICATE----- MIIDXTCCAkWgAwIBAgIJ... -----END CERTIFICATE----- ``` *** ## Best Practices For production, use certificates from trusted Certificate Authorities like Let's Encrypt, DigiCert, or your organization's internal CA. Track certificate expiration dates and rotate before they expire. Consider automating renewal with tools like cert-manager or ACME clients. Never share private keys via email or chat. Use secure transfer methods and limit access to key files. *** ## Next Steps Configure custom domains with TLS certificates Learn how to grant access and inject secrets # Username & Password Source: https://docs.controlplane.com/guides/create-secret/userpass Create a username and password secret to store authentication credentials for databases, APIs, SMTP servers, or other services. Username & Password secrets store authentication credentials as a username/password pair. Use them for database connections, API authentication, SMTP servers, or any service requiring basic credentials. ## Use Cases * **Database Authentication**: Store credentials for PostgreSQL, MySQL, MongoDB, etc. * **API Authentication**: Basic auth credentials for REST APIs * **SMTP/Email Services**: Credentials for email sending services * **Legacy Systems**: Connect to systems using username/password authentication * **FTP/SFTP Access**: Credentials for file transfer services ## Configuration Options | Field | Description | Required | | :--------- | :------------------------------------------ | :-------------------- | | `username` | The username or account identifier | Yes | | `password` | The password or secret credential | Yes | | `encoding` | How values are encoded: `plain` or `base64` | No (default: `plain`) | Use `encoding: base64` when your credentials contain special characters that might cause parsing issues, or when you're storing pre-encoded credentials from another system. *** ## Create a Username & Password Secret In the Console, navigate to **Secrets** and click **New**, or use the **Create** dropdown in the top-right corner and select **Secret**. Enter a **Name** and optional **Description**. Select **Username/Password** as the secret type. Click **Data** in the left pane. Enter the **Username** and **Password**. If your credentials are base64 encoded, enable **Base64 Decode at Runtime** to decode them when accessed. Click **Create**. ```bash theme={null} cpln secret create-userpass \ --name db-credentials \ --username app_user \ --password 'secure-password-here' \ --org my-org ``` Wrap passwords in single quotes to prevent shell interpretation of special characters. ```hcl theme={null} resource "cpln_secret" "db_credentials" { name = "db-credentials" description = "PostgreSQL database credentials" userpass { username = "app_user" password = "secure-password-here" encoding = "plain" } } ``` This example uses hardcoded credentials for testing. In production, use Terraform variables, environment variables, or integrate with a secrets manager like HashiCorp Vault. ```typescript theme={null} import * as cpln from "@pulumiverse/cpln"; const dbCredentials = new cpln.Secret("db-credentials", { name: "db-credentials", description: "PostgreSQL database credentials", userpass: { username: "app_user", password: "secure-password-here", encoding: "plain", }, }); ``` ```python theme={null} import pulumiverse_cpln as cpln db_credentials = cpln.Secret("db-credentials", name="db-credentials", description="PostgreSQL database credentials", userpass={ "username": "app_user", "password": "secure-password-here", "encoding": "plain", }) ``` ```go theme={null} package main import ( "github.com/pulumi/pulumi/sdk/v3/go/pulumi" "github.com/pulumiverse/pulumi-cpln/sdk/go/cpln" ) func main() { pulumi.Run(func(ctx *pulumi.Context) error { _, err := cpln.NewSecret(ctx, "db-credentials", &cpln.SecretArgs{ Name: pulumi.String("db-credentials"), Description: pulumi.String("PostgreSQL database credentials"), Userpass: &cpln.SecretUserpassArgs{ Username: pulumi.String("app_user"), Password: pulumi.String("secure-password-here"), Encoding: pulumi.String("plain"), }, }) return err }) } ``` ```csharp theme={null} using Pulumi; using Pulumiverse.Cpln; using Pulumiverse.Cpln.Inputs; return await Deployment.RunAsync(() => { var dbCredentials = new Secret("db-credentials", new SecretArgs { Name = "db-credentials", Description = "PostgreSQL database credentials", Userpass = new SecretUserpassArgs { Username = "app_user", Password = "secure-password-here", Encoding = "plain", }, }); }); ``` *** ## Injecting into Workloads ### As Environment Variables Reference the username and password separately: ```yaml theme={null} env: - name: DB_USER value: "cpln://secret/db-credentials.username" - name: DB_PASSWORD value: "cpln://secret/db-credentials.password" ``` ### As Volume Files When mounted as a volume, the secret creates two files: ```yaml theme={null} volumes: - uri: "cpln://secret/db-credentials" path: /etc/credentials ``` This creates: * `/etc/credentials/username` containing the username * `/etc/credentials/password` containing the password *** ## Common Patterns ### PostgreSQL Connection ```yaml theme={null} kind: secret name: postgres-credentials type: userpass data: username: postgres password: your-secure-password ``` Workload environment: ```yaml theme={null} env: - name: PGUSER value: "cpln://secret/postgres-credentials.username" - name: PGPASSWORD value: "cpln://secret/postgres-credentials.password" ``` ### MongoDB Connection ```yaml theme={null} kind: secret name: mongodb-credentials type: userpass data: username: mongouser password: your-secure-password ``` ### SMTP Credentials ```yaml theme={null} kind: secret name: smtp-credentials type: userpass data: username: apikey password: SG.xxxxx # SendGrid API key ``` ### Basic Auth for APIs ```yaml theme={null} kind: secret name: api-credentials type: userpass data: username: api_client_id password: api_client_secret ``` *** ## Best Practices Generate random passwords with sufficient length and complexity. Avoid reusing passwords across different services. Establish a rotation schedule for credentials. Update both the secret in Control Plane and the corresponding service. Create dedicated service accounts rather than using personal credentials. This enables better auditing and easier credential rotation. Where possible, prefer more secure authentication methods like IAM roles, service accounts, or certificate-based auth over username/password. *** ## Next Steps Store API keys and other single-value secrets Learn how to grant access and inject secrets # Create a Service Account Source: https://docs.controlplane.com/guides/create-service-account Create a service account within your org and generate API keys for programmatic access to Control Plane resources. ## Overview Follow the steps below to create a [service account](/reference/serviceaccount) within your [org](/reference/org). ## Prerequisites * Review the [service account](/reference/serviceaccount) reference page. * Have [permissions](/reference/policy#permissions) to create a [service account](/reference/serviceaccount). * Optional: Install the [CLI](/cli-reference/installation). ## Create using the Console 1. Create a new service account using one of the following methods: * Click `Service Accounts`, then click `New`, or * Click the `Create` dropdown in the upper right corner and select `Service Account`. 2. Enter a unique name and an optional description. 3. Optional: Select a group for the service account to inherit the group's permissions. 4. Optionally, click `Tags` and enter any [tags](/core/misc#tags). 5. Click `Create`. 6. Click `Keys` to generate a new token. 7. Enter an optional key description and click `Add`. Copy or download the generated key. Click `Done`. ## Create using the CLI Refer to the [serviceaccount create](/cli-reference/commands/serviceaccount#serviceaccount-create) and [serviceaccount add-key](/cli-reference/commands/serviceaccount#serviceaccount-add-key) commands for details and examples. ## Next Steps After creating a service account with a key, it can be referenced in a [policy](/reference/policy) to grant permissions to any Control Plane resource. After permissions are granted, use [browser-less CLI login](/guides/browser-less-cli-login) to authenticate and run CLI commands. # Create a Workload Source: https://docs.controlplane.com/guides/create-workload Deploy containerized applications step by step via the Console or CLI. Covers container setup, identity assignment, scaling options, and firewall rules. ## Overview Follow the steps below to create a [workload](/reference/workload) within your [GVC](/reference/gvc). ## Prerequisites * Review the [workload](/reference/workload) reference page. * Have [permissions](/reference/policy#permissions) to create a [workload](/reference/workload). * Optional: Install the [CLI](/cli-reference/installation). ## Create using the Console 1. Create a new workload using one of the following methods: * Click `Workloads` in the left menu, then click `New`. * Click the `Create` dropdown in the upper right corner and select `Workload`. 2. Enter a unique name, an optional description, and select the GVC where the workload will be created. 3. Click `Options` to configure workload settings. For details, see [workload general settings](/reference/workload/general). 4. Click `Containers` to configure the container image, ports, commands, environment variables, and mounted files. For details, see [workload containers](/reference/workload/containers). 5. Click `Identity` and select one of the available [identities](/reference/identity). If no identities are defined, leave this field blank. 6. Click `Firewall` to configure firewall settings for the workload. For details, see [workload firewall settings](/reference/workload/firewall). 7. Click `Load Balancer` to configure load-balancing settings for the workload. For details, see [workload load balancing](/reference/workload/load-balancing). 8. Click `Advanced Settings` to configure additional workload settings. For details, see [workload general settings](/reference/workload/general). 9. Click `Sidecar | Envoy` to configure sidecar and Envoy settings for the workload. For details, see [workload JWT authentication](/reference/workload/jwt-auth). Depending on the requirements of your container, the firewall options can be configured to allow either or both inbound and outbound requests. If your container will be accepting requests from the Internet, verify that the `All Inbound Requests Allowed` switch is turned on or enter the expected CIDRs. If your container requires Internet access, verify that the `All Outbound Requests Allowed` switch is turned on or enter the expected CIDRs or hostnames in their respective text boxes. 10. Optionally, click `Tags` and enter any [tags](/core/misc#tags). 11. Click `Create`. The workload summary page is then shown. After a few moments, if the image is deployed successfully, the `Health` status will show `Ready`. You can click the `Canonical Endpoint (Global)` link to open it in a new tab. ## Create using the CLI Refer to the [workload create](/cli-reference/commands/workload#workload-create) command for details and examples. # Default Metrics Source: https://docs.controlplane.com/guides/default-metrics Reference of built-in metrics available for orgs, HTTP/gRPC traffic, volumes, replicas, CPU, memory, GPU, and network usage. ### Org Metrics * `logs_storage_mb`: Log storage used in megabytes * `tracing_storage_mb`: Tracing storage used in megabytes * `metrics_storage_mb`: Metrics storage used in megabytes * `agent_peers_count`: Number of agent peers * `agent_services_count`: Number of agent services * `agent_tx_bytes_total`: Total transmitted bytes by agents * `agent_rx_bytes_total`: Total received bytes by agents * `agent_tx_packets_total`: Total transmitted packets by agents * `agent_rx_packets_total`: Total received packets by agents * `threat_detection_forward_enabled`: 0 or 1 indicating if threat detection forwarding is enabled for the org (syslog) * `threat_detection_forward_total`: Total of all threat events forwarded to the Syslog target * `threat_detection_alerts`: Increments when a threat detection alert is generated ### HTTP/GRPC * `requests_per_second`: Number of HTTP/GRPC requests received per second * `requests_initiated_per_second`: Number of HTTP/GRPC requests initiated per second * `request_duration_ms_bucket`: Latency histogram for HTTP/gRPC requests received ### Volumes * `volume_set_capacity_billable`: Billable capacity of volume sets * `volume_set_snapshots_billable`: Billable snapshot capacity of volume sets * `volume_set_free_bytes`: Free bytes available in volume sets * `volume_set_capacity_bytes`: Total capacity of volume sets in bytes ### Resources * `cpu_reserved`: CPU resources reserved * `cpu_used`: CPU resources utilized * `cpu_billable`: Billable CPU resources * `memory_reserved`: Memory resources reserved (bytes) * `memory_used`: Memory resources utilized (bytes) * `memory_billable`: Billable memory resources (bytes) ### Network * `egress`: Egress network traffic (bytes) * `cross_zone_traffic`: Cross-zone network traffic (bytes) ### Workloads * `replica_count`: Number of replicas * `container_restarts`: Number of container restarts * `load_balancer`: Number of load balancers * `cron_executions`: Number of cron job executions * `cron_execution_rate`: Rate of cron job executions * `workload_progress_failure`: Number of workload progress failures * `workload_ready_replicas`: Number of ready replicas in workloads * `workload_rescheduled_replicas`: Number of replicas rescheduled to other nodes in workloads * `capacity_ai_updates`: Number of times capacity AI has updated the workload's resources ### Domains * `domain_warnings`: Number of domain warnings ## MK8s Specific Metrics If you are using MK8s and have metrics enabled, you will have access to kube and node metrics as well: ### kube metrics We expose many metrics published by kube-state-metrics. These metrics all start with the `kube_` prefix. Information on these metrics can be found in the [kube-state-metrics documentation](https://github.com/kubernetes/kube-state-metrics/tree/main/docs#exposed-metrics) ### node metrics We expose many metrics published by node-exporter. These metrics all start with the `node_` prefix. Information on these metrics can be found in the [node-exporter documentation](https://github.com/prometheus/node_exporter#collectors) # Environment Promotion Source: https://docs.controlplane.com/guides/environment-promotion Promote workloads across development, staging, and production environments using separate orgs and GitHub Actions workflows. As a best practice, each deployment environment (development, staging, production, etc.) should map to a Control Plane [Org](/reference/org). The primary benefit of having each environment under a separate [Org](/reference/org) is that definitions of different [GVCs](/reference/gvc) and [workloads](/reference/workload) can be applied to different [Orgs](/reference/org) without the need to include an environment name in any object. For example, in the development [Org](/reference/org), you could apply nearly the same YAML manifest files (used by [cpln apply](/guides/cpln-apply)) as the production [Org](/reference/org) with different content for your secrets. During the promotion process, the [image](/reference/image) that was built for development can be referred to by other [workloads](/reference/workload) in the same [Org](/reference/org) and across [Orgs](/reference/org). This is a great time saver for lengthy build processes. ## Promotion using GitHub Actions By leveraging the functionality of [GitHub Actions](https://docs.github.com/en/actions), promoting code changes from one environment to another is as easy as committing/pushing your code and opening/merging pull requests. This [example](https://github.com/controlplane-com/promotion-demo) project contains three GitHub Actions (in the `./.github/workflows` directory) that perform the following: 1. On a pull request (or updates to an existing pull request) to the `main` branch, the application is containerized and pushed to the dev [Org's](/reference/org) private [image registry](/reference/image). The [GVC](/reference/gvc) and `Review Workload` is created/updated by applying the YAML contents of the files `./cpln/cpln-gvc.yaml` and `./cpln/cpln-workload.yaml`. The name of the `Review Workload` will be prefixed by the name of the branch that created the pull request. 2. When a pull request is accepted and the code is merged to the `main` branch, a `dev` [workload](/reference/workload) is updated (or created if it doesn't exists) in the dev [Org](/reference/org) by applying the same files as step 1, except that the [workload](/reference/workload) name is prefixed with `dev`. This allows the application to be reviewed and tested before being pushed to the production [Org](/reference/org). 3. The promotion to the `stage` and `prod` environment is accomplished by manually executing the `Deploy-To-Stage-or-Prod` workflow and selecting the target environment. The target [GVC](/reference/gvc) and [Workload](/reference/workload) in the production [Org](/reference/org) is updated (or created if it doesn't exists) by applying the YAML contents of the files `./cpln/cpln-gvc-prod.yaml` and `./cpln/cpln-workload.yaml`. The main difference between the two [GVC](/reference/gvc) files is that the `prod` version contains the `Pull Secret` that is needed to pull the [image](/reference/image) from the dev [Org](/reference/org) and the `stage` and `prod` [workloads](/reference/workload) refer to the [image](/reference/image) that was pushed to the development [Org](/reference/org). ## Review Workload The above example demonstrates the concept of a **Review Workload**. This [workload](/reference/workload) allows for review and testing of the application before being promoted to upstream [workloads](/reference/workload). The deployment of the **Review Workload** occurs when a pull request from a feature branch to the `main` branch is opened. The application will only be promoted to the `dev` [workload](/reference/workload) if the pull request is closed and merged. ## Permissions To control which users have the ability to perform sensitive actions, such as merging a pull request, you can utilize the built-in capabilities of GitHub by creating a custom repository role. Review these [instructions](https://docs.github.com/en/enterprise-cloud@latest/organizations/managing-peoples-access-to-your-organization-with-roles/managing-custom-repository-roles-for-an-organization) on how to create a role. ## Notes * The example above demonstrates promoting code across four environments (review and development in one [Org](/reference/org), and staging and production in a separate [Org](/reference/org)). Of course, you can arrange each environment within its own [Org](/reference/org), or mix multiple environments within an [Org](/reference/org) as shown in the example. There is no limit to the number of [Orgs](/reference/org) / environments that can be created. This example can be adapted to suit your unique deployment requirements. * For more complex applications, each environment's [GVC](/reference/gvc) / [workload](/reference/workload) should be configured with its own: * [Domain](/reference/domain) * [Pull Secrets](/reference/gvc#pull-secrets) * [Environment Variables](/reference/workload#environment-variables) # Export Metrics to Prometheus Source: https://docs.controlplane.com/guides/export-metrics Export metrics from multiple organizations using the Prometheus `/federate` endpoint and centralize collection in an external Prometheus instance. ## Overview This guide demonstrates how to export metrics from multiple organizations using the Prometheus `/federate` endpoint. This setup allows you to centralize metrics collection by federating Control Plane metrics into a single external Prometheus instance or service that can collect data using a Prometheus scrape job. ### Prerequisites * Superuser access to the Control Plane Console for each organization. * A Prometheus instance configured to scrape metrics or a third-party service that can collect metrics using a Prometheus scrape job. ### Steps #### In the Source Organization * Log in to the [Console](https://console.cpln.io). * [Create a new service account](/guides/create-service-account) named `prometheus-federate`. * Click `Keys` and create a new key for the `prometheus-federate` service account and store it securely. Grant the service account created above the `readMetrics` permission by creating a policy using **one** of the following methods: 1. Using the Console, create a policy named `prometheus-federate` that grants the `readMetrics` permission to the service account. * Click `Policies` in the left menu. * Click `New` and fill in the details: * **Name**: `prometheus-federate` * **Description**: `prometheus-federate` * Click `Target`. * **Target Kind**: `org` * Click `Binding`. * Click `Add Binding`: * On the `Permissions` tab, check `readMetrics` * On the `Service Accounts` tab, check `prometheus-federate` * Click `Ok`. * Click `Create`. 2. Using YAML * Apply the YAML manifest below using the [cpln apply](/guides/cpln-apply) command. Substitute `ORG_NAME` with your org name. ```yaml YAML theme={null} kind: policy name: prometheus-federate description: prometheus-federate tags: {} bindings: - permissions: - readMetrics principalLinks: - /org/ORG_NAME/serviceaccount/prometheus-federate target: all targetKind: org ``` #### In the Prometheus Configuration 1. **Configure Scrape Job:** * Add the following scrape configuration to your Prometheus instance: ```yaml theme={null} scrape_configs: - job_name: 'federate' scrape_interval: 1m honor_labels: true scheme: https metrics_path: '/metrics/org/${org}/api/v1/federate' params: 'match[]': - '{__name__=~".+"}' # Adjust this matcher as needed authorization: type: Bearer credentials: "${CPLN_SERVICE_ACCOUNT_TOKEN}" # Token value from env variable static_configs: - targets: - 'metrics.cpln.io' ``` * Replace `${org}` with the actual organization name. * Ensure `${CPLN_SERVICE_ACCOUNT_TOKEN}` is set as an environment variable containing the token from the `prometheus-federate` service account. ### Verification * After configuring the scrape job, verify that Prometheus is successfully collecting metrics from the source organization by checking the Prometheus UI. * You should be able to see the same metrics in the Prometheus UI as you would in the Control Plane managed Grafana instance. ### Additional Notes * Adjust the `match[]` parameter in the scrape configuration to filter the metrics as needed. * Repeat the steps for additional organizations by creating separate service accounts and policies. * Egress charges apply to metrics scraped from Control Plane. ### Cleanup * Delete the scrape job in the Prometheus instance. * Delete the policy and service account in the source organization. # CI/CD Examples Source: https://docs.controlplane.com/guides/gitops CI/CD integration examples for GitHub Actions, GitLab CI, Bitbucket Pipelines, and CircleCI. Covers building, testing, and deploying with Terraform or CLI. Below are CI/CD examples for [GitHub](https://github.com), [GitLab](https://gitlab.com), [BitBucket](https://bitbucket.org), and [CircleCI](https://circleci.com). The examples illustrate using pipelines to automate the build, test and deploy phases. Two examples per source control system are provided. One using [Terraform](https://registry.terraform.io/providers/controlplane-com/cpln/latest/docs) and the other using the [CLI](/cli-reference/overview). The `README.md` in the examples provide instructions for using the respective pipeline. These examples are provided as a starting point and your own unique delivery and/or deployment requirements will dictate the steps needed in your situation. ## Terraform Control Plane provides a Terraform plugin allowing you to build your Control Plane infrastructure declaratively. The following examples include the plugin installation instructions, containerizing and pushing an app to the org’s private [image registry](/reference/image) and a sample Terraform configuration file defining a [GVC](/reference/gvc) and a [workload](/reference/workload). ### Examples * [GitHub](https://github.com/controlplane-com/github-actions-example-terraform) * [GitLab](https://gitlab.com/controlplane-com/gitlab-pipeline-example-terraform) * [Bitbucket](https://bitbucket.org/controlplane-com/bitbucket-pipeline-example-terraform) - The GitHub and Bitbucket example uses the [Terraform Cloud](https://app.terraform.io/) to store the state file. - The GitLab example leverages their [managed Terraform state backend](https://docs.gitlab.com/ee/user/infrastructure/terraform_state.html) to store the state file. ## CLI The [CLI apply](/cli-reference/commands/apply) command creates and updates Control Plane resources. The command can be used in a pipeline to manage the deployment of an application. It takes as an input a JSON or YAML file containing the properties of the resource to manage. The command can accept a YAML file containing multiple resources. Each resource must be separated using `---`. Resources in a single `cpln apply` invocation are applied in dependency order automatically (e.g., a [GVC](/reference/gvc) is applied before the [workload](/reference/workload) that references it), so related resources can share a file in any order. If the name of an existing resource is changed, the `cpln apply` command will create a new resource. Any orphaned resources will need to be manually deleted. The examples below include installation of the [CLI](/cli-reference/installation), containerizing and pushing an application to the org's private repository. The examples contain two sample YAML files. One that manages a GVC, and one for a workload. ### Examples * [GitHub](https://github.com/controlplane-com/github-actions-example-cli) * [GitLab](https://gitlab.com/controlplane-com/gitlab-pipeline-example-cli) * [Bitbucket](https://bitbucket.org/controlplane-com/bitbucket-pipeline-example-cli) * [CircleCI](https://github.com/controlplane-com/circle-ci-pipeline-example-cli) * [Google Cloud Build](https://github.com/controlplane-com/google-cloud-build-example-cli) Samples of existing resources can be exported using the console or the CLI. These samples can assist when defining resources for your application. Using the console, after selecting a resource, there will be an `Export` pull down button in the upper right corner. Select JSON or YAML to download the file. Using the CLI's `get` command for each resource, the `-o` flag can output the resource as JSON or YAML. For example: `cpln gvc get GVC_NAME -o yaml-slim --org ORG_NAME` # Invite Users Source: https://docs.controlplane.com/guides/invite-users Invite new users to your org and assign them to groups using the Console UI, CLI, or CSV upload. ## Overview New users can be invited to join the current [org](/reference/org) and optionally added to a [group](/reference/group). ## Invite using the UI Console 1. Click `Users` in the left menu and then click the `Invite` tab at the top. 2. Enter an email address and select a group to assign the user to. 3. Click `Add to Invitation List`. 4. If an email address was entered by mistake, it can be removed by clicking the `Remove` link. 5. After entering all the users to invite, click `Confirm Invitations`. The users will be emailed an onboarding link to join your [org](/reference/org). The invited users will be placed in the `Pending Invites` table. If an invite was sent by mistake, you can delete the invite by clicking the `Delete` link. ## Invite using the CLI Refer to the [user invite](/cli-reference/commands/user#user-invite) command for details and examples on how to invite a user using the CLI. ## Upload Users A list of users to invite can be uploaded using the console. A `.csv` file in the following format is required: `USER_EMAIL,GROUP_TO_ASSIGN` Example: ```csv theme={null} cpln-user@example.com,none cpln-admin@example.com,superusers cpln-viewer@gmail.com,viewers ``` ### Instructions 1. Click `Users` in the left menu and then click the `Invite` tab at the top 2. Click the `Upload from CSV` button and select the file containing the list of users to invite. 3. Click `Confirm Invitations`. The users will be emailed an onboarding link to join your [org](/reference/org). The invited users will be placed in the `Pending Invites` table. If an invite was sent by mistake, you can delete the invite by clicking the `Delete` link. # Jenkins Plugin Source: https://docs.controlplane.com/guides/jenkins-plugin Use the Control Plane Jenkins Plugin to dynamically provision and deprovision Jenkins agent workloads on Control Plane based on job queue load. ## Overview The Control Plane Jenkins Plugin manages Jenkins agent workloads on Control Plane dynamically — agents are provisioned when jobs enter the queue and deprovisioned when they go idle. Each agent runs as a Control Plane workload inside a GVC, connects back to the Jenkins controller over WebSocket, processes jobs, and is then removed automatically. This eliminates permanently-running build infrastructure. Agents scale up as job load increases and scale back to zero when idle. Control Plane Plugin installed in Jenkins ## Prerequisites * A running Jenkins controller (version **2.462.3** or later) * A Control Plane account and organization * An API key with the following permissions on your organization: * List orgs * List GVCs * List volume sets *(in the configured GVC)* * List identities *(in the configured GVC)* * Read, create, and delete workloads *(in the configured GVC)* * A GVC with **exactly one location** — the plugin deploys one agent replica per cloud configuration, and multi-location GVCs are not supported ## Installation The plugin is distributed as an `.hpi` file. You can either build it from source or use the pre-built artifact included in the repository. ### Option A — Build from source 1. Clone the repository: ```sh theme={null} git clone git@github.com:controlplane-com/cpln-jenkins-plugin.git ``` 2. Install [Maven](https://maven.apache.org/download.cgi) and Java 21. 3. From the project root, build the plugin: ```sh theme={null} mvn clean install ``` The `.hpi` file will be generated at: ``` target/cpln-jenkins-plugin/cpln-jenkins-plugin-2.0.0.hpi ``` ### Option B — Use the pre-built artifact A pre-built `.hpi` is included in the repository under `docs/artifacts/` for convenience. Replace it with a build from source before deploying to production. ### Installing the HPI into Jenkins 1. Go to **Manage Jenkins → Plugins → Advanced**. 2. In the **Deploy Plugin** section, click **Choose File** and select the `.hpi` file. 3. Click **Deploy**. 4. Restart the Jenkins controller and log back in. 5. Go to **Manage Jenkins → Plugins → Installed Plugins** and verify **Control Plane Plugin** is listed and enabled. After installation, enable WebSocket agent connections: * Go to **Manage Jenkins → Security**. * Under **Agents**, set the **TCP port for inbound agents** to **Random**. * Save. ## Configuration ### Disable the built-in executor Before creating cloud configurations, prevent Jenkins from running jobs on its own node: * Go to **Manage Jenkins → Nodes → Built-In Node → Configure**. * Set **Number of executors** to `0`. * Save. This ensures all jobs are routed to cloud agents rather than the controller. ### Create a cloud configuration 1. Go to **Manage Jenkins → Clouds → New cloud**. 2. Enter a name (e.g., `cpln-us-west`) and select **Control Plane** as the cloud type, then click **Create**. 3. Paste your **API Key**. The plugin will automatically populate the **Org** field with the available organizations. 4. Select the **Org** — this will populate the **GVC** dropdown. 5. Select the **GVC**. Only single-location GVCs are supported. 6. Set the **agent workload name** (e.g., `west-generic`). This is the name used when creating the agent workload in your GVC. Cloud configuration form ### Cloud configuration options | Setting | Description | | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Volume Set** *(optional)* | A shared volume set from the selected GVC. Must be of type `shared`. If set, the **Volume Set Path** must also be set. | | **Volume Set Path** *(optional)* | Mount path inside the agent container (e.g., `/mnt/workspace`). | | **Identity** *(optional)* | A workload identity from the selected GVC, assigned to the agent workload for access to Control Plane secrets or cloud resources. | | **Labels** | Space-separated labels used for job routing (e.g., `cpln linux-build`). Pattern: `()|(([a-z][-a-z0-9]+)( [a-z][-a-z0-9]+)*)` | | **Allow jobs without labels** | When checked, unlabeled jobs can run on this cloud. When unchecked, only jobs with a matching label will be picked up. | | **Number of executors** | Maximum concurrent jobs per agent. Set to `0` to disable the cloud instance. | | **Unique agent workloads** | When checked, Jenkins provisions a dedicated agent workload per job instead of sharing agents across concurrent jobs. See [Unique Agents](#unique-agents) below. | | **CPU / Memory** | Resources for each agent workload. Defaults: 200 millicores / 512 MiB. Minimums: 50m CPU / 128 MiB memory. | | **Idle timeout** | Minutes an agent can be idle before being deprovisioned. Minimum 1 minute. | | **Jenkins inbound agent image** | Docker image for the agent. Defaults to [`jenkins/inbound-agent`](https://hub.docker.com/r/jenkins/inbound-agent). Must be compatible with standard Jenkins agent environment variables. | | **Jenkins controller URL** | The URL the agent workload uses to reach the Jenkins controller (must be externally resolvable from the GVC location). | The Jenkins inbound agent is a Java process. Setting CPU below 50m or memory below 128 MiB will cause agent startup failures or OOM kills. Values of 200m CPU and 256 MiB memory or higher are recommended. #### Volume sets When using a volume set, it **must be of type `shared`**. Volumes of type `ext4` or `xfs` are not supported with standard workloads. Volume set config in Jenkins cloud #### Unique agents When **Unique agent workloads** is enabled, Jenkins provisions a dedicated agent workload for each job instead of routing multiple jobs to a shared agent. This allows jobs to run fully concurrently regardless of the per-agent executor limit. Set the cloud's **Number of executors** to `1` when using unique agents so each agent processes exactly one job at a time. Some over-provisioning of agents is expected on burst loads, short job durations, or when agents take time to start. Unused agents are cleaned up automatically after the idle timeout expires. Unique agents configuration Unique agents scaling behavior ## How It Works ### Agent lifecycle 1. A job enters the Jenkins queue. 2. The plugin provisions a new agent workload in the configured GVC. 3. The workload starts, connects back to the Jenkins controller over WebSocket using the configured controller URL. 4. Once connected, the agent picks up the queued job and processes it. 5. After the job completes, the agent remains idle for the configured idle timeout period. 6. If no new jobs arrive within that window, the plugin deletes both the Jenkins agent and the corresponding Control Plane workload. Agent being provisioned Agent online and processing a job ### Agent container properties Agent workloads receive standard Jenkins environment variables (`JENKINS_URL`, `JENKINS_AGENT_NAME`, `JENKINS_SECRET`, etc.) as container properties, enabling the inbound agent to authenticate and connect automatically. The running agent workload is also visible directly in the Control Plane console: Agent workload in Control Plane GVC ## Label-Based Job Routing The plugin integrates with Jenkins' standard label system, letting you control exactly which cloud handles each job. This is especially useful in multi-cloud environments. ### Configuring labels Set labels in the **Labels** field of a cloud configuration (space-separated). Example: `cpln linux-build`. ### Restricting a job to Control Plane 1. Open the job configuration. 2. Check **Restrict where this project can be run**. 3. Enter a label expression matching one of the cloud's labels (e.g., `cpln`). ### Multi-cloud example | Cloud | Labels | Allow unlabeled jobs | | ------------- | ------ | -------------------- | | Control Plane | `cpln` | No | | Kubernetes | `k8s` | No | | EC2 | `ec2` | Yes | | Job label expression | Runs on | | -------------------- | ----------------------------------------- | | `cpln` | Control Plane only | | `k8s` | Kubernetes only | | *(no label)* | EC2 (only cloud accepting unlabeled jobs) | ### Pipeline jobs Use the `agent` directive with a label expression: ```groovy theme={null} pipeline { agent { label 'cpln' } stages { stage('Build') { steps { sh 'echo "Running on Control Plane agent"' } } } } ``` To target different clouds for different stages: ```groovy theme={null} pipeline { agent none stages { stage('Build on CPLN') { agent { label 'cpln' } steps { sh 'echo "Running on Control Plane"' } } stage('Build on K8s') { agent { label 'k8s' } steps { sh 'echo "Running on Kubernetes"' } } } } ``` ## Automatic Cleanup and Health Monitoring The plugin includes several background mechanisms to recover from failures and prevent resource leaks. ### Workload reconciler Runs every 60 seconds to detect and remove orphan workloads — Control Plane workloads that have no corresponding Jenkins node. A configurable grace period prevents premature cleanup of workloads that are still starting up. ### Stuck agent detection Detects agents that are online but have stalled JVMs (high response latency, no channel activity). Agents that remain stuck for the configured grace period with no active builds are cleaned up automatically. ### Stalled build detection Detects builds where the job script finished but Jenkins never received the completion event. When log output stops, the channel becomes stale, and no progress is observed for an extended period, the plugin forces build interruption and cleans up the agent. These mechanisms handle scenarios including: * Handshake failures * OOM kills * Fast exits before the agent registers * Abnormal WebSocket terminations * Controller restarts * JVM stalls from CPU overload ## Important Notes * **Removing an agent from Jenkins** also removes the corresponding workload from the Control Plane GVC. * **Forcefully removing a workload from Control Plane** (outside Jenkins) may cause the plugin to recreate it if there are jobs in the queue for that cloud configuration. * **Unlabeled jobs** may be picked up by any cloud instance with `Allow jobs without labels` enabled. If no agents are running, one will be provisioned to handle the job. * **Jobs with no matching cloud** remain queued indefinitely until either the cloud configuration is updated or the job's label requirement is changed. * **Agent workloads use WebSocket** to connect to the Jenkins controller. The controller must be reachable over HTTP/HTTPS from the GVC's location. * **TLS between controller and agents** is not configured by the plugin itself. Refer to standard Jenkins TLS documentation for securing the controller endpoint: * [Enabling HTTPS in Jenkins (Baeldung)](https://www.baeldung.com/ops/jenkins-enable-https) * [Jenkins community HTTPS discussion](https://community.jenkins.io/t/what-is-best-recommended-way-to-enable-https-into-jenkins/26878) * **Provisioning cooldown** (default: 60 seconds) prevents over-provisioning when agents take time to connect. For unique agents, this stops Jenkins from creating duplicate agents for the same job. The cooldown clears automatically once an agent successfully connects. * **Jenkins provisioning parameters** `MARGIN`, `MARGIN0`, and `MARGIN_DECAY` can be used to fine-tune load-based provisioning behavior. Consult the Jenkins documentation for details. ## External References Control Plane platform documentation Official Jenkins documentation Default agent Docker image used by the plugin Source code, issues, and releases # PrivateLink with RDS Source: https://docs.controlplane.com/guides/native-networking/aws-privatelink/privatelink-rds-terraform Provision an AWS RDS instance with PrivateLink for secure private connectivity to Control Plane workloads using Terraform. ## Overview Follow the steps below to provision (or integrate with existing) an [RDS](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Welcome.html) instance with a [PrivateLink](https://docs.aws.amazon.com/vpc/latest/privatelink/what-is-privatelink.html) endpoint in AWS for Control Plane connectivity. ## Create using Control Plane Terraform ### Modes 1. **Create New Infrastructure (default)** * RDS, Secret Manager Secret and VPC infrastructure will be created along with networking resources. 2. **Existing Infrastructure** * Uses your preexisting RDS, Secret Manager Secret and VPC infrastructure to create networking resources. * To use this mode, you must provide your RDS ARN and Secret Manager Secret ARN. ### What's Created * VPC & Subnets * RDS PostgreSQL Instance (multi-AZ) * Secrets Manager (secure credential storage) * RDS Proxy (connection pooling and failover) * Network Load Balancer * Lambda Function (dynamic IP updates) * PrivateLink Endpoint Service * RDS Proxy (connection pooling and failover) * Network Load Balancer * Lambda Function (dynamic IP updates) * PrivateLink Endpoint Service ### Prerequisites **Software Requirements:** * Install AWS [CLI](https://aws.amazon.com/cli/). * Install Terraform [CLI](https://developer.hashicorp.com/terraform) version `1.11.1` or later. * [Git](https://git-scm.com/) (for cloning the repository). **AWS Account Requirements:** * [Amazon Web Service (AWS)](https://aws.amazon.com/) account with billing enabled. * AWS IAM User/Role with appropriate permissions (VPC, RDS, Lambda, NLB, Secrets Manager, IAM, CloudWatch, etc.). * Deploy resources in the same region as your Control Plane [workload](/concepts/workload). **Existing RDS Requirements:** * VPC, subnets, and RDS infrastructure already created and available. * Create Secrets Manager Secret with database credentials in the following JSON format. ```JSON theme={null} { "username": "your_db_username", "password": "your_db_password" } ``` * Your RDS instance's security group must allow inbound PostgreSQL traffic (port 5432) from the VPC CIDR, so the RDS Proxy created by the Terraform can reach the database. ### Step 1 - Clone Control Plane Terraform ```bash theme={null} git clone https://github.com/controlplane-com/cpln-rds-producer cd cpln-rds-producer ``` * This [repository](https://github.com/controlplane-com/cpln-rds-producer) contains Terraform modules for provisioning AWS infrastructure that integrates with Control Plane via PrivateLink. ### Step 2 - Create Configuration File * Create a `terraform.tfvars` file in the root of the cloned repository. * **For using Create New Infrastructure mode:** ```hcl terraform.tfvars theme={null} aws_region = "us-west-2" db_username = "postgres" db_password = "SecurePassword123!" ``` * **For using Existing Infrastructure mode:** ```hcl terraform.tfvars theme={null} db_instance_arn = "arn:aws:rds:us-west-2:123456789012:db:my-db" secret_arn = "arn:aws:secretsmanager:us-west-2:123456789012:secret:my-secret" ``` ### Step 3 - Deploy Infrastructure * Run the Terraform with your configuration: ```bash theme={null} terraform init terraform plan terraform apply ``` * The Terraform will automatically provision all necessary resources and output your PrivateLink endpoint service name. If `terraform apply` fails with `Cannot find version X for postgres`, AWS has retired that PostgreSQL minor version. Set the `rds_engine_version` variable to a currently offered version (list them with `aws rds describe-db-engine-versions --engine postgres --query 'DBEngineVersions[].EngineVersion'`). * For more information, refer to the [repository README](https://github.com/controlplane-com/cpln-rds-producer#control-plane-rds-producer-terraform-modules) ## Next Steps * Contact [support@controlplane.com](mailto:support@controlplane.com) with your service name and region. * Control Plane will use this to create the consumer-side endpoint connection. * No manual acceptance is required: the endpoint service is created with automatic acceptance enabled and only allows connections from Control Plane's AWS account (the `allowed_principal_arn` variable). * Proceed to follow the [Native Networking Setup](/guides/native-networking/native-networking-setup) in Control Plane. ## Connecting to the Database * **TLS is required.** The RDS Proxy is configured with `require_tls`, so clients must connect with SSL enabled (e.g., `sslmode=require` for PostgreSQL clients). * Authenticate using the database username and password stored in the Secrets Manager Secret. * Connect to the PrivateLink endpoint on port `5432`. # Native Networking Setup Source: https://docs.controlplane.com/guides/native-networking/native-networking-setup Configure Native Networking within an identity using AWS PrivateLink or GCP Private Service Connect to securely access cloud resources. ## Overview Follow these steps to configure Native Networking within an [identity](/reference/identity) with [AWS PrivateLink](https://docs.aws.amazon.com/vpc/latest/privatelink/what-is-privatelink.html) or [GCP Private Service Connect](https://cloud.google.com/vpc/docs/private-service-connect). ### Prerequisites * Review the [identity](/reference/identity) reference page. * Create a resource with one of the following: * For AWS, create a resource with PrivateLink configured [(see example)](/guides/native-networking/aws-privatelink/privatelink-rds-terraform). * For GCP, create a resource with Private Service Connect configured [(see example)](/guides/native-networking/private-service-connect/cloud-sql). * Contact Control Plane support to associate the resource's service name (AWS) or service attachment (GCP) to the org. ## Create using the UI Console 1. Create or edit an [identity](/guides/create-identity). 2. Under the `Native Networking` submenu, click on `AWS PrivateLink` or `GCP Service Connect`. 3. Click on `Add AWS Resource` or `Add GCP Resource`. 4. In the `FQDN` (Fully Qualified Domain Name) field, enter the hostname of the cloud resource. 5. In the `Name` field, enter another unique name that does not match the FQDN. Your workload can reference either the FQDN or the Name to connect using the attached [identity](/reference/identity). If the resource is configured with TLS, the FQDN must be used. You can configure multiple Native Networking connections to different resources by specifying different port numbers for each resource. If the same port is required, a new PrivateLink or Private Service Connect endpoint must be configured. 6. Optional: If you have a connected [cloud account](/reference/cloudaccount), select the account that contains the endpoint service. 7. Enter the endpoint service name (`Service name` for *AWS* or `Service attachment name` for *GCP*). If you selected a cloud account, you can also click `...` to browse for it. 8. Add at least one port number. 9. Click `Add` to finish configuring Native Networking. 10. Click `Update` to save. ## Next Steps * Finish creating or updating your [identity](/guides/create-identity). * The identity can be associated with one or more [workloads' identity](/reference/workload#identity) setting. # PSC with Cloud SQL Source: https://docs.controlplane.com/guides/native-networking/private-service-connect/cloud-sql Create a Google Cloud SQL instance with Private Service Connect (PSC) for secure private connectivity to Control Plane workloads. ## Overview Follow the steps below to create a [Cloud SQL](https://cloud.google.com/sql/docs/introduction) instance with [Private Service Connect](https://cloud.google.com/vpc/docs/private-service-connect) (PSC) enabled within Google Cloud. ## Create using Terraform ### What's Created * Necessary APIs enabled * VPC with firewall rule * Private Service Access (PSA) enabled with IP reserved * Cloud SQL PostgreSQL Instance with Private Service Connect (PSC) enabled ### Prerequisites **Software Requirements:** * Install Google Cloud [CLI](https://cloud.google.com/sdk/docs/install). * Install Terraform [CLI](https://developer.hashicorp.com/terraform). * [Git](https://git-scm.com/) (for cloning the repository). **GCP Account Requirements:** * [Google Cloud Platform (GCP)](https://cloud.google.com/) account with billing enabled. * Deploy resources in the same region as your Control Plane [workload](/concepts/workload). ### Step 1 - Clone Control Plane Terraform ```bash theme={null} git clone https://github.com/controlplane-com/gcp-psc-producer-automation cd gcp-psc-producer-automation ``` * This [repository](https://github.com/controlplane-com/gcp-psc-producer-automation) contains Terraform modules for provisioning GCP infrastructure that integrates with Control Plane via Private Service Connect. ### Step 2 - Create Configuration File * Create a `terraform.tfvars` file in the root of the cloned repository. ```hcl terraform.tfvars theme={null} project_id = "your-gcp-project-id" region = "us-central1" db_username = "postgres" db_password = "SecurePassword123!" ``` ### Step 3 - Deploy Infrastructure * Run the Terraform with your configuration: ```bash theme={null} terraform init terraform plan terraform apply ``` * The Terraform will automatically provision all necessary resources and output your service attachment. * For more information, refer to the [repository README](https://github.com/controlplane-com/gcp-psc-producer-automation?tab=readme-ov-file#readme) Follow [Next Steps](/guides/native-networking/private-service-connect/cloud-sql#next-steps). ## Create using the GCP Console ### Prerequisites * [Google Cloud Platform (GCP)](https://cloud.google.com/) account. * Install the latest Google Cloud [CLI](https://cloud.google.com/sdk/docs/install). * Existing GCP project * Enable the following APIs in your GCP project: * `SQL Admin` * `Compute Engine` * `Service Networking` ### Step 1 - Create a VPC 1. Navigate to `VPC Networks` and select `Create VPC Network`. 2. Name the VPC, enable the necessary firewall rules for your intended use and click `Create`. A subnet is not required for PSC with Cloud SQL. Cloud SQL lives in Google's managed data center and the VPC is only required for using a private IP, routing, and firewall rules. ### Step 2 - Create Cloud SQL 1. Navigate to `SQL` and select `Create Instance`. 2. Choose your database engine (ex. PostgreSQL). 3. Select your preferred SQL edition. 4. Name your SQL instance and choose a secure password. Ensure the region selected in the Cloud SQL matches the region in your Control Plane [workload](/concepts/workload). If the regions are not the same, the Private Service Connection will fail. 5. Scroll to the bottom of the page and click `Show Configuration Options` to drop down more options. 6. Select the `Connections` tab, disable Public IP and enable Private IP. 7. Enable Private Service Access (PSA) * Under Private IP, a drop down should appear to configure PSA. * Choose your VPC and select `Set up connection`. * You can allocate an IP range by either automatically allocating or by selecting your own range * Select `Continue` and `Create Connection` 8. Once the connection is created you can create your instance Private Service Connect enablement is not currently supported in the GCP console. After the instance is created, you must [edit the Cloud SQL instance](/guides/native-networking/private-service-connect/cloud-sql#edit-existing-cloud-sql-instance) using the [CLI](https://cloud.google.com/sdk/docs/install). ## Edit existing Cloud SQL Instance The following steps will show how to enable Private Service Connect with the allowed consumer project for Control Plane in an existing Cloud SQL instance. ### Prerequisites * Cloud SQL created with private IP only (no public IP) * Private Service Access (PSA) enabled with reserved IP (required when using private IP only) ### Step 1 - Verify current project using the CLI Ensure you are in the correct project before patching the Cloud SQL instance. ```bash theme={null} gcloud config set project YOUR_PROJECT_ID ``` ### Step 2 - Patch the Cloud SQL instance Use the `gcloud` CLI to edit the Cloud SQL instance. When patching the Cloud SQL instance, you must specify the allowed consumer project id(s) that will be used to consume the instance. Use the following `gcloud` command: ```bash theme={null} gcloud sql instances patch INSTANCE_NAME \ --enable-private-service-connect \ --allowed-psc-projects=cpln-prod01 ``` If you need to change the allowed consumer projects in the future, use the same command and omit the `---enable-private-service-connect` flag. Once patching is complete, your Cloud SQL instance will have PSC enabled with the necessary Control Plane project allowed to connect to your [workload](/concepts/workload). * You can find your service attachment in the console under `Connections` for your Cloud SQL instance. ## Next Steps * Contact [support@controlplane.com](mailto:support@controlplane.com) with your service attachment and region. * Control Plane will use this to create the consumer-side endpoint connection. When using Cloud SQL, there is no manual required acceptance for new connections created by specified allowed consumer projects. * Proceed to follow the [Native Networking Setup](/guides/native-networking/native-networking-setup) in Control Plane. # How-to Guides Source: https://docs.controlplane.com/guides/overview Step-by-step guides for common tasks on Control Plane Learn how to accomplish specific tasks on Control Plane with these practical, step-by-step guides. Each guide walks you through a complete workflow from start to finish. Configure the CLI for headless environments and automation Kubernetes Operator and third-party integrations Set up domains, CDN, agents, and resource protection Create orgs, GVCs, workloads, identities, and more Deploy with cpln apply, Helm, Docker Compose, and more Implement GitOps workflows and environment promotion Build, push, pull, and copy container images Set up AWS PrivateLink and GCP Private Service Connect Configure metrics collection and export Create a cloud development environment in under five minutes Connect, exec, copy files, and port-forward to workloads # Create a Policy Source: https://docs.controlplane.com/guides/policy Create access control policies via Console UI and CLI. Covers principal targeting, permission binding, resource scoping, and policy testing. ## Overview Follow the steps below to create a [policy](/reference/policy) defining access permissions to any Control Plane resource for any principal ([user](/reference/user), [service account](/reference/serviceaccount), [group](/reference/group), and [identity](/reference/identity)). ## Prerequisites * Review the [policy](/reference/policy) reference page. * Have [permissions](/reference/policy#permissions) to create a [policy](/reference/policy). * Optional: Install the [CLI](/cli-reference/installation). ## Create using the Console 1. Create a new policy using one of the following methods: * Click `Policies` in the left menu, then click `New`. * Click the `Create` dropdown in the upper right corner and select `Policy`. 2. Configure the policy: * Enter a policy name and optional description. * Click `Target` and select the target kind you want to control access to. * You have the option to select specific resources or target all the resources in your [org](/reference/org) for that kind by turning on the `Target All Items` switch. * If you are targeting all the resources, skip the next step. 3. Select resources: * Choose one or both of the following methods to select resources: * For direct assignment: Click `Items` and then click the `Add` button and select from the available resources. Click `OK`. * For dynamic assignment: Click `Query` and then configure match rules using [tag queries](/core/misc#tags), properties, or relations. Click `Ok`. 4. Add a binding: * Select `Bindings`. * At least one binding is required. Click `Add Binding`. * Select one or more permissions. These permissions are specific to the selected kind. * Browse through the principal tabs and select at least one principal. Click `OK`. * If required, add additional bindings. Each binding must have a unique set of permissions. Click `Create`. * The policy has been created and is now active. ## Create using the CLI Refer to the [policy create](/cli-reference/commands/policy#policy-create) and [policy add-binding](/cli-reference/commands/policy#policy-add-binding) commands for details and examples. ## Next Steps Control Plane [policies](/reference/policy) allow for fine-grained authorization to the resources within your [org](/reference/org). By granting principals only the permissions they need, policies limit the information they can view and the actions they can perform. Most applications and services running on the platform are mission-critical, so authorized principals should have only the access they require. # Pull Images from Registries Source: https://docs.controlplane.com/guides/pull-image Configure workloads to pull container images from public and private registries. Control Plane can pull container images from any Docker-compatible registry. Public images work out of the box, while private registries require [pull secrets](/concepts/gvc#pull-secrets). ## When to use this Pull from **Docker Hub private repos**, **Amazon Elastic Container Registry (ECR)**, **Google Container Registry (GCR)**, **Google Artifact Registry (GAR)**, **Azure Container Registry (ACR)**, or **GitHub Container Registry (GHCR)**. Use images from another Control Plane organization Connect to self-hosted or enterprise container registries Control access to private images with pull secrets ## Prerequisites * `create` and `use` on [Secrets](/reference/secret) * `edit` on [GVC](/reference/gvc) * `edit` or `create` on [Workload](/reference/workload) The CLI is optional but helpful. See [Installation](/cli-reference/installation). ## Pull from public registries Public images do not require pull secrets. Use these formats: | Registry | Format | | ------------------------- | ------------------------------------------------ | | Docker Hub | `IMAGE_NAME:TAG` | | Amazon ECR Public | `public.ecr.aws/REGISTRY-ALIAS/IMAGE:TAG` | | Google Artifact Registry | `LOCATION-docker.pkg.dev/PROJECT/REPO/IMAGE:TAG` | | GitHub Container Registry | `ghcr.io/OWNER/IMAGE:TAG` | ## Pull from private registries 1. Navigate to **Secrets** in the left menu. 2. Click **New** or use the **Create** dropdown. 3. Enter a name for the secret. 4. Select the secret type: * [**Docker**](/guides/create-secret/docker) for Docker Hub, Azure Container Registry, Google Artifact Registry, GitHub Container Registry, or another Control Plane org * [**ECR**](/guides/create-secret/ecr) for Amazon ECR * [**GCP**](/guides/create-secret/gcp) for Google Container Registry 5. Click `Data`, then enter the credentials. 6. Click **Create**. For Docker Hub or compatible registries: ```bash theme={null} cpln secret create-docker --name my-docker-secret \ --file /path/to/auths.json ``` For Amazon ECR: ```bash theme={null} cpln secret create-ecr --name my-ecr-secret \ --access-key AKIAXXXXXXXX \ --secret-key xxxxxxxxxxxxx \ --repo AWS_ACCOUNT_ID.dkr.ecr.REGION.amazonaws.com/REPO_NAME \ --role-arn arn:aws:iam::123456789:role/ecr-role ``` For GCP: ```bash theme={null} cpln secret create-gcp --name my-gcp-secret \ --file /path/to/service-account.json ``` Images from your own org’s Control Plane registry do not need a pull secret. 1. Navigate to your GVC. 2. Click **Pull Secrets**. 3. Click **Add Pull Secret**, select your secret, and click `OK`. 4. Click **Update**. ```bash theme={null} cpln gvc update my-gvc --set spec.pullSecretLinks+=SECRET_NAME ``` Reference the image in your workload using the appropriate format: | Registry | Image Format | | ------------------------- | ------------------------------------------------ | | Control Plane (same org) | `//image/IMAGE:TAG` | | Control Plane (cross-org) | `ORG.registry.cpln.io/IMAGE:TAG` | | Docker Hub | `IMAGE:TAG` | | Amazon ECR | `ACCOUNT.dkr.ecr.REGION.amazonaws.com/IMAGE:TAG` | | Azure Container Registry | `REGISTRY.azurecr.io/IMAGE:TAG` | | Google Container Registry | `gcr.io/PROJECT/IMAGE:TAG` | | GitHub Container Registry | `ghcr.io/OWNER/IMAGE:TAG` | ## Pull from Control Plane registry ### Same organization No pull secret is needed: ```yaml theme={null} containers: - name: my-container image: //image/my-app:v1 ``` ### Cross-organization 1. [Create a Docker secret](/reference/secret#control-plane) with the other org’s credentials. 2. Add it as a pull secret to your GVC. 3. Reference the image: ```yaml theme={null} containers: - name: my-container image: other-org.registry.cpln.io/my-app:v1 ``` ## Troubleshooting 1. Verify the secret credentials are correct. 2. Check the secret is associated with the GVC. 3. Ensure the secret type matches your registry. Verify the image name and tag are correct. Check the registry for the exact image path. * Ensure the source org has granted access and your Docker secret has the correct credentials. * Learn more about [configuring a secret](/reference/secret#control-plane) for pulling images from other Control Plane orgs. ## Next steps Push images to Control Plane registry Copy images between organizations Deploy containers with your images Learn about secret types # Push Images to Registry Source: https://docs.controlplane.com/guides/push-image Build and push container images to Control Plane's private registry. Push container images to your organization's private registry on Control Plane. Use Docker, the CLI, or any Docker-compatible client. ## When to use this Build and push images in automated pipelines Push locally-built images for testing Store images in your org's secure registry Build automatically with buildpacks ## Prerequisites Install the Control Plane CLI. See [Installation](/cli-reference/installation). Install [Docker](https://www.docker.com). The Buildx plugin is recommended (used by default when available) but no longer required for single-platform builds. Docker is only needed for local builds; `cpln image build --remote` builds remotely instead. You need push permission on images. See [Image Permissions](/reference/image#minimum-policy). As of CLI v3.7.2, `cpln image build` uses `docker buildx build`. As of CLI v3.9.0, it falls back to legacy `docker build` when Buildx is unavailable, so Buildx is optional for single-platform builds. Multi-platform builds (comma-separated `--platform` values) still require Buildx. Verify Buildx is available: ```bash theme={null} docker buildx version ``` If not installed, add the plugin: ```bash theme={null} BUILDX_VERSION=v0.29.1 curl -sSL "https://github.com/docker/buildx/releases/download/${BUILDX_VERSION}/buildx-${BUILDX_VERSION}.linux-amd64" \ | install -m 0755 -D /dev/stdin ~/.docker/cli-plugins/docker-buildx ``` ## Build and push **Best for:** Local development, CI/CD pipelines, buildpack builds * Authenticates to your org's private registry automatically * Simple naming (`--name my-app:v1`) * Supports Dockerfile or Buildpacks * Builds remotely without Docker (`--remote`) ### With a Dockerfile The Dockerfile is automatically detected (defaults to `./Dockerfile`): ```bash theme={null} cpln image build --name my-app:v1 --push ``` Or specify a different path: ```bash theme={null} cpln image build --dockerfile ./path/to/Dockerfile --name my-app:v1 --push ``` Learn more about working with images: [CLI images guide](/cli-reference/get-started/images) | [Command reference](/cli-reference/commands/image) ### With build arguments Pass a value the Dockerfile reads with `ARG`, either directly or from your environment: ```bash theme={null} cpln image build --name my-app:v1 --push \ --build-arg NODE_VERSION=22 \ --build-arg GIT_SHA ``` See [Build Arguments](/cli-reference/get-started/images#build-arguments) for the forms it accepts. ### With a private registry or other build credential A build that installs from a private package registry needs a credential during the build, but not in the finished image. Store it as a secret in your org and mount it for the step that needs it: ```bash theme={null} # Once: store the credential cpln secret create-opaque --name npm-token --file ./npm-token.txt --encoding plain # Every build: mount it for a single RUN step cpln image build --name my-app:v1 --push \ --secret id=npmtoken,src=cpln://secret/npm-token ``` ```dockerfile theme={null} RUN --mount=type=secret,id=npmtoken,required=true \ NPM_TOKEN="$(cat /run/secrets/npmtoken)" npm ci ``` The value is mounted for that step only, so it never lands in the image or its layers. See [Build Secrets](/cli-reference/get-started/images#build-secrets). Do not pass a credential with `--build-arg` or `--env`. Those values become part of the image and can be recovered by anyone who can pull it. ### With Buildpacks Build automatically without a Dockerfile: ```bash theme={null} cpln image build --name my-app:v1 --push ``` Buildpacks detect your language and create an optimized image. See [Buildpacks conventions](/cli-reference/get-started/images#buildpacks-conventions) for language-specific requirements. ### Without Docker Add `--remote` to build remotely; the image is pushed for you: ```bash theme={null} cpln image build --name my-app:v1 --remote ``` Or build a repository directly: ```bash theme={null} cpln image build --name my-app:v1 --remote --repo https://github.com/my-org/my-app --branch main ``` See [Remote builds](/cli-reference/get-started/images#build-options) for details. **Best for:** Existing Docker workflows, custom build processes, external CI/CD systems * Requires manual authentication * Must use full registry path: `ORG.registry.cpln.io/IMAGE:TAG` ### Image name format When using Docker directly, images must use this format: ``` ORG_NAME.registry.cpln.io/IMAGE_NAME:TAG ``` Examples: * `my-org.registry.cpln.io/api:v1.0.0` * `my-org.registry.cpln.io/frontend:latest` * `my-org.registry.cpln.io/worker:abc123` ### Push steps Configure Docker to authenticate with your org's registry: ```bash theme={null} cpln image docker-login ``` This uses your default CLI profile. Set `CPLN_PROFILE` to use a different profile. Build with the correct tag format: ```bash theme={null} docker buildx build --platform=linux/amd64 \ -t my-org.registry.cpln.io/my-app:v1 . ``` Always target `linux/amd64` to ensure compatibility with Control Plane. If you have an existing image, tag it for your registry: ```bash theme={null} docker tag my-app:v1 my-org.registry.cpln.io/my-app:v1 ``` ```bash theme={null} docker push my-org.registry.cpln.io/my-app:v1 ``` ### CI/CD authentication For CI/CD pipelines without the CLI, authenticate with a service account: ```bash theme={null} echo $SERVICE_ACCOUNT_KEY | docker login my-org.registry.cpln.io -u '' --password-stdin ``` | Parameter | Value | | --------- | --------------------------- | | Registry | `ORG_NAME.registry.cpln.io` | | Username | `` (literal string) | | Password | Service account key | Store the service account key securely. If compromised, delete and regenerate it. See [Create a Service Account](/guides/create-service-account) for key generation. ## Reference images in workloads Once pushed, reference your images in workloads using the Control Plane image link format. **Important:** Do not use the Docker registry path (`org.registry.cpln.io/...`) in workload configurations. Always use the Control Plane image link format shown below. **Shorthand format (recommended):** ```text theme={null} //image/IMAGE_NAME:TAG ``` **Full format:** ```text theme={null} /org/ORG_NAME/image/IMAGE_NAME:TAG ``` **Example workload configuration:** ```yaml theme={null} containers: - name: my-container image: //image/my-app:v1 ``` Or with the full path: ```yaml theme={null} containers: - name: my-container image: /org/my-org/image/my-app:v1 ``` In the console, press `Ctrl+I` when configuring a workload to browse available images. ## Common workflows ### Build and deploy ```bash theme={null} # Build and push (Authenticates automatically) cpln image build --name my-app:v1.2.3 --push # Update workload cpln workload update my-app --set spec.containers.image=//image/my-app:v1.2.3 ``` ### CI/CD pipeline ```bash theme={null} # Build and push (Authenticates automatically) cpln image build --name my-app:$CI_COMMIT_SHA --push # Or, on a runner without a Docker daemon, build remotely cpln image build --name my-app:$CI_COMMIT_SHA --remote # Deploy (has the new image defined in the workload manifest container) cpln apply --file workload.yaml ``` ### Build with a stamped revision and a private registry credential ```bash theme={null} cpln image build --name my-app:$CI_COMMIT_SHA --push \ --build-arg GIT_SHA=$CI_COMMIT_SHA \ --secret id=npmtoken,src=cpln://secret/npm-token ``` Keeping the credential in an org secret means the pipeline never holds it: no registry token in the CI variables, and nothing to rotate in the pipeline when the credential changes. The same command works on a runner without Docker by swapping `--push` for `--remote`, where the build service resolves the secret itself. ## Troubleshooting For common issues with building and pushing images, see [Images Troubleshooting](/cli-reference/get-started/images#troubleshooting). ## Next steps Configure workloads to pull images Copy images between organizations Deploy your pushed images Image configuration details # Rate Limiting Source: https://docs.controlplane.com/guides/rate-limiting Configure rate limiting for workloads using the Envoy Rate Limit project with a Redis backend deployed on Control Plane. Rate limiting can be added to [Workloads](/reference/workload) by configuring an endpoint which serves the Envoy Rate Limit project. This endpoint receives a rate limit request from a configured Workload and decides if the request should be limited. Follow the steps below to configure your [Workload](/reference/workload) with a rate limiting policy. ## Prerequisites * An existing [Workload](/reference/workload) that requires rate limiting. * Review the Rate Limiting project's [README](https://github.com/envoyproxy/ratelimit). ## Configuration Steps Envoy's Rate Limit project is deployed as a [Workload](/reference/workload) at Control Plane and requires a local Redis [Workload](/reference/workload). Follow the steps below to download and apply a YAML manifest file which will deploy, create, and configure: * The Rate Limit [Workload](/reference/workload). * The Redis [Workload](/reference/workload). * An [opaque secret](/reference/secret#opaque) containing the rate limiting definition. * A [Workload Identity](/reference/identity) for the Rate Limit Workload. * A [Policy](/reference/policy) allowing the Rate Limit Workload to reveal the secret. ### Step One - Configure the Rate Limiting Workload * Download and review the [YAML manifest file](https://raw.githubusercontent.com/controlplane-com/examples/main/examples/rate-limiting/rate-limiting.yaml) that will be applied. * Using the UI or [CLI](/cli-reference/overview): * Create a [GVC](/reference/gvc) named `ratelimit`. If you choose a different GVC name, update the YAML manifest file accordingly. * Within that GVC, use the [cpln apply](/guides/cpln-apply) command to deploy the manifest file. The latest `envoyproxy/ratelimit` deployment can be obtained from the [envoyproxy/ratelimit Docker Hub page](https://hub.docker.com/r/envoyproxy/ratelimit/tags?page=1\&ordering=last_updated). The downloaded YAML manifest file uses the image `envoyproxy/ratelimit:5b6e65da`. If there is a newer tag, it can be substituted in the YAML file before executing the `cpln apply` command. ### Step Two - Customize the Config File The rate limit config file is stored as an [opaque secret](/reference/secret#opaque) named `ratelimit-config` that was created when applying the YAML manifest file. The configuration that was deployed with the manifest file sets a limit of 10 requests per minute for any request that contains an `authorization` header (see sample config below). To adjust the rate limit per your requirements, use the UI or CLI to edit the `ratelimit-config` secret using the pattern in the [ratelimit configuration documentation](https://github.com/envoyproxy/ratelimit#configuration). #### Sample Config ```yaml YAML theme={null} domain: cpln descriptors: - key: authorization rate_limit: unit: minute requests_per_unit: 10 ``` After saving the updated secret, it's recommended to perform a `Force Redeploy` of the `ratelimit` Workload to reload the config file. ### Step Three - Configure Rate Limiting on a Workload Add the following [tags](/core/misc#tags) to enable rate limiting on the desired [Workload](/reference/workload). * `cpln/rateLimitAddress` * **Required** * Use the `Global Endpoint` of the Rate Limiting [Workload](/reference/workload) that was configured in [step one](#step-one-configure-the-rate-limiting-workload). This endpoint can be obtained by browsing to the `Info` page of the `ratelimit` Workload. Include only the hostname without the `https://` protocol prefix. * `cpln/rateLimitScheme` * Optional. * Default is: `https`. * `cpln/rateLimitPort` * Optional. * Default is: `443`. * `cpln/rateLimitDomain` * Optional. * Default is: `cpln`. * This value corresponds to the domain entry within the config file that was configured in [step 2](#step-two-customize-the-config-file). * `cpln/rateLimitDescriptors` * Optional. Default is: `authorization`. * Allowed values: `authorization`, `host`, `path`. * This value corresponds to the descriptor entry within the config file that was configured in [step 2](#step-two-customize-the-config-file). After saving these tags and the Workload has deployed successfuly, the rate limits defined within the config file will be enforced. If a client hits the limit, an HTTP 429 (Too Many Requests) response will be returned. # Resource Protection Source: https://docs.controlplane.com/guides/resource-protection Prevent accidental deletion of critical resources by applying the `cpln/protected` tag. Control Plane prevents deletion of any resource that has the `cpln/protected` tag set to `true`. Use this tag to safeguard critical resources such as workloads and secrets from accidental deletion. To delete a protected resource, first remove the tag or change its value from `true`. ## Using the Console ### Add tag From nearly any resource within the Console, follow these steps to add the protected tag: 1. Click `Tags`. 2. Click the `Add` button. 3. In the blank row that was added: * Enter `cpln/protected` for the `Key`. * Enter `true` for the `Value`. 4. Click `Update`. ### Remove tag From nearly any resource within the Console, follow these steps to remove the protected tag: 1. Click `Tags`. 2. Click the delete icon for the row that contains the `cpln/protected` tag. 3. Click `Update`. You can also toggle resource protection by clicking the lock switch to the right of the `Actions` button. ## Using the CLI Most resource commands support the `tag` subcommand. The examples below add and remove the `cpln/protected` tag from a workload. Substitute `workload` with the target resource kind. ```bash Add tag theme={null} cpln workload tag WORKLOAD_NAME --tag cpln/protected=true --gvc GVC_NAME --org ORG_NAME ``` ```bash Remove tag theme={null} cpln workload tag WORKLOAD_NAME --remove-tag cpln/protected --gvc GVC_NAME --org ORG_NAME ``` # Container Internals Source: https://docs.controlplane.com/guides/sandbox/container-internals How the sandbox container boots, exposes the browser IDE and terminal, and routes traffic through the gateway proxy. ## Boot Sequence The container entrypoint starts the sandbox services in this order: 1. Seed `/root` from defaults on first boot 2. Create `/root/workspace` and symlink `/workspace` 3. Raise inotify limits 4. Start the gateway proxy (port 8888) 5. Start `sshd` 6. Run deferred install script (if present) 7. Start code-server (port 8443) 8. Start ttyd browser terminal (port 7681) 9. Start port/resource monitor 10. Snapshot installed packages for fork detection 11. Mark the sandbox ready ## Gateway Proxy Each sandbox runs a gateway proxy that routes browser IDE, terminal, and application traffic through a single public endpoint. | Path | Backend | Auth | Purpose | | :---------------- | :---------------------- | :------- | :--------------------------- | | `/_ide/` | code-server (8443) | Password | VS Code in browser | | `/_term/` | ttyd (7681) | Password | Browser terminal | | `/_port//` | Any process (N) | Password | Proxy to detected local port | | `/_callback//` | Any process (N) | None | OAuth/webhook callbacks | | `/_status` | Gateway | None | JSON runtime status | | `/_ports` | Gateway | None | JSON discovered ports | | `/healthz` | Gateway | None | Readiness probe | | `/` | App process (APP\_PORT) | None | Public application URL | The `/_ide/`, `/_term/`, and `/_port/` routes are password-protected via cookie session. The password is displayed in the connect panel. ## Ports | Port | Visibility | Purpose | | :----------------------- | :-------------------------- | :--------------- | | 8888 | Public | Gateway endpoint | | APP\_PORT (default 8080) | Local | User application | | 8443 | Local | code-server | | 7681 | Local | ttyd | | 22 | Private (port-forward only) | SSH | Reserved ports (22, 7681, 8443, 8888) cannot be used as the app port. ## Port Discovery A background scanner checks for non-reserved TCP listeners every 10 seconds. Detected ports are available at `/_ports` and in the connect panel with auto-inferred labels (Vite, Next.js, Django, etc.). The primary app port is served at the root URL (`/`). Additional detected ports use prefix routing (`/_port//`). ## IDE Backends ### code-server Starts on `127.0.0.1:8443` with code-server auth disabled. The gateway handles browser authentication. ### Browser Terminal ttyd starts on port 7681 with tmux for session persistence. See [Terminal & tmux](/guides/sandbox/terminal-tmux). ### SSH Server `sshd` starts for desktop IDE connectivity. Interactive SSH sessions auto-attach to a persistent tmux session. The `cpln sandbox connect` command automates SSH access by injecting your public key, setting up port-forwarding, and launching your desktop IDE. ## Persistent Workspace `/root` is mounted from a persistent [volumeset](/reference/volumeset). Everything under `/root` survives container restarts and suspend/resume: * `~/workspace/` — repositories and working files * `~/.bashrc`, `~/.gitconfig` — shell and tool config * `~/.config/` — code-server settings, extensions * `~/.npm-global/` — globally installed npm packages * `~/.local/` — pip user installs Deleting the sandbox deletes the volumeset and everything under `/root`. ## Troubleshooting No process is listening on `APP_PORT`. Start your app from the IDE terminal. The gateway shows a waiting page until a process binds to the port. Check the service logs inside the container: ```bash theme={null} theme={null} cat /tmp/code-server.log cat /tmp/ttyd.log cat /tmp/ide-gateway.log ``` The service is detected but the gateway can't reach it. Verify the service binds to `0.0.0.0` or `127.0.0.1`, not a specific interface. # Getting Started with Sandbox Source: https://docs.controlplane.com/guides/sandbox/getting-started Create a cloud development environment with a browser IDE, terminal, and persistent storage in under five minutes. ## Prerequisites | Requirement | Purpose | | :------------------------- | :--------------------------------------------------------------- | | A Control Plane org | With at least one GVC | | Access to the CPLN Console | The Sandbox Manager runs as an embedded app in `console.cpln.io` | | `cpln` CLI (optional) | Required for `cpln sandbox connect` (desktop IDE and SSH access) | VS Code Desktop and JetBrains Gateway are optional. The browser IDE (code-server) and terminal (ttyd) require zero local installs. ## Open the Sandbox Manager The Sandbox Manager is embedded in the CPLN Console. Navigate to the **Sandbox** section for your org. ## Create a Toolbox Image (Optional) Several sandbox templates are available to begin working immediately. These images include core runtimes and libraries for common development workflows. To customize a template or build from scratch: Click **Images** in the sidebar. Click **New Image**. * **From Template** — select runtimes, packages, and tools interactively * **BYOI** — provide an existing OCI image reference Click **Build** and wait for the build to complete. See [Building Toolbox Images](/guides/sandbox/toolbox-images) for details. ## Create a Sandbox Click **Instances** in the sidebar, then click **New Instance**. * **Name** (optional) — unique environment name * **Image** — select a sandbox image * **Size** (Medium pre-selected) — CPU/memory profile * **App Port** — port your app will listen on (default: 8080) * **Identity** and **Secrets** — optional access to private infrastructure Click **Create**. The sandbox provisions a persistent volume and workload, then becomes ready in \~60–90 seconds. Create a single-location [GVC](/reference/gvc) for your sandboxes. This reduces costs and provides the optimal developer experience. ## Connect Your IDE Once the instance shows **Ready**, use the connect panel or the CLI. ### From the Web UI The connect panel shows direct links and the IDE password: | Feature | Access | | :------------------------ | :----------------------------------------------- | | Browser IDE (code-server) | `https:///_ide/` (password-protected) | | Browser terminal (ttyd) | `https:///_term/` (password-protected) | | Public app URL | `https:///` (shareable) | ### From the CLI The `cpln sandbox connect` command sets up SSH tunnels for desktop IDE access. It automatically finds your sandbox by searching your accessible orgs and GVCs: ```bash theme={null} theme={null} # Open in VS Code (default) cpln sandbox connect # Open in Cursor cpln sandbox connect --ide cursor # Direct SSH session cpln sandbox connect --ide ssh # Open browser IDE cpln sandbox connect --ide browser ``` Specify org and GVC explicitly for faster lookup: ```bash theme={null} theme={null} cpln sandbox connect --org acme --gvc dev ``` ## Start Coding Clone your repo and install dependencies from the IDE terminal. For private repos, authenticate git inside the environment: ```bash theme={null} theme={null} gh auth login # GitHub glab auth login # GitLab (if installed) ``` Credentials stored under `/root` survive suspend/resume because `/root` is backed by a persistent [volumeset](/reference/volumeset). They are removed when the environment is deleted. ## Suspend and Resume Suspend to save costs when you're done for the day. Your full state is preserved. | Action | Effect | Cost | | :---------- | :------------------------------------------------- | :---------------- | | **Suspend** | Workload scaled to zero, volumeset retained | Storage only | | **Resume** | Workload scales back up, mounts existing volumeset | Compute + storage | | **Delete** | Workload and volumeset removed | None | Use the **Suspend** and **Resume** buttons in the Instances list or connect panel. Deleting an environment removes the workload and volumeset permanently, including cloned repos, credentials, IDE settings, and uncommitted changes. Use suspend to preserve state. # Multi-Repo Workspaces Source: https://docs.controlplane.com/guides/sandbox/multi-repo Work with multiple repositories inside a single sandbox environment. ## Overview Sandboxes do not clone repositories at startup. Multi-repo workflows are handled inside the running sandbox using the browser IDE or terminal. ## Recommended Flow ```bash theme={null} theme={null} cd /root/workspace gh auth login git clone https://github.com/acme/frontend git clone https://github.com/acme/api git clone https://github.com/acme/shared-lib ``` All repos live under `/root/workspace`, which is stored on a persistent [volumeset](/reference/volumeset). The workspace survives suspend/resume and is removed when the environment is deleted. ## Private Repos Use provider-native tooling inside the environment: * **GitHub:** `gh auth login` * **GitLab:** `glab auth login` (if installed in the toolbox) * **SSH:** add keys under `/root/.ssh` Credentials stored under `/root` persist with the environment. ## Running Multiple Services Start services from separate IDE terminals. The configured app port is served at the workload root URL. Additional local ports can be reached through the `/_port//` gateway route. Use `sandbox ports` to see all detected ports with their external URLs. Bind dev servers to `0.0.0.0` so the gateway can reach them. # Sandbox Helper CLI Source: https://docs.controlplane.com/guides/sandbox/sandbox-helper Reference for the sandbox command available inside every sandbox — check status, discover ports, and diagnose runtime issues. ## Overview Every sandbox includes a `sandbox` command for inspecting runtime state, discovering ports, and diagnosing issues. No credentials required. ## Commands | Command | Description | | :------------------- | :--------------------------------------------- | | `sandbox status` | Show runtime service status and resource usage | | `sandbox ports` | Show detected local ports and external URLs | | `sandbox url ` | Print the external URL for a specific port | | `sandbox app` | Show primary app port and common run hints | | `sandbox doctor` | Check installed runtimes and sandbox basics | ## sandbox status ```bash theme={null} theme={null} sandbox status ``` ```text Output theme={null} theme={null} Sandbox: ready - Sandbox ready Primary app port: 8080 Root URL: https://dev-alice-x7k2m.cpln.app/ gateway:8888: ready - Gateway listening codeServer:8443: ready - code-server listening terminal:7681: ready - Terminal listening app:8080: waiting - No app listening on APP_PORT memory: 412MiB / 16GiB, peak 1.2GiB ``` The `app` service shows `waiting` until you start a process on `APP_PORT`. This is normal. ## sandbox ports ```bash theme={null} theme={null} sandbox ports ``` ```text Output theme={null} theme={null} Sandbox ports 8080 Primary app root https://dev-alice-x7k2m.cpln.app/ 3000 Next.js prefix https://dev-alice-x7k2m.cpln.app/_port/3000/ 5173 Vite prefix https://dev-alice-x7k2m.cpln.app/_port/5173/ Tip: bind dev servers to 0.0.0.0 so the gateway can reach them. ``` A background scanner detects non-reserved TCP listeners every 10 seconds. Ports are auto-labeled based on process name and common conventions (Vite, Next.js, Django, Flask, Rails, etc.). Secondary ports use prefix routing (`/_port/3000/`). Most dev servers work without changes. Bind to `0.0.0.0` to ensure the gateway can reach them. ## sandbox app ```bash theme={null} theme={null} sandbox app ``` ```text Output theme={null} theme={null} Primary app port: 8080 Root URL: https://dev-alice-x7k2m.cpln.app/ Common run examples: vite --host 0.0.0.0 --port 8080 next dev -H 0.0.0.0 -p 8080 python manage.py runserver 0.0.0.0:8080 rails server -b 0.0.0.0 -p 8080 ``` ## sandbox doctor ```bash theme={null} theme={null} sandbox doctor ``` ```text Output theme={null} theme={null} Sandbox doctor Runtime checks git git version 2.43.0 node v20.18.0 npm 10.8.2 python Python 3.12.7 go go version go1.23.0 linux/amd64 code-server 4.96.4 gh gh version 2.63.0 Sandbox status ready app port 8080 app url https://dev-alice-x7k2m.cpln.app/ workspace /root/workspace ``` Only installed tools are shown. Missing tools are silently omitted. ## Troubleshooting The helper is installed during image build. Rebuild the toolbox image to get the latest version. As a workaround, check status files directly: `cat /tmp/devbox-status.json | jq .status`. The port scanner runs every 10 seconds. Wait a few seconds after starting a service, then retry. Verify your service is listening: `ss -tlnp | grep LISTEN`. Verify the service binds to `0.0.0.0` or `127.0.0.1`, not a specific interface. Check `sandbox status` for service states. # Terminal & tmux Source: https://docs.controlplane.com/guides/sandbox/terminal-tmux How tmux terminal sessions work inside sandboxes, including persistence, named sessions, and key bindings. ## How It Works Every sandbox runs [tmux](https://github.com/tmux/tmux) as an invisible session manager. Terminal sessions persist across disconnects — running processes, scroll history, and working directory survive browser refreshes, SSH reconnects, and IDE restarts. Three named sessions are created automatically: | Session | Created By | Attaches When | | :------- | :---------------------- | :------------------------------------------------------ | | `vscode` | code-server settings | Opening a terminal in the browser IDE | | `term` | ttyd (browser terminal) | Opening `/_term/` in the browser | | `ssh` | `.bashrc` auto-attach | Connecting via SSH (VS Code Remote, Cursor, direct SSH) | Each session is independent. Work in the browser IDE terminal does not appear in the SSH session, and vice versa. ## Session Persistence tmux sessions persist as long as the container is running. | Event | Sessions Preserved | | :------------------------------ | :----------------- | | Browser tab closed and reopened | Yes | | SSH disconnect and reconnect | Yes | | IDE restart | Yes | | Workload container restart | No | | Suspend and resume | No | Files, git repos, and installed tools persist across suspend/resume because `/root` is backed by a [volumeset](/reference/volumeset). Only tmux sessions (running processes, shell history in memory) are lost. ## Working with tmux The sandbox tmux configuration hides the status bar and unbinds the prefix key, so it stays invisible. You can still use tmux commands directly: ```bash theme={null} theme={null} # List all sessions tmux ls # Attach to a specific session tmux attach -t vscode # Create a new named session tmux new-session -s my-session # Kill a session tmux kill-session -t my-session ``` To enable standard tmux key bindings, add a prefix to `/root/.tmux.conf`: ```bash theme={null} theme={null} echo "set -g prefix C-a" >> /root/.tmux.conf tmux source-file /root/.tmux.conf ``` This persists across container restarts. ## Troubleshooting This is expected. tmux preserves the scrollback buffer. Scroll down or press Enter to get to the current prompt. tmux keeps processes alive when you disconnect. To stop a process, reattach and terminate it, or kill the session: `tmux kill-session -t term`. Check browser permissions for the sandbox URL. The tmux config enables OSC 52 clipboard support. The sandbox tmux config sets `escape-time` to 0. Verify: `tmux show-option -g escape-time`. Reset if needed: `tmux set -g escape-time 0`. # Building Toolbox Images Source: https://docs.controlplane.com/guides/sandbox/toolbox-images Build, customize, and manage sandbox images using templates, runtimes, packages, and AI coding tools. ## What is a Toolbox A toolbox is a container image that provides the development stack: runtimes, package managers, IDEs, and utilities. Application code is **not** baked into the image — developers clone and manage repos inside the running sandbox. Every toolbox image includes the dev access layer: | Component | Purpose | | :-------------- | :---------------------------------------- | | openssh-server | SSH for VS Code Remote, JetBrains Gateway | | code-server | VS Code in browser (port 8443) | | ttyd | Browser terminal (port 7681) | | Claude Code CLI | AI coding assistant | | gh CLI | GitHub operations | ## Build Modes Build toolbox images from the **Images** page in the web UI. ### Scratch Build an image interactively by selecting components: | Step | Description | | :------------------ | :--------------------------------------------------------------- | | Template (optional) | Start from a pre-built template for faster builds | | Runtimes | Choose from 11 languages with version selection | | System Packages | Search/add from 7 categories or type custom apt package names | | Install Commands | Shell commands run during build | | AI Coding Tools | Claude Code, Codex CLI, Gemini CLI, Plandex, OpenCode, or custom | | VS Code Extensions | Extensions bundled into the image | ### BYOI (Bring Your Own Image) Provide an existing OCI image reference. The dev access layer (SSH, code-server, ttyd, gateway) is layered on top automatically. ## Templates Templates are pre-built toolbox images available as starting points. Selecting a template skips the base tool installation and only adds your customizations, making builds significantly faster. | Template | Description | | :---------------- | :-------------------------------- | | Blank | Ubuntu 24.04 + SSH + Claude Code | | Python API | Flask / FastAPI / Django | | Node.js Fullstack | React / Next.js / Express | | Go Microservice | Go APIs / gRPC services | | Java API | Spring Boot / Quarkus / Micronaut | | Rust | Systems / CLI tools / WebAssembly | | Data Science | pandas / ML workflows | | AI / Agents | Claude Code / LangChain / RAG | ## Supported Runtimes | Runtime | Default Version | Install Method | | :------ | :-------------- | :-------------------- | | Python | 3.12 | Base image or apt | | Node.js | 20 | NodeSource apt repo | | Go | 1.23 | Binary tarball | | Rust | stable | rustup | | Java | 21 | Temurin JDK | | Ruby | system | apt | | .NET | 8.0 | apt | | PHP | system | apt + Composer | | Elixir | system | apt (Erlang + Elixir) | | Kotlin | latest | JetBrains release zip | | Swift | 5.9 | Swift.org tarball | ## Image Forking When a developer installs packages inside a running sandbox, those changes can be detected and captured into a new image via the **Fork** feature. Fork detection compares the current state against snapshots taken at boot: | Package Manager | What's Detected | | :----------------- | :--------------------------- | | apt | New system packages | | pip | New Python packages (global) | | npm | New Node packages (global) | | VS Code extensions | Newly installed extensions | Click **Fork** on a running sandbox to see detected changes and build a new image that includes them. ## Build Progress When you start a build, a modal shows live progress: 1. **Creating** — preparing the build 2. **Building** — executing the Dockerfile (installing runtimes, packages, tools) 3. **Pushing** — uploading the image to the registry 4. **Done** — image available for use Build logs stream in real time. If a build fails, the log shows which step failed and why. ## Managing Images The **Images** page lists all toolbox images in your [org](/reference/org): | Action | Description | | :---------- | :-------------------------------------------- | | Build | Create a new image from scratch or a template | | Clone | Copy an existing image config to a new image | | Set Default | Set as the default image for new sandboxes | | Delete | Remove the image from the registry | # Sandbox Web UI Source: https://docs.controlplane.com/guides/sandbox/ui-reference Reference for the Sandbox Manager web interface — dashboard, instances, images, and global settings. ## Sidebar Navigation | Element | Description | | :-------------- | :---------------------------------------------------------------------------------------------- | | Overview | Organization health dashboard | | Instances | Sandbox instance management (create, suspend, resume, delete) | | Images | Toolbox image builder and manager | | Global Settings | Org-level configuration (Sandbox Profiles, Secrets & API Keys, Environment Variables, Defaults) | ## Overview Dashboard At-a-glance view of organization health and sandbox status. ### Health Cards | Card | Description | | :--------- | :--------------------------------- | | Sandboxes | Count of running sandboxes | | Toolboxes | Count of configured toolbox images | | Volumesets | Count of persistent volumes | | Secrets | Count of org secrets | ### Sandbox Table A full table of all sandboxes in the current GVC. Each row shows name, image, endpoint, readiness, size, and last modified date. ### Sandbox Actions | Action | Description | | :------------ | :----------------------------------------------- | | Connect | Open the connect panel | | Open IDE | Launch the browser IDE | | Open Terminal | Open a web terminal session | | Open App | Navigate to the application endpoint | | Suspend | Scale to zero while retaining the volumeset | | Resume | Scale back to one and remount the volumeset | | Delete | Delete the sandbox and its volumeset permanently | ### Orphaned Volumesets Volumesets no longer attached to a sandbox are surfaced with age badges. A one-click restore action spins up a new sandbox from an orphaned volumeset. ## Instances ### Create Sandbox | Field | Required | Default | Description | | :-------------------- | :------- | :--------------- | :----------------------------------------- | | Name | No | `dev-{username}` | Sandbox name | | Image | Yes | org default | Toolbox image | | Size Profile | Yes | Medium | CPU/memory preset | | App Port | No | `8080` | Port your app listens on | | Identity | No | org default | Infrastructure identity | | Secrets | No | — | Secret-to-env-var mappings | | Environment Variables | No | — | Key-value pairs exposed inside the sandbox | ### Connect Panel After selecting a sandbox, the connect panel displays IDE connection links, the IDE password, runtime diagnostics, and detected ports. ## Images ### Toolbox List | Column | Description | | :--------- | :------------------------------ | | Image Name | Image reference | | Runtimes | Configured language runtimes | | Mode | Build mode used (Scratch, BYOI) | | Created | Date the image was built | ### Build Modes * **Scratch** — select runtimes, packages, AI tools, and extensions interactively * **BYOI** — provide an existing image. Dev tools are layered on top. ### Build Modal An animated modal tracks build progress through four phases: Creating, Building, Pushing, Done. ### Image Actions | Action | Description | | :---------- | :-------------------------------------- | | Clone | Create a copy with a new name | | Set Default | Set as the org default toolbox | | Delete | Remove the toolbox and delete the image | ## Settings Organization-level configuration, accessible via the **Global Settings** submenu. ### Sandbox Profiles CPU and memory presets that appear in the sandbox create form. | Field | Type | Description | | :---------- | :----- | :-------------------------------------- | | Name | string | Profile display name | | CPU | string | CPU allocation (e.g., `4`) | | Memory | string | Memory allocation (e.g., `16Gi`) | | Description | string | Human-readable description | | Default | radio | Pre-selected profile in the create form | ### Secrets & API Keys Global secret mappings that bind a Control Plane [secret](/reference/secret) to an environment variable name. | Column | Description | | :----------- | :-------------------------------------------- | | CPLN Secret | Name of the Control Plane secret | | Env Var Name | Environment variable exposed inside sandboxes | New secrets can be created inline from the settings page. ### Environment Variables Default environment variables injected into every new sandbox. | Field | Type | Description | | :---------- | :----- | :--------------------------------- | | Name | string | Variable name | | Value | string | Variable value | | Locked | toggle | Prevent developers from overriding | | Description | string | Human-readable description | ### Defaults | Setting | Description | | :--------------- | :-------------------------------------------------------- | | Default Toolbox | Toolbox image used when none is specified | | Default Identity | [Identity](/reference/identity) attached to new sandboxes | | Identity Locked | Prevent developers from overriding the identity | | Default IDE | IDE used when none is specified | | IDE Locked | Prevent developers from overriding the IDE | Locked settings are enforced only in the UI. Users with direct CPLN API access can still override these values. # Service-to-Service Calls Source: https://docs.controlplane.com/guides/service-to-service Configure internal firewall rules for workload-to-workload communication. Covers service endpoint syntax, same-GVC and cross-GVC calls, and latency benefits. ## Overview The [internal firewall](/reference/workload/firewall#internal) rules for a [workload](/reference/workload) can be configured to allow inbound access from other [workloads](/reference/workload) in the same [org](/reference/org). Allowing this type of access decreases the latency of your [workloads](/reference/workload) by making the request in the most direct path possible and increases security by validating the client certificate of the remote workload. Calling the endpoint of a service follows a specific URL [syntax](#service-endpoint-syntax) that uses the [workload](/reference/workload) name and [GVC](/reference/gvc) name. ## Prerequisites * Have [permissions](/reference/policy#permissions) to edit a [workload](/reference/workload). * Optional: Install the [CLI](/cli-reference/installation). ## Internal Firewall The [internal firewall](/reference/workload/firewall#internal) settings are part of the firewall options of a [workload](/reference/workload) and can be set to one of the following types: * `None` * No access is allowed to this [workload](/reference/workload) from other [workloads](/reference/workload). * `Same GVC` * [Workloads](/reference/workload) running in the same [GVC](/reference/gvc) are allowed to access this [workload](/reference/workload). * `Same Org` * [Workloads](/reference/workload) running in the same org are allowed to access this [workload](/reference/workload). * `Workload List` * Only specific [workloads](/reference/workload) listed in `inboundAllowWorkload`, including workloads from other GVCs, are allowed access to this workload. * With this rule selected, the [workload](/reference/workload) can be set to allow replicas of itself to access itself by enabling the `Allow Access To Itself` button. Calling a workload using `localhost` doesn't require this setting and will force the request to the current workload. By using this setting and sending the request using the [service endpoint syntax](#service-endpoint-syntax), the request will be routed to all replicas instead of only the local one. * If using KEDA, enable the `Allow KEDA Access` button. See [KEDA autoscaling](/reference/workload/autoscaling#keda) for details. * `Same GVC and Workload List` * A combination of the `Same GVC` and `Workload List`. Only the ports listed in the workload containers array will be made accessible to other workloads. Internal workload to workload traffic that spans GVCs may also span locations and will incur egress charges. ## Service Endpoint Syntax Once inbound access has been allowed on a [workload](/reference/workload), the following syntax is used when calling the [workload](/reference/workload) from another [workload](/reference/workload): ```text Syntax theme={null} http://WORKLOAD_NAME.GVC_NAME.cpln.local[:PORT] ``` * If `PORT` is omitted, the first port listed in the target workload's container array is used as the default port for `http` requests. * Internal communication works with `gRPC`, `HTTP`, `HTTP2`, and `TCP`. * Requests are initiated using plaintext communication. A sidecar service initiates TLS on behalf of the workload. * Control Plane enforces mutual TLS (mTLS) for all internal workload-to-workload communication. * Headers may optionally be used on the receiving workload to verify that the communication was encrypted. * Only the ports listed in the workload containers array will be made accessible to other workloads. The syntax above load-balances across all replicas. To reach an individual replica directly, or to find the hostnames assigned to a given replica, see [Finding Replica Hostnames](/reference/workload/general#finding-replica-hostnames) in the Workload reference. ## Configure internal firewall using the Console To configure the internal firewall rules using the Console: 1. Click `Workloads` in the left menu, then select the [workload](/reference/workload) to configure. 2. Click `Firewall`, then click the `Internal` tab. 3. Click the `Inbound Allow Type` dropdown and select the inbound access for this [workload](/reference/workload). 4. Click `Update`. After updating, a new deployment of the [workload](/reference/workload) will be queued, and once deployed, the rule will be enforced. ## Configure internal firewall using the CLI To modify the internal firewall rule with the CLI, use the [workload edit](/cli-reference/commands/workload#workload-edit) command. 1. Run the following command to open the workload metadata in your default text editor: ```bash theme={null} cpln workload edit WORKLOAD_NAME --org ORG_NAME --gvc GVC_NAME ``` 2. Scroll down to the `internal` section under `firewallConfig`. 3. Update the `inboundAllowType` property to one of the following (case sensitive) values: * `none` * `same-gvc` * `same-org` * `workload-list` To allow specific workloads, add their links to `inboundAllowWorkload`. Use this with `workload-list`, or with `same-gvc` when you want to allow all workloads in the same GVC plus specific workloads from other GVCs. ```text Format theme={null} //gvc/GVC_NAME/workload/WORKLOAD_NAME ``` To allow a workload to call itself, use that workload's own name for `WORKLOAD_NAME`. 4. Save the file. If the save is successful, the new rules will be applied within a minute and a deployment of the [workload](/reference/workload) will be queued. ## Quick Start A quick start demonstrating service-to-service calls is available in the [Service-to-Service quickstart](/quickstart/quick-start-4-service-to-service). # Configure an Agent Source: https://docs.controlplane.com/guides/setup-agent Install and configure a Control Plane agent on AWS, Azure, GCP, Docker, or Kubernetes for secure connectivity to private network resources. Click the desired cloud provider and follow the installation and configuration instructions: * [Amazon Web Services (AWS)](#aws) * [Microsoft Azure](#azure) * [Google Cloud Platform (GCP)](#gcp) * [Running locally in Docker](#running-locally-in-docker) * [Kubernetes (k8s) Cluster](#kubernetes-k8s-cluster) ## AWS ### Overview Follow the steps below to install and configure an [agent](/reference/agent) within your Amazon Web Services (AWS) environment. ### Prerequisites * Review the [Agent](/reference/agent) reference page. * [Amazon Web Services](https://aws.amazon.com/) (AWS) account. ### Step One - Create an Agent Follow the [Create an Agent](/guides/agent) guide to define an agent and generate the bootstrap config file that will be used in [step two](#step-two-create-a-launch-template-in-aws). ### Step Two - Create a Launch Template in AWS 1. If you already have the `Userdata Script`, you can skip this step. In the Control Plane UI, click `Agents` in the left menu, select your agent, then choose `Download Scripts` from the `Actions` menu. Paste your bootstrap token and copy the YAML from the `Userdata Script` tab. 2. Log in to the [AWS Console](https://console.aws.amazon.com). Once logged in, select the AWS Region at the navigation bar in the top-right corner. Choose the same region where the services it will access are located. 3. In the AWS Console, navigate to `EC2`. In the left sidebar, under Instances, select `Launch Templates` and then click `Create launch template`. 4. Enter a unique name for the launch template. (e.g., `cpln-agent-launch-template`). 5. Under the `Auto Scaling guidance` section, check the box to use this template with EC2 Auto Scaling. 6. Under the `Application and OS Images` section, select `Quick Start` and choose `Ubuntu` (Ubuntu Server 24.04 LTS recommended). 7. For `Instance type`, select `t3.small` or `t3.medium` for testing purposes. In production environments, refer to the [Agent Sizing Guidance](/reference/agent#agent-sizing-guidance) to select the appropriate instance type for running an agent. An instance with at least 2 vCPUs and 4 GiB of memory is recommended for optimal performance. 8. `Optional:` Under the `Key pair (login)` section, select or create a new key pair to enable SSH access to the agent. A key pair is necessary only for accessing the agent during troubleshooting. 9. Under the `Networking` section, select `Don't include subnet in the launch template`, subnets are configured by EC2 Auto Scaling. Attach a security group and make sure it belongs to the VPC you plan on using when creating the Auto Scaling Group in Step 3 below. * `Note:` The VPC must have an `Internet Gateway` attached. To verify, navigate to `VPC`, click `Internet Gateways`, find the gateway associated with your VPC, and confirm the state is `Attached`. 10. Expand the `Advanced details` section. Scroll to the bottom and paste the `Userdata Script` copied in Step 1 into the `User data` textbox. 11. Click `Create launch template`. ### Step Three - Create an Auto Scaling Group in AWS 1. In the AWS Console, navigate to `EC2`. In the left sidebar, scroll down to Auto Scaling, select `Auto Scaling Groups`, then click `Create Auto Scaling group` in the right corner. 2. Enter a name for the Auto Scaling Group. (e.g., `cpln-agent`). 3. For the Launch template, select the name you created in Step Two (e.g., `cpln-agent-launch-template`), and click `Next`. 4. Under the `Network` section, select the VPC used in your launch template from Step Two. For Availability Zones and subnets, select at least `one` subnet and set Availability Zone distribution to `Balanced best effort`. * `Note:` Confirm your subnet has Auto-assign public IPv4 address by navigating back to `VPC` and clicking `Subnets` in the AWS console. If it is not enabled, you can also expand `Advanced network configuration` and click `Add network interface`, then set `Auto-assign public IP` to `Enable`. 5. `Optional:` Leave all settings as default and click `Next`. 6. For testing, set Desired, Min, and Max capacity all to 1. For production, set Desired capacity to 2, Min capacity to 2, and Max capacity to at least 4. This way you can scale up to multiple agents as needed. Leave all other settings as default, then click `Skip to review`. * `Note:` You can also scale down to 0 or scale up to multiple agents for redundancy and reliability. 7. Review your settings, then scroll down and click `Create Auto Scaling group` in the bottom-right corner. The agent virtual machine will begin the deployment process. After a few moments, the agent will be running, connecting to the Control Plane servers, and ready to process requests. To verify, go to the Control Plane UI, click `Agents` in the left menu, select your agent, and you will see a green heartbeat pinged recently. It may take up to 2-3 minutes to appear after the agent starts. ### Next Steps Now that you have an agent configured and running, it can be used within an [identity](/reference/identity) to allow your [workload](/reference/workload) to connect to your internal AWS resources. ## Azure ### Overview Follow the steps below to install and configure an [agent](/reference/agent) within your Microsoft Azure environment. ### Prerequisites * Review the [Agent](/reference/agent) reference page. * [Microsoft Azure](https://azure.com) account. ### Step One - Create an Agent Follow the [Create an Agent](/guides/agent) guide to define an agent and generate the bootstrap config file that will be used in [step two](#step-two-create-a-virtual-machine-scale-set-in-azure). ### Step Two - Create a Virtual Machine Scale Set in Azure 1. If you already have the `Userdata Script`, you can skip this step. In the Control Plane UI, click `Agents` in the left menu, select your agent, then choose `Download Scripts` from the `Actions` menu. Paste or import the bootstrap config, click `Next`, and copy or download the YAML from the `Userdata Script` tab. Click `Done`. 2. Log in to the [Azure Console](https://portal.azure.com). 3. In the Azure console, navigate to `Virtual machine scale sets` and click `Create`. 4. Select your `Subscription` and `Resource Group`. If needed, create a new resource group before proceeding. 5. Under the `Scale set details` section, enter a unique name for the Virtual Machine Scale Set. (e.g., `cpln-agent`). 6. Select the Region where your Azure resources are located and set `Availability Zones` to Zones 1, 2, and 3. 7. Under the `Scaling mode` section, select `Autoscaling`, then set the Image to a `Ubuntu Server 24.04 LTS` (recommended). 8. For `Size`, select `Standard D2s v3` for testing purposes. An instance with at least 2 vCPUs and 4 GiB of memory is recommended for optimal performance. 9. Leave the Username as `azureuser`. For SSH Key Type, select an existing key or generate a new one. 10. Click `Next`, skip the Spot section, and click `Next: Disks`. 11. For `OS Disk Type`, select Premium SSD then click `Next: Networking`. 12. Select your `Virtual Network`, `Subnet`, and `Network interface`, or create new ones if they do not exist. 13. Under `Load balancing`, select `None`, then click `Next: Management`. 14. Leave the defaults and click `Next: Health`. 15. Leave the defaults and click `Next: Advanced`. 16. Under the `Custom data and cloud init` section, paste the `Userdata Script` from Step 1 into the `Custom Data` field, then click `Create`. If you created a new key pair, click `Download private key and create resource`. The agent virtual machine will begin the deployment process. After a few moments, the agent will be running, connecting to the Control Plane servers, and ready to process requests. To verify, go to the Control Plane UI, click `Agents` in the left menu, select your agent, and you will see a green heartbeat pinged recently. It may take up to 2-3 minutes to appear after the agent starts. ### Next Steps Now that you have an agent configured and running, it can be used within an [identity](/reference/identity) to allow your [workload](/reference/workload) to connect to your internal Azure resources. ## GCP ### Overview Follow the steps below to install and configure an [agent](/reference/agent) within your Google Cloud Platform (GCP) environment. ### Prerequisites * Review the [Agent](/reference/agent) reference page. * [Google Cloud Platform (GCP)](https://cloud.google.com/) account. ### Step One - Create an Agent Follow the [Create an Agent](/guides/agent) guide to define an agent and generate the bootstrap config JSON file that will be used in [step two](#step-two-create-an-instance-template-in-gcp). ### Step Two - Create an Instance Template in GCP 1. If you already have the `Startup script`, you can skip this step. In the Control Plane Console UI, click `Agents` in the left menu, select your agent, then choose `Download Scripts` from the `Actions` menu. Paste your bootstrap token and copy the script shown in the `Startup script` tab. The `Startup script` is used to automatically install and configure the Control Plane agent on your VM upon startup. Some images in GCP also support cloud-init so you can use that instead. Consult the docs for image details. For example, Ubuntu supports cloud-init at the time of this writing while Debian does not. 2. Log in to the [GCP Console](https://console.cloud.google.com). To the left of the search bar, select your target project. 3. In the GCP Console, navigate to `Compute Engine`. In the left sidebar, under Virtual Machines, select `Instance templates`, then click `Create instance template`. 4. Enter a name for the Instance template. (e.g., `cpln-agent-instance-template`). 5. Under the `Location` section, select `Regional`, and choose your preferred region. * `Note:` The region needs to match where your internal resources are located, as the agent must be in the same VPC and region to connect to them. 6. For the `Machine configuration`, select `C4` for testing purposes. In production environments, refer to the [Agent Sizing Guidance](/reference/agent#agent-sizing-guidance) to select the appropriate instance type for running an agent. 7. Under the `Machine Type` section, select `c4-standard-2` (2 vCPUs, 7 GB memory) or `c4-standard-4` (4 vCPUs, 15 GB memory). An instance with at least 2 vCPUs and 4 GiB of memory is recommended for optimal performance. 8. Under the `Boot disk` section, set the operating system to `Ubuntu` and select `Ubuntu 26.04 LTS Minimal`, then click `Select`. 9. Scroll to the bottom of the page and expand `Advanced Options`, then expand `Management`. Paste the `Startup script` from Step 1 into the `Automation` field and click `Create`. ### Step Three - Create an Instance Group in GCP 1. In the GCP Console, navigate to `Compute Engine`. In the left sidebar, scroll down to Instance groups, select `Instance groups`, then click `Create instance group`. 2. Choose `New Managed Instance Group (Stateless)` and enter a name for the instance group. (e.g., `cpln-agent`). 3. For the Instance template, select the name you created in Step Two (e.g., `cpln-agent-instance-template`). 4. Under the `Location` section, select `Multiple zones` and choose your preferred regions. 5. For the Target distribution shape, select `Balanced` to support active-active configuration and evenly distribute instances across all selected zones. 6. Under the `Autoscaling` section, click `Configure Autoscaling`. Set the `Autoscaling Mode` to `On`. Update the Minimum and Maximum number of instances as needed, and add any additional autoscaling signals if required. * For testing, set Minimum and Maximum capacity to 1. * For production, set Minimum to 2 and Maximum to at least 4. * `Note:` You can also scale down to 0 or scale up to multiple agents for redundancy and reliability. 7. Click `Create` in the bottom-left corner. The agent virtual machine will begin the deployment process. After a few moments, the agent will be running, connecting to the Control Plane servers, and ready to process requests. To verify, go to the Control Plane UI, click `Agents` in the left menu, select your agent, and you will see a green heartbeat pinged recently. It may take up to 2-3 minutes to appear after the agent starts. ### Next Steps Now that you have an agent configured and running, it can be used within an [identity](/reference/identity) to allow your [workload](/reference/workload) to connect to your internal GCP resources. ## Running locally in Docker ### Overview Follow the steps below to install and configure an [agent](/reference/agent) within your private network. ### Prerequisites * Review the [Agent](/reference/agent) reference page. * Install the [CLI](/cli-reference/installation). * Install [Docker](https://docs.docker.com/engine/install/). ### Step One - Create an Agent Follow the [Create an Agent](/guides/agent) guide to define an agent and generate the bootstrap config file that will be used in [step two](#step-two-launch-agent-locally). ### Step Two - Launch agent locally 1. Open a new shell and execute the following command. Use the bootstrap file that was created in [step one](#step-one-create-an-agent-4). ```bash theme={null} cpln agent up --bootstrap-file=path/to/bootstrapConfig.json ``` If you are using Windows, follow these instructions: * Configure Docker to **not** use the WSL 2 based engine. * Run the `cpln` command above using a Windows command prompt and not WSL. 2. The agent will now be running, connecting to the Control Plane servers, and ready to process requests. To verify, go to the Control Plane UI, click `Agents` in the left menu, select your agent, and you will see a green heartbeat pinged recently. It may take up to a minute to appear after the agent starts. ### Next Steps Now that you have an agent configured and running, it can be used within an [identity](/reference/identity) to allow your [workload](/reference/workload) to connect to your local resources. When running an agent locally, it is running within a local Docker container. When configuring an [identity network resource](/reference/identity#network-resources-cloud-wormhole), you must use the IP of the network adapter that Docker installed on the local machine. ## Kubernetes (k8s) Cluster ### Overview Follow the steps below to install and configure an [agent](/reference/agent) within your Kubernetes cluster. ### Prerequisites * Review the [Agent](/reference/agent) reference page. * Install the [CLI](/cli-reference/installation). ### Step One - Create an Agent Follow the [Create an Agent](/guides/agent) guide to define an agent and generate the bootstrap config file that will be used in [step two](#step-two-launch-agent-within-a-k8s-cluster). ### Step Two - Launch agent within a K8s cluster 1. Prepare your k8s manifests by going to your agent's page in Control Plane. Select `Download Scripts` from the `Actions` menu. Paste the bootstrap token you saved earlier and copy the YAML that shows in the `K8S Manifests` tab. There you can optionally configure a namespace and number of replicas. The recommended number of replicas is 2. 2. Assuming you have saved the manifest to a file locally just apply it with `kubectl`: ```bash theme={null} # may need to create the namespace using kubectl create ns agent-namespace kubectl apply -f agent-manifests.yaml ``` 3. Within a few minutes, the agent pods will be ready to handle requests. ### Next Steps Now that you have an agent configured and running, it can be used within an [identity](/reference/identity) to allow your [workload](/reference/workload) to connect to your local resources. # Configure an Agent (Legacy) Source: https://docs.controlplane.com/guides/setup-agent-legacy This page describes `v1` agents which are deprecated starting April 2026. Please refer to the updated [Configure an Agent](/guides/setup-agent) guide. Click on the desired cloud provider and follow the installation and configuration instructions: * [Amazon Web Services (AWS)](#aws) * [Microsoft Azure](#azure) * [Google Cloud Platform (GCP)](#gcp) * [Private Network](#running-locally-in-docker) * [Kubernetes (k8s) Cluster](#kubernetes-k8s-cluster) ## AWS ### Overview Follow the steps below to install and configure an [agent](/reference/agent) within your Amazon Web Services (AWS) environment. ### Prerequisites * Review the [Agent](/reference/agent) reference page. * [Amazon Web Services](https://aws.amazon.com/) (AWS) account. ### Step One - Create an Agent Follow the [Create an Agent](/guides/agent) guide to define an agent and generate the bootstrap config file that will be used in [step two](#step-two-launch-an-agent-in-aws). ### Step Two - Launch an agent in AWS 1. Log in to the [AWS Console](https://console.aws.amazon.com). 2. Click this [link](https://aws.amazon.com/marketplace/pp/prodview-dq5cug2iej46m) to be directed to the `Control Plane Secure Communications Agent` within the AWS Marketplace. * For the `ARM`s version of the agent, [click here](https://aws.amazon.com/marketplace/pp/prodview-fvvtn73sdxxos). 3. Click the `View purchase options` button in the upper right corner. 4. After reading the terms and conditions, click the `Accept Terms`. 5. After the subscription has loaded, click the `Continue to Configuration` button in the upper right corner. 6. Click the `region` pull-down and select the region where your AWS resources reside. 7. Click the `Continue to Launch` button in the upper right corner. 8. Click the `Choose Action` pull-down and select `Launch through EC2`. Click the `Launch` button. 9. The `Launch an instance` wizard will be displayed. 10. Under the `Name and tag` section, enter the agent's name. (e.g., `cpln-agent`). 11. Under the `Instance type` section, select an applicable instance type. Refer to the [Agent Sizing Guidance](/reference/agent#agent-sizing-guidance) page for additional details on which instance type to select. 12. `Optional:` Under the `Key pair(login)` section, select or create a new key pair to enable SSH access to the agent. A key pair is necessary only for accessing the agent during troubleshooting. If you do not have an AWS key-pair created, the console will help you to create one. Since the agent instance will never need to be connected to (except for troubleshooting), you may proceed without a key-pair. 13. Under the `Network setting` section, review the details and verify that the selected VPC is the same as the AWS resource you are trying to access. For the agent to properly connect to the Control Plane servers, it requires outbound Internet access. Verify that the `Auto-assign Public IP` option is set to `Enable`. If your requirements do not allow the instance to have a public IP, please review the section `How do instances without public IP addresses access the Internet` in this [AWS FAQ](https://aws.amazon.com/vpc/faqs/). Either create or select an existing security group. The security groups belonging to the resources that the agent will need to have access to will require to have the security group belonging to the agent added to its list of allowed inbound traffic. Initially, remove the checkbox for the "Allow SSH from" property. SSH access is only necessary for troubleshooting purposes. Control Plane will never need to connect directly to the agent. 14. Under the `Configure storage` section, click the `Advanced` link and expand the volume property. Modify the `Delete on termination` dropdown to `Yes`. This will ensure the associated volume is removed if the agent is terminated, thereby preventing any orphaned volumes. 15. Expand the `Advanced details` section. Scroll to the bottom and paste the contents of the JSON payload (from the bootstrap config file) generated in [step one](#step-one-create-an-agent) within the `User data` textbox. Please review the other properties in this section to check if any default values need to be modified. 16. Click `Launch instance` in the lower right corner. 17. After a brief moment, the instance will launch and be ready to process requests. ### Next Steps Now that you have an agent configured and running, it can be used within an [identity](/reference/identity) to allow your [workload](/reference/workload) to connect to your internal AWS resources. ## Azure ### Overview Follow the steps below to install and configure an [agent](/reference/agent) within your Microsoft Azure environment. ### Prerequisites * Review the [Agent](/reference/agent) reference page * [Microsoft Azure](https://azure.com) account ### Step One - Create an Agent Follow the [Create an Agent](/guides/agent) guide to define an agent and generate the bootstrap config file that will be used in [step two](#step-two-launch-an-agent-in-azure). ### Step Two - Launch an agent in Azure 1. Log in to the [Azure Console](https://portal.azure.com). 2. From the Azure homepage, click the `Marketplace` icon. 3. In the Marketplace search bar, enter: ``` Control Plane Secure Communications Agent ``` 4. Press `Enter`. 5. When clicking the `Create` dropdown, select `gen-1`. 6. Use these recommended settings for the `Create a virtual machine` wizard: * Basic * Subscription: Choose the appropriate subscription. * Resource Group: Choose the appropriate resource group. If necessary, create a new one. * Virtual machine name: Enter `Control-Plane-Agent-01`. If installing multiple agents, increment the number. * Region: Select a region closest to your other Azure resources. * Availability options: Select `No infrastructure redundancy required`. Use a different option for your environment if you are running in production. * Image: Leave as `gen-1`. * Size: An instance with at least 2 vCPUs and 4 GiB of memory is recommended for optimal performance. * Authentication type: Select `SSH public key`. * Username: Leave as `azureuser`. * SSH public key source: Choose the appropriate key. If necessary, create a new one. * Key pair name: Select appropriate key, or if creating a new one, use the default or update the key name. * Public inbound ports: Select `None`. The agent does not need any inbound ports open. * Click `Next: Disks`. * Disks * OS disk type: Select `Premium SSD`. * Encryption type: Select `(Default) Encryption at-rest with a platform-managed key`. * Click `Next: Networking`. * Networking * Virtual network: Choose an appropriate network or use the new network that will be created. * Subnet: Choose appropriate network or use the default. * Public IP: Select `None`. * NIC network security group: Select `Basic`. * Public inbound ports: Select `None`. * Click `Next: Management`. * Management * Enable basic plan for free: Enabled. * Boot diagnostics: Select `Enable with managed storage account`. * Enable OS guest diagnostics: Disabled. * System assigned managed identity: Disabled. * Enable auto-shutdown: Disabled. * Patch orchestration options: Select `Image default`. * Click `Next: Advanced`. * Advanced * Custom data: Paste the JSON text generated from [Step One](#step-one-create-an-agent-2) into the textbox. * Click `Next: Tags`. * Tags * Optional: Enter any necessary tags. * Click `Next: Review + create`. * Review + create * Review all the settings and enter any missing values. * Click `Create`. * If you requested to create a new key pair, a modal will pop-up requesting to download the private key. Click `Download private key and create resource`. The agent virtual machine will begin the deployment process. After a few moments, the agent will be running, connecting to the Control Plane servers, and ready to process requests. ### Next Steps Now that you have an agent configured and running, it can be used within an [identity](/reference/identity) to allow your [workload](/reference/workload) to connect to your internal Azure resources. ## GCP ### Overview Follow the steps below to install and configure an [agent](/reference/agent) within your Google Cloud Platform (GCP) environment. ### Prerequisites * Review the [Agent](/reference/agent) reference page * [Google Cloud Platform (GCP)](https://cloud.google.com/) account. ### Step One - Create an Agent Follow the [Create an Agent](/guides/agent) guide to define an agent and generate the bootstrap config file that will be used in [step two](#step-two-launch-agent-in-gcp). ### Step Two - Launch agent in GCP * Using the Google Cloud SDK 1. Install the latest [SDK](https://cloud.google.com/sdk/docs/install). 2. If necessary, log in using `gcloud init`. 3. Execute the command in the note below. Ensure that the instance will be deployed in the same VPC and region as your GCP resources. Use a unique `INSTANCE_NAME` and the bootstrap file `(AGENT_NAME-bootstrapConfig.json)` that was created in [step one](#step-one-create-an-agent-3). ``` gcloud compute instances create **INSTANCE_NAME** --image controlplane-agent-amd64-20260218-2334239718-16cf8727 --image-project cpln-build --metadata-from-file=user-data=**AGENT_NAME-bootstrapConfig.json** ``` Refer to the [Agent Sizing Guidance](/reference/agent#agent-sizing-guidance) page for additional details on which machine type to select. Add the flag `--machine-type=MACHINE_TYPE` to the command above to select a different type. Otherwise, the default type is `n1-standard-1`. 4. After a few moments, the command will return and show the status of the deployed instance. 5. The agent will now be running, connecting to the Control Plane servers, and ready to process requests. ### Step Three - Configure Firewall By default, the GCP firewall rules open the common SSH, RDP, and ICMP ports to the world and allows all internal ports within the VPC. The agent does not need any of these ports open. At a minimum, the agent needs to be able to connect to your GCP resources and the Internet. ### Next Steps Now that you have an agent configured and running, it can be used within an [identity](/reference/identity) to allow your [workload](/reference/workload) to connect to your internal GCP resources. ## Private Network ### Overview Follow the steps below to install and configure an [agent](/reference/agent) within your private network. ### Prerequisites * Review the [Agent](/reference/agent) reference page. * Install the [CLI](/cli-reference/installation). * Install [Docker](https://docs.docker.com/engine/install/) ### Step One - Create an Agent Follow the [Create an Agent](/guides/agent) guide to define an agent and generate the bootstrap config file that will be used in [step two](#step-two-launch-agent-locally). ### Step Two - Launch agent locally 1. Open a new shell and execute the following command. Use the bootstrap file that was created in [step one](#step-one-create-an-agent-4). ``` cpln agent up --bootstrap-file=PATH/AGENT_NAME-bootstrapConfig.json ``` If you are using Windows, follow these instructions: * Configure Docker to **not** use the WSL 2 based engine. * Run the `cpln` command above using a Windows command prompt and not using WSL. 2. The agent will now be running, connecting to the Control Plane servers, and ready to process requests. ### Next Steps Now that you have an agent configured and running, it can be used within an [identity](/reference/identity) to allow your [workload](/reference/workload) to connect to your local resources. When running an agent locally, it is running within a local Docker container. When configuring an [identity network resource](/reference/identity#network-resources-cloud-wormhole), you must use the IP of the network adapter that Docker installed on the local machine. ## Kubernetes (k8s) Cluster ### Overview Follow the steps below to install and configure an [agent](/reference/agent) within your k8s cluster. ### Prerequisites * Review the [Agent](/reference/agent) reference page. * Install the [CLI](/cli-reference/installation). ### Step One - Create an Agent Follow the [Create an Agent](/guides/agent) guide to define an agent and generate the bootstrap config file that will be used in [step two](#step-two-launch-agent-within-a-k8s-cluster). ### Step Two - Launch agent within a K8s cluster 1. Open a new shell and execute the following command. Update tokens as necessary. Use the bootstrap file that was created in [step one](#step-one-create-an-agent-5). ``` cpln agent manifest --bootstrap-file bootstrap.json --namespace NAMESPACE --replicas 2 --cluster CLUSTER_ID > manifest.yaml # inspect/modify the manifest file manually, if needed. kubectl apply -f manifest.yaml ``` `cpln` will generate the manifest.yaml file that will deploy two replicas of the agent to the namespace of your choice (`NAMESPACE` in the example). The parameter `--cluster CLUSTER_ID` will be added to the agent's status which is used as a hint to know which cluster an agent has been deploy to. It is recommended to use the option `--replicas=2` for high availability (HA) configuration. This will generate two unique agent deployments in an active/failover configuration using leader election through the Control Plane API. Never scale the kubernetes agent deployments to more than one replica. Each deployment has a unique key that is used to uniquely identity itself in the network. If more than one replica from the same deployment is running then intermittent latency and dropped packets will occur. On startup, the agent will generate a public/private key-pair which is persisted as a k8s secret. In this scenario, the agents run under a k8s service account, which can create/modify secrets in its own namespace. If this is a concern, the agent can be configured to run in a dedicated namespace. 2. The agent will now be running, connecting to the Control Plane servers, and ready to process requests. ### Next Steps Now that you have an agent configured and running, it can be used within an [identity](/reference/identity) to allow your [workload](/reference/workload) to connect to your local resources. # Publishing & Converting VM Images Source: https://docs.controlplane.com/guides/vm-images Package a disk image as an OCI containerDisk and push it to your registry, boot from an HTTP(S) image, and convert VMDK/qcow2/VHD images for use with VM workloads. A [VM workload](/reference/workload/vm) boots from a disk image. There are two ways to provide one: * An **OCI containerDisk** — a disk image packaged as a normal OCI image and pushed to a registry. Best for reproducibility and reuse. * An **HTTP(S) disk image** — a disk file (qcow2, raw, VMDK, VHD, …) hosted at a URL and imported into a persistent disk on first boot. Best for large images or a one‑time "lift and shift." This guide covers building and publishing both, and converting images that start in another format. ## Choosing an approach Reproducible, versioned, and pulled like any other image. Recommended for images you reuse or roll out across many VMs. No image build step. Point at a URL; Control Plane imports and converts it into a persistent disk. Good for large or one-off images. ## Supported source formats The HTTP(S) importer accepts and auto‑converts these disk formats: | Format | Extensions | Notes | | :----------------- | :-------------- | :------------------------------- | | QEMU copy‑on‑write | `.qcow2` | Recommended; compact and sparse. | | Raw | `.img`, `.raw` | Uncompressed full‑size disk. | | VMware | `.vmdk` | VMware exports. | | Hyper‑V | `.vhd`, `.vhdx` | Windows/Hyper‑V exports. | | VirtualBox | `.vdi` | VirtualBox exports. | | Optical | `.iso` | CD/DVD media. | Compressed variants (`.gz`, `.xz`) are also accepted and decompressed on import. **Both** the OCI containerDisk and HTTP(S) paths run the same conversion — CDI converts the source to the boot disk's native format on import — so you can ship a `.vmdk` (or `.vhd`, `.vdi`, …) directly without converting it first. Converting to `qcow2` at build time (see [Converting images](#converting-images)) is an optional optimization that moves the one-time conversion cost out of the import. ## Option A — Publish an OCI containerDisk A containerDisk is an OCI image whose only contents are a single disk file placed in `/disk/`. KubeVirt loads that disk as the VM's boot device. Use any [supported format](#supported-source-formats) — `qcow2`, `raw`, `vmdk`, `vhd`/`vhdx`, or `vdi`. CDI converts it to the boot disk's native format on import, so no pre-conversion is needed. (Converting to `qcow2` first is optional — see [Converting images](#converting-images).) The image is built `FROM scratch` with the disk added to `/disk/`. The disk file must be readable by UID `107` (the `qemu` user that runs the VM), so set ownership with `--chown=107:107`: ```dockerfile Dockerfile theme={null} FROM scratch ADD --chown=107:107 my-disk.vmdk /disk/ ``` Nothing else belongs in a containerDisk — no base OS, no entrypoint. The disk file alone is the payload (any supported format). Omitting `--chown=107:107` causes a permission error when KubeVirt tries to open the disk. Build for `linux/amd64` and push to your org's registry. Using the CLI: ```bash theme={null} cpln image build --name my-vm-disk:v1 --push ``` Or with Docker directly (registry path `ORG.registry.cpln.io/IMAGE:TAG`): ```bash theme={null} cpln image docker-login docker buildx build --platform=linux/amd64 \ -t my-org.registry.cpln.io/my-vm-disk:v1 . docker push my-org.registry.cpln.io/my-vm-disk:v1 ``` See [Push Images to Registry](/guides/push-image) for authentication and CI/CD details. Use the Control Plane image link format — `//image/:` or `/org//image/:`: ```yaml YAML theme={null} spec: type: vm vm: bootDisk: source: oci: image: //image/my-vm-disk:v1 ``` Public containerDisks work too, e.g. `quay.io/containerdisks/ubuntu:22.04`. A VM's boot disk is always backed by a volume set, so `bootDisk.persist.volumeSet` is required alongside the source. See [Persistence](/reference/workload/vm#persistence-with-volume-sets). ## Option B — Boot from an HTTP(S) image Host the disk image at an HTTP(S) URL and let Control Plane import it. The import lands in a per‑replica persistent disk, so a boot volume set is **required**. Place the disk file (any [supported format](#supported-source-formats)) behind an `http(s)` URL the cluster can reach — an object store signed URL, a release asset, or a simple web server. ```bash theme={null} sha256sum my-disk.qcow2 ``` ```yaml YAML theme={null} spec: type: vm vm: bootDisk: source: http: url: https://example.com/images/my-disk.qcow2 checksum: 'sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08' persist: volumeSet: cpln://volumeset/my-vm-root ``` On first boot the image is downloaded, verified against the checksum, converted to the disk's native format, and written to the replica's persistent disk. Subsequent boots reuse the imported disk. Object‑store schemes (`s3://`, `gs://`) are not yet accepted directly — use a signed `http(s)` URL instead. ## Converting images CDI converts disk formats for you on import, so this step is **optional** — reach for it only to shrink an image or to pay the conversion cost once at build time instead of on every import. `qemu-img` (from the `qemu-utils` package) converts between formats: ```bash VMDK → qcow2 theme={null} qemu-img convert -p -O qcow2 disk.vmdk disk.qcow2 ``` ```bash VHD/VHDX → qcow2 theme={null} qemu-img convert -p -O qcow2 disk.vhdx disk.qcow2 ``` ```bash VDI → qcow2 theme={null} qemu-img convert -p -O qcow2 disk.vdi disk.qcow2 ``` ```bash raw → qcow2 theme={null} qemu-img convert -p -O qcow2 disk.img disk.qcow2 ``` Inspect an image to confirm its format and virtual size: ```bash theme={null} qemu-img info disk.qcow2 ``` To shrink a sparse disk after conversion, run `virt-sparsify --in-place disk.qcow2` (from `libguestfs-tools`). Smaller images push and import faster. ### Building a containerDisk from a converted image Once converted, package and push it as in [Option A](#option-a--publish-an-oci-containerdisk): ```dockerfile Dockerfile theme={null} FROM scratch ADD --chown=107:107 disk.qcow2 /disk/ ``` ```bash theme={null} cpln image build --name my-vm-disk:v1 --push ``` ## Windows images Windows guests work the same way, with a few practicalities: * Set `spec.vm.guestOS: windows` so the correct DNS bootstrap is injected. See [Cloud-init & platform injection](/reference/workload/vm#cloud-init--platform-injection). * Windows disk images can be large; the [HTTP import](#option-b--boot-from-an-https-image) path avoids building a multi‑GB OCI image. * A generation‑1 (MBR) VHD boots with `firmware.bootloader: bios`; a UEFI image uses the default `efi`. * Ensure VirtIO drivers are present in the image so the guest sees its disk and network. Microsoft's evaluation VHDs typically need the VirtIO drivers slipstreamed in before upload. ```yaml YAML theme={null} spec: type: vm vm: guestOS: windows bootDisk: source: http: url: https://example.com/images/windows-server-2022.vhd persist: volumeSet: cpln://volumeset/win-root firmware: bootloader: bios ``` ## Next steps Configure and run the VM Persist boot and data disks Registry authentication and CI/CD Deploy from a manifest # Infrastructure as Code Source: https://docs.controlplane.com/iac/overview Manage Control Plane resources as code with Terraform and Pulumi Define, deploy, and manage your Control Plane infrastructure using your preferred Infrastructure as Code tool. Both providers offer full coverage of Control Plane resources with native language support. HashiCorp's industry-standard IaC tool with HCL syntax Modern IaC in TypeScript, Python, Go, and C# ## Why Infrastructure as Code? Track all infrastructure changes in Git with full history Deploy identical environments across dev, staging, and production Review infrastructure changes through pull requests Integrate with CI/CD pipelines for automated deployments ## Getting Started Pick [Terraform](/iac/terraform) for HCL-based configuration or [Pulumi](/iac/pulumi) for general-purpose programming languages. Follow the installation guide for your chosen tool to add the Control Plane provider. Set up your Control Plane credentials using environment variables or configuration files. Write your infrastructure code to define GVCs, workloads, domains, and more. Apply your configuration to create or update resources on Control Plane. ## Resources * [Terraform Registry - Control Plane Provider](https://registry.terraform.io/providers/controlplane-com/cpln/latest) * [Pulumi Registry - Control Plane Provider](https://www.pulumi.com/registry/packages/cpln) * [Terraform Examples](https://github.com/controlplane-com/examples/tree/main/terraform) # Pulumi Provider Source: https://docs.controlplane.com/iac/pulumi Manage Control Plane resources as infrastructure-as-code using the official Pulumi provider with JavaScript, Python, Go, or .NET. The Control Plane Pulumi provider lets you manage Control Plane resources as code across JavaScript/TypeScript, Python, Go, and .NET (C#). View the official Control Plane provider on the Pulumi Registry for full documentation, resources, and data sources. ## Install Pulumi CLI ```bash theme={null} choco install pulumi ``` ```bash theme={null} brew install pulumi ``` ```bash theme={null} curl -fsSL https://get.pulumi.com | sh ``` Need a different installer or hit an error? Check Pulumi’s download & install guide for platform-specific binaries, installer options, and troubleshooting tips: [Pulumi Download & Install](https://www.pulumi.com/docs/iac/download-install) ## Login to Manage Pulumi State By default, Pulumi stores state in the Pulumi Service. To authenticate: ```bash theme={null} pulumi login ``` This opens a browser to sign in. ## Create a New Pulumi Project Create an empty directory, e.g: ```bash theme={null} mkdir pulumi-cpln-infra && cd pulumi-cpln-infra ``` Run the Pulumi project initializer: ```bash theme={null} pulumi new javascript ``` ```bash theme={null} pulumi new typescript ``` ```bash theme={null} pulumi new python ``` ```bash theme={null} pulumi new go ``` ```bash theme={null} pulumi new csharp ``` From the list of templates, choose your language (TypeScript, Python, Go, .NET (C#)). Enter a project name (e.g. `pulumi-cpln-infra`). Accept or change the default stack name (`dev`). The initializer will scaffold a basic project for your chosen language; you’ll add the Control Plane provider next. ## Install the Control Plane Provider ```bash theme={null} # npm npm install @pulumiverse/cpln # yarn yarn add @pulumiverse/cpln # pnpm pnpm add @pulumiverse/cpln ``` ```bash theme={null} pip install pulumiverse-cpln ``` ```bash theme={null} go get github.com/pulumiverse/pulumi-cpln/sdk/go/cpln ``` ```bash theme={null} dotnet add package Pulumiverse.cpln ``` ## Upgrading the Provider Keep your Pulumi Control Plane provider up to date by upgrading the package in your project: ```bash theme={null} # npm npm install @pulumiverse/cpln@latest # yarn yarn upgrade @pulumiverse/cpln@latest # pnpm pnpm add @pulumiverse/cpln@latest ``` ```bash theme={null} pip install --upgrade pulumiverse-cpln ``` ```bash theme={null} go get -u github.com/pulumiverse/pulumi-cpln/sdk/go/cpln ``` ```bash theme={null} dotnet add package Pulumiverse.Cpln ``` 🔄 After upgrading, run pulumi up to apply any necessary changes. ## Configure the Provider For more configuration information refer to the [Control Plane Installation & Configuration](https://www.pulumi.com/registry/packages/cpln/installation-configuration) page. Set your Control Plane provider configuration: ```bash theme={null} # Required pulumi config set cpln:org # Optional pulumi config set cpln:endpoint pulumi config set cpln:profile pulumi config set --secret cpln:token pulumi config set --secret cpln:refreshToken ``` ### Using Environment Variables You can also supply your Control Plane credentials via environment variables. | Option | Env Var | Default | | -------------- | -------------------- | ---------------------------------- | | `org` | `CPLN_ORG` | None (Required) | | `endpoint` | `CPLN_ENDPOINT` | `https://api.cpln.io` (Optional) | | `profile` | `CPLN_PROFILE` | The default CLI profile (Optional) | | `token` | `CPLN_TOKEN` | None (Optional) | | `refreshToken` | `CPLN_REFRESH_TOKEN` | None (Optional) | ## Create & Deploy a Resource ### Defining Resource Edit the generated code (e.g. `index.ts`, `__main__.py`, `main.go`, `Program.cs`) to define a resource. ```javascript theme={null} import * as pulumi from '@pulumi/pulumi'; import * as cpln from '@pulumiverse/cpln'; const gvc = new cpln.Gvc('example', { name: 'example', // …other args }); ``` ```python theme={null} import pulumi import pulumiverse_cpln as cpln gvc = cpln.Gvc("example", name="example", # …other args ) ``` ```go theme={null} package main import ( "github.com/pulumiverse/pulumi-cpln/sdk/go/cpln" "github.com/pulumi/pulumi/sdk/v3/go/pulumi" ) func main() { pulumi.Run(func(ctx *pulumi.Context) error { _, err := cpln.NewGvc(ctx, "example", &cpln.GvcArgs{ Name: pulumi.String("example"), // …other args }) return err }) } ``` ```csharp theme={null} using System.Collections.Generic; using Pulumi; using Pulumiverse.Cpln; return await Deployment.RunAsync(() => { // Add your resources here var gvc = new Gvc("example", new GvcArgs { Name = "example", // …other args }); // Export outputs here return new Dictionary { }; }); ``` Refer to the [Pulumi GVC resource documentation](https://www.pulumi.com/registry/packages/cpln/api-docs/gvc) page for the full list of args. ### Deploying Resource Run the following command to deploy your resources to the Control Plane platform: ```bash theme={null} pulumi up ``` ### Destroying Resource When you need to tear down your Control Plane infrastructure, run the following command: ```bash theme={null} pulumi destroy ``` ### Removing the Stack After destroying resources, you can delete the stack itself: ```bash theme={null} pulumi stack rm ``` ## Migrating from Terraform If you’re moving existing Terraform projects to Pulumi, you can: Ensure your Terraform configuration files are in the current working directory, then run the converter: ```bash theme={null} pulumi convert --from terraform --language javascript --out pulumi-cpln-infra-javascript ``` ```bash theme={null} pulumi convert --from terraform --language typescript --out pulumi-cpln-infra-typescript ``` ```bash theme={null} pulumi convert --from terraform --language python --out pulumi-cpln-infra-python ``` ```bash theme={null} pulumi convert --from terraform --language go --out pulumi-cpln-infra-go ``` ```bash theme={null} pulumi convert --from terraform --language csharp --out pulumi-cpln-infra-csharp ``` Before importing your Terraform state, initialize (or select) a Pulumi stack to hold it. Replace `` with your desired name (e.g. `migrate`): ```bash theme={null} pulumi stack init migrate ``` Specify the correct path to your Terraform state file, then run the import command: ```bash theme={null} pulumi import --from terraform /path/to/terraform.tfstate ``` Replace `/path/to/terraform.tfstate` with the location of your state file. This reads the specified state and merges its resources into your Pulumi stack’s state. Review and refine the generated Pulumi code. For full details, examples, and advanced workflows, see the Pulumi guide on [Migrating from Terraform to Pulumi](https://www.pulumi.com/docs/iac/adopting-pulumi/migrating-to-pulumi/from-terraform). ## Next Steps Dive into all Control Plane resources & data sources at the [Control Plane Pulumi Registry](https://www.pulumi.com/registry/packages/cpln). Check the registry docs regularly for new releases, resources, and data sources. # Terraform Provider Source: https://docs.controlplane.com/iac/terraform Manage Control Plane resources as infrastructure-as-code using the official Terraform provider with HCL configuration. The Control Plane Terraform provider lets you manage Control Plane resources as code using HashiCorp Configuration Language (HCL). View the official Control Plane provider on the Terraform Registry for full documentation, resources, and data sources. ## Setup Add the Control Plane provider to your Terraform configuration: ```hcl theme={null} terraform { required_providers { cpln = { source = "controlplane-com/cpln" } } } provider "cpln" { # Required # Can use CPLN_ORG Environment Variable org = var.org # Optional # Default Value: https://api.cpln.io # Can use CPLN_ENDPOINT Environment Variable endpoint = var.endpoint # Optional # Can use CPLN_PROFILE Environment Variable profile = var.profile # Optional # Can use CPLN_TOKEN Environment Variable token = var.token # Optional # Can use CPLN_REFRESH_TOKEN Environment Variable refresh_token = var.refresh_token } ``` Then initialize your project: ```bash theme={null} terraform init ``` Requires Terraform 0.13+ ## Resources * [Download Terraform CLI](https://www.terraform.io/downloads.html) * [Terraform Documentation](https://www.terraform.io/docs/index.html) ## Upgrading Provider Version After installing a new provider version, follow these steps to upgrade an existing installation: 1. Update the version property inside the HCL file that contains the `required_providers` declaration block for the `cpln` provider. 2. Run the command: `terraform init -upgrade` to upgrade the Terraform dependencies (state file, etc.). The Terraform commands (plan, apply, etc.) will now use the latest version. If necessary, the provider version can be downgraded. ## Examples * [Terraform - Control Plane Examples](https://github.com/controlplane-com/examples/tree/main/terraform) # Introduction Source: https://docs.controlplane.com/introduction Control Plane documentation for deploying and managing workloads across multiple clouds from a single platform. Control Plane Control Plane Control Plane is a cloud virtualization platform that lets you combine any cloud infrastructure into one or more AI-native virtual clouds. Compose bare metal, VMs, Kubernetes, AWS, GCP, Azure, and OCI into a single environment, cut infrastructure costs by 30–50%, and deploy without vendor lock-in. Learn about the platform and its capabilities Get started in minutes with our quickstart guide SOC 2 Type II, HIPAA, and more Connect Claude, Cursor, Codex, Antigravity, and other AI agents to deploy and manage your infrastructure. *** ## Getting Started New to Control Plane? Start here to understand the fundamentals and deploy your first application. Get started with Control Plane in minutes Understand Organizations, GVCs, Workloads, and Policies Comprehensive guide to the Control Plane CLI REST API documentation for programmatic access *** ## Build & Deploy Everything you need to build, deploy, and manage your applications on Control Plane. Set up Global Virtual Clouds Deploy containerized applications Set up custom domains and routing Push container images to the registry Securely store and access sensitive data Configure access control and permissions *** ## Infrastructure Manage Kubernetes clusters, bring your own infrastructure, and connect on-premises resources. Create and manage Kubernetes clusters across clouds Bring Your Own Kubernetes to Control Plane Connect private networks and on-premises resources *** ## Observability & Logging Monitor your applications and infrastructure with integrated observability tools. Export logs to Datadog, CloudWatch, Coralogix, and more Centralized metrics collection and management *** ## Integrations & Tools Integrate Control Plane into your existing workflows with our tools and providers. Infrastructure as Code Modern IaC in your language Plugin + MCP server for AI agents Kubernetes-native management *** ## Reference Documentation Detailed documentation for all Control Plane resources and configurations. Containers, autoscaling, networking, and more Global Virtual Cloud configuration Cloud identities and access Fine-grained access control Custom domains and TLS Complete resource documentation *** ## Additional Resources Latest updates and changes CLI version history Need help? Check out the [FAQ](https://controlplane.com/faq), watch our [Product Demos](https://controlplane.com/product-demos), or read the latest on our [Blog](https://controlplane.com/blog). # AWS ECR Source: https://docs.controlplane.com/mk8s/add-ons/aws_ecr Enable Managed Kubernetes clusters to pull container images from private Amazon ECR registries. ## Overview The AWS ECR add-on facilitates access to [Amazon ECR](https://docs.aws.amazon.com/AmazonECR/latest/userguide/what-is-ecr.html) for managed Kubernetes clusters. ## Supported Providers * [AWS](../aws) * [Hetzner](../hetzner) * [Generic](../generic) ## Prerequisites * The [AWS Workload Identity](/mk8s/add-ons/aws_workload_identity) add-on must be enabled for your cluster. * For **AWS providers** accessing private ECR within the **same account**, no additional Role ARN (Amazon Resource Name) is required. * For **non-AWS providers** or when accessing an ECR in a **different account**, create an AWS IAM Role with the necessary permissions to pull images from a private AWS ECR. Attach the `AmazonEC2ContainerRegistryReadOnly` managed policy to grant these permissions. You will need the IAM Role's ARN for configuration. ## How to Enable You can enable the AWS ECR add-on for your Kubernetes cluster either during the cluster creation process or at any time afterwards. The following sections outline the methods for enabling the add-on: ### At Cluster Creation #### AWS Providers Accessing Private ECR in the Same Account * **Through Cluster Manifest**: Add the following snippet to your cluster manifest when creating the cluster: ```yaml YAML theme={null} spec: ... addOns: awsECR: {} ... ``` * **Using the Console**: If you're creating the cluster through the console, navigate to `Add-ons`, find the `AWS ECR` add-on in the list of available add-ons, and toggle it on. #### For NON-AWS Providers or Different AWS Account Access * **Through Cluster Manifest**: Add the following snippet to your cluster manifest when creating the cluster: ```yaml YAML theme={null} spec: ... addOns: awsECR: roleArn: 'arn:aws:iam::999999999999:role/mk8s-ecr-driver' ... ``` * **Using the Console**: If you're creating the cluster through the console, navigate to `Add-ons`, find the `AWS ECR` add-on in the list of available add-ons, toggle it on, and then enter the ROLE ARN required for accessing the AWS ECR repository. ### After Cluster Creation If the AWS ECR add-on was not enabled during the cluster creation, you can still enable it using the following methods: #### AWS Providers Accessing Private ECR in the Same Account ##### Using Manifest To enable the AWS ECR add-on after cluster creation, add the following to your cluster's YAML manifest: * **Direct Edit & Apply**: Navigate to your cluster in the Console, and use the `Edit & Apply` option. * **CLI Application**: Apply the entire manifest using the `cpln apply >_` command or through the `cpln` [CLI](/cli-reference/overview). ```yaml YAML theme={null} spec: ... addOns: awsECR: {} ... ``` * **Using the Console**: If you're creating the cluster through the console, navigate to `Add-ons` of your cluster, find the `AWS ECR` add-on in the list of available add-ons, and toggle it on. #### For NON-AWS Providers or Different AWS Account Access * **Through Cluster Manifest**: Add the following snippet to your cluster manifest when creating the cluster: ```yaml YAML theme={null} spec: ... addOns: awsECR: roleArn: 'arn:aws:iam::999999999999:role/mk8s-ecr-driver' ... ``` * **Using the Console**: If you're creating the cluster through the console, navigate to `Add-ons` of your cluster, find the `AWS ECR` add-on in the list of available add-ons, toggle it on, and then enter the ROLE ARN required for accessing the AWS ECR repository. ## Usage Instructions ### Accessing ECR in Same AWS Account If you are using an AWS provider to access ECR within the same AWS account, follow these steps: #### Step 1 - Create OIDC Identity Provider in AWS (If not already done) Skip this step if you have already created the provider as part of the [AWS Workload Identity configuration](/mk8s/add-ons/aws_workload_identity#steps-to-configure-access). 1. Retrieve the cluster's **oidcProviderUrl**: ``` cpln mk8s get -o json $cluster_name | jq -r '.status.oidcProviderUrl' ``` 2. Access the AWS console and navigate to **IAM**. In the left panel, under `Access management`, select `Identity providers` and then click `Add provider`. 3. Select `OpenID Connect`, paste the `Provider URL` obtained in the previous step, and click `Get thumbprint`. 4. In the `Audience` field, enter `sts.amazonaws.com`. #### Step 2 - Creating Kubernetes workload Deploy a Kubernetes workload using an image from a private ECR Registry: ```yaml YAML theme={null} --- apiVersion: apps/v1 kind: Deployment metadata: name: example-ecr spec: replicas: 1 selector: matchLabels: app: example template: metadata: labels: app: example spec: terminationGracePeriodSeconds: 0 containers: - image: [account-id].dkr.ecr.[region].amazonaws.com/example name: example imagePullPolicy: Always ``` Replace `[account-id]` and `[region]` with appropriate values. ### Accessing ECR with NON-AWS Provider or Different AWS Account If you are using a non-AWS provider or an AWS provider to access ECR in a different AWS account, follow these steps: #### Step 1 - Create OIDC Identity Provider in AWS (If not already done) Skip this step if you have already created the provider as part of the [AWS Workload Identity configuration](/mk8s/add-ons/aws_workload_identity#steps-to-configure-access). 1. Retrieve the cluster's **oidcProviderUrl**: ``` cpln mk8s get -o json $cluster_name | jq -r '.status.oidcProviderUrl' ``` 2. Access the AWS console and navigate to **IAM**. In the left panel, under `Access management`, select `Identity providers` and then click `Add provider`. 3. Select `OpenID Connect`, paste the `Provider URL` obtained in the previous step, and click `Get thumbprint`. 4. In the `Audience` field, enter `sts.amazonaws.com`. #### Step 2 - Update Trust Policy 1. Obtain the trust policy template: ``` cpln mk8s get -o json $cluster_name | jq -r '.status.addOns.awsECR.trustPolicy' ``` 2. Update the `Trust Policy` of the IAM Role in the AWS Account to reflect these changes. #### Step 3 - Creating Kubernetes workload Deploy a Kubernetes workload using an image from private ECR Registry. Use proper values. ```yaml YAML theme={null} apiVersion: apps/v1 kind: Deployment metadata: name: example-ecr spec: replicas: 1 selector: matchLabels: app: example template: metadata: labels: app: example spec: terminationGracePeriodSeconds: 0 containers: - image: [account-id].dkr.ecr.[region].amazonaws.com/example name: example imagePullPolicy: Always ``` Replace `[account-id]` and `[region]` with appropriate values. # AWS EFS Source: https://docs.controlplane.com/mk8s/add-ons/aws_efs Enable Kubernetes workloads on Managed Kubernetes clusters to access Amazon EFS for shared, persistent file storage. ## Overview The AWS EFS add-on facilitates access to [Amazon EFS](https://docs.aws.amazon.com/efs/latest/ug/whatisefs.html) for Kubernetes workloads running on a managed Kubernetes cluster. ## Supported Providers * [AWS](../aws) * [Hetzner](../hetzner) * [Generic](../generic) ## Prerequisites * [AWS Workload Identity](/mk8s/add-ons/aws_workload_identity) * AWS Account ## How to Enable To use the AWS EFS add-on for your Kubernetes cluster, ensure you have created an AWS IAM Role with the [necessary permissions](https://docs.aws.amazon.com/efs/latest/ug/security_iam_service-with-iam.html) to use the AWS EFS filesystem. The ARN (Amazon Resource Name) of this role is required for the configuration steps that follow. For the required permissions, you can attach the managed policy `AmazonEFSCSIDriverPolicy` to the IAM Role and verify that the EFS filesystem policy permits access. The AWS EFS add-on can be enabled for your Kubernetes cluster either during the cluster creation process or at any time thereafter. The following sections outline the methods for enabling the add-on: ### At Cluster Creation * **Through Cluster Manifest**: Add the following snippet to your cluster manifest when creating the cluster: ```yaml YAML theme={null} spec: ... addOns: awsEFS: roleArn: 'arn:aws:iam::999999999999:role/mk8s-efs-driver' ... ``` * **Using the Console**: If you're creating the cluster through the console, navigate to `Add-ons`, find the `AWS EFS` add-on in the list of available add-ons, toggle it on, and then enter the ROLE ARN required for accessing the AWS EFS filesystem. ### After Cluster Creation If the AWS EFS add-on was not enabled during the cluster creation, you can still enable it using either of the following methods: #### Using Manifest To enable the AWS EFS add-on after cluster creation, add the following to your cluster's YAML manifest: * **Direct Edit & Apply**: Navigate to your cluster in the Console, and use the `Edit & Apply` option. * **CLI Application**: Apply the entire manifest using the `cpln apply >_` command or through the `cpln` [CLI](/cli-reference/overview). ```yaml YAML theme={null} spec: ... addOns: awsEFS: roleArn: 'arn:aws:iam::999999999999:role/mk8s-efs-driver' ... ``` #### Using the UI 1. **Navigate to the Control Plane Console**: Open [Control Plane Console](https://console.cpln.io/console/). 2. **Select Your Kubernetes Cluster**: In the Control Plane Console, go to `Kubernetes` in the left sidebar, and click on the cluster you wish to configure. 3. **Activate the Dashboard**: Choose `Add-ons`, find the `AWS EFS` add-on in the list, and toggle it on. 4. **Enter Role ARN**: Provide the ROLE ARN necessary for AWS EFS filesystem access. ## Usage Instructions After enabling the AWS EFS add-on, two additional steps are required before you can successfully create [Kubernetes volumes](https://kubernetes.io/docs/concepts/storage/volumes/) using AWS EFS. First, it's essential to update the **trust policy** to grant the Managed Kubernetes cluster the necessary permissions to assume the AWS Role for accessing AWS EFS. This step ensures that your Kubernetes cluster can securely interact with the AWS EFS service. Second, you must properly configure the [Kubernetes Storage Class](https://kubernetes.io/docs/concepts/storage/storage-classes/). This configuration allows Kubernetes to understand how to provision storage based on AWS EFS for your applications. Follow the steps below to configure: ### Step 1 - Create OIDC Identity Provider in aws (If not already done) Skip this step if you have already created the provider as part of the [AWS Workload Identity configuration](/mk8s/add-ons/aws_workload_identity#steps-to-configure-access). 1. Retrieve the cluster's **oidcProviderUrl**: ``` cpln mk8s get -o json $cluster_name | jq -r '.status.oidcProviderUrl' ``` 2. Access the AWS console and navigate to **IAM**. In the left panel, under `Access management`, select `Identity providers` and then click `Add provider`. 3. Select `OpenID Connect`, paste the `Provider URL` obtained in the previous step, and click `Get thumbprint`. 4. In the `Audience` field, enter `sts.amazonaws.com`. ### Step 2 - Update Trust Policy 1. Obtain the trust policy template: ``` cpln mk8s get -o json $cluster_name | jq -r '.status.addOns.awsEFS.trustPolicy' ``` 2. Update the `Trust Policy` of the IAM Role in your AWS Account to reflect these changes. ### Step 3 - Create Storage Class Create the following Storage Class in your Managed Kubernetes cluster. For guidance on accessing your cluster, refer to the documentation page of your Provider. Ensure to replace the `fileSystemId` with the correct one from your account. ```yaml YAML theme={null} kind: StorageClass apiVersion: storage.k8s.io/v1 metadata: name: efs-dynamic provisioner: efs.csi.aws.com parameters: provisioningMode: efs-ap fileSystemId: fs-999999999999 # Replace with your EFS FileSystemId directoryPerms: '700' gidRangeStart: '1000' # optional gidRangeEnd: '2000' # optional basePath: '/dynamic_provisioning' # optional subPathPattern: '${.PVC.namespace}/${.PVC.name}' # optional ensureUniqueDirectory: 'true' # optional reuseAccessPoint: 'false' # optional ``` ### Step 4 - Example on creating volumes Create the following `PersistentVolumeClaim` and `Pod` in your Managed Kubernetes cluster. This example demonstrates creating a Pod that writes the current date to a file every 5 seconds, utilizing a volume backed by the AWS EFS, as configured previously. ```yaml YAML theme={null} apiVersion: v1 kind: PersistentVolumeClaim metadata: name: efs-claim spec: accessModes: - ReadWriteMany storageClassName: efs-dynamic resources: requests: storage: 5Gi --- apiVersion: v1 kind: Pod metadata: name: efs-app spec: terminationGracePeriodSeconds: 0 containers: - name: app image: alpine:latest command: ['/bin/sh'] args: ['-c', 'while true; do echo $(date -u) >> /data/out; sleep 5; done'] volumeMounts: - name: persistent-storage mountPath: /data volumes: - name: persistent-storage persistentVolumeClaim: claimName: efs-claim ``` # AWS ELB Source: https://docs.controlplane.com/mk8s/add-ons/aws_elb Configure the AWS Load Balancer Controller for routing external traffic to your cluster using NLB, ALB, or CloudFront WAF integration. ## Overview The AWS ELB add-on configures the [AWS Load Balancer Controller](https://kubernetes-sigs.github.io/aws-load-balancer-controller/latest/) for use by the cluster. This add-on is required for routing external traffic to the cluster with: * AWS [Network Load Balancer (NLB)](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/introduction.html) * AWS [Application Load Balancer (ALB)](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html) * UDP protocol access from public users * AWS [Cloudfront](https://aws.amazon.com/blogs/containers/protecting-your-amazon-eks-web-apps-with-aws-waf/) K8S WAF Integration ## Supported Providers * [AWS](../aws) ## Prerequisites * [AWS Workload Identity](/mk8s/add-ons/aws_workload_identity) * AWS Account ## Step 1: Enable the AWS ELB Add-On The awsELB addon has one optional parameter of elbRole. If provided, the controller will this role to access the AWS API. If no role is provided then the [recommended role](https://kubernetes-sigs.github.io/aws-load-balancer-controller/latest/deploy/installation/#configure-iam) from AWS will be used. The [AWS Workload Identity](/mk8s/add-ons/aws_workload_identity) Add-On is leveraged in the configuration of the load balancer controller to configure access for this role. ### Using Manifest Enable AWS ELB w/ a custom role ```yaml YAML theme={null} spec: ... addOns: awsELB: roleArn: 'arn:aws:iam::999999999999:role/custom-elb-addon-role' ... ``` Enable AWS ELB w/ the built-in role ```yaml YAML theme={null} spec: ... addOns: awsELB: {} ... ``` This add-on can be enabled at cluster creation or afterwards. ### Using the UI 1. **Navigate to the Control Plane Console**: Open [Control Plane Console](https://console.cpln.io/console/). 2. **Select Your Kubernetes Cluster**: In the Control Plane Console, go to `Kubernetes` in the left sidebar, and click on the cluster you wish to configure. 3. **Enable the Add-on**: Choose `Add-ons`, find the `AWS ELB` add-on in the list, and toggle it on. 4. **Optional: Enter Role ARN**: Select the AWS ELB menu. Provide the ROLE ARN necessary for AWS ELB Controller access to your AWS account. ## Step 2: Subnet Configuration The [AWS Load Balancer Controller](https://kubernetes-sigs.github.io/aws-load-balancer-controller/latest/) needs to know which subnets it is allowed to use for internet facing and internal load balancer resources for this cluster. The following tags must be added to AWS Subnets in order to designate how they can be used: **Private subnet tag**: Key: "kubernetes.io/role/internal-elb" Value: "1" **Public subnet tag**: Key: "kubernetes.io/role/elb" Value: "1" ## Step 3: Verify Controller Logs After enabling the AWS ELB add-on, check to make sure that the controller is running and that it can access the AWS API using AWS Workload Identity. Connect to the MK8s Cluster using the Kubernetes Dashboard or the kubectl CLI. ### Dashboard UI 1. Select the `kube-system` namespace from the drop down menu on the top of the page. 2. Select `Pods` on the left menu. 3. In the center pane, find the aws-load-balancer-controller pod. 4. Click the three dot menu on the right side of the pod and choose `Logs`. A live log view will open. Inspect the output for any error messages. ### kubectl ```bash theme={null} kubectl logs -n kube-system -l app.kubernetes.io/name=aws-load-balancer-controller -f ``` If the controller cannot access the AWS API, verify that you have performed [Step 1 for the AWS Identity Add-On](/mk8s/add-ons/aws_workload_identity#steps-to-configure-access). Additional troubleshooting steps are available in this AWS Knowledge Center [article](https://repost.aws/knowledge-center/load-balancer-troubleshoot-creating). ## Step 4 - Create Sample App Create the following `Service` and `Deployment` in your Managed Kubernetes cluster. This example demonstrates creating a workload that is exposed externally. ```yaml YAML theme={null} apiVersion: v1 kind: Service metadata: name: httpbin annotations: service.beta.kubernetes.io/aws-load-balancer-type: external service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing spec: ports: - port: 80 targetPort: 80 protocol: TCP type: LoadBalancer selector: app: httpbin --- apiVersion: apps/v1 kind: Deployment metadata: name: httpbin spec: replicas: 1 selector: matchLabels: app: httpbin template: metadata: labels: app: httpbin spec: containers: - name: httpbin image: kennethreitz/httpbin ports: - name: tcp containerPort: 80 ``` ### Collect the endpoint Once the objects are created, check the status of the Service. An Endpoint should be created for it automatically and listed under the External-IP column of the output. ```bash theme={null} kubectl get svc httpbin ``` ### Test the endpoint ```bash theme={null} curl http://${endpoint}/headers ``` ## Troubleshooting Check the describe output of the service and the logs of the load balancer controller if the endpoint is not created or it is not working correctly. ```bash theme={null} kubectl describe svc httpbin ``` ```bash theme={null} kubectl logs -n kube-system -l app.kubernetes.io/name=aws-load-balancer-controller -f ``` ## Cleanup ```bash theme={null} kubectl delete svc,deployment httpbin ``` # AWS Workload Identity Source: https://docs.controlplane.com/mk8s/add-ons/aws_workload_identity Allow pods on Managed Kubernetes clusters to securely assume AWS IAM Roles for accessing AWS resources like S3, DynamoDB, and other services. ## Overview The AWS Workload Identity is a feature designed to enhance security and streamline access management for Control Plane managed Kubernetes clusters. This feature enables Pods running on Kubernetes clusters to assume an AWS IAM Role. By leveraging these IAM Roles, Pods can securely access AWS resources, adhering to the permissions defined in the corresponding IAM policies. A key application of this feature is in scenarios where a Pod needs to interact with AWS services. For instance, a Pod requiring access to an S3 bucket can assume an IAM Role with the necessary permissions to perform actions on that bucket. Enable AWS Workload Identity when secure access to AWS resources from Pods on the cluster is required. ## Supported Providers * [AWS](../aws) * [Hetzner](../hetzner) * [Generic](../generic) ## Prerequisites * AWS Account ## How to Enable The `AWS Workload Identity` add-on can be enabled for your Kubernetes cluster either during the cluster creation process or at any time thereafter. The following sections outline the methods for enabling the add-on: ### At Cluster Creation * **Through Cluster Manifest**: Add the following snippet to your cluster manifest when creating the cluster: ```yaml YAML theme={null} spec: ... addOns: awsWorkloadIdentity: {} ... ``` * **Using the Console**: If you're creating the cluster through the console, navigate to `Add-ons`, find the `AWS Workload Identity` add-on in the list of available add-ons, and toggle it on. ### After Cluster Creation If the `AWS Workload Identity` add-on was not enabled during the cluster creation, you can still enable it using either of the following methods: #### Using Manifest Under `spec.addOns` in the YAML manifest of the cluster, you can edit it either by navigating to the cluster in the Console and using the `Edit & Apply` option for the cluster, or by applying the entire manifest using the `cpln apply >_` option in the upper right corner or by using the `cpln` [CLI](/cli-reference/overview). Add the following: ```yaml YAML theme={null} spec: --- addOns: awsWorkloadIdentity: {} ``` #### Using UI 1. **Navigate to Control Plane Console**: Visit the [Control Plane Console](https://console.cpln.io/console/). 2. **Navigate to the Kubernetes cluster**: In the Control Plane Console, navigate to `Kubernetes` in the left sidebar panel and click on the Kubernetes cluster for which you want to enable the dashboard. 3. **Enable the AWS Workload Identity add-on**: Choose `Add-ons` and locate the `AWS Workload Identity` add-on from the list of available add-ons, then toggle it on. ## Setup If the role provided to Control Plane includes the ability to create the OIDC Identity Provider, then it will be created for you automatically. If the role provided to Control Plane does not include access to create OIDC Identity Providers, then the following manual steps must be completed before AWS Workload Identity or any other dependent add-ons will function. After enabling AWS Workload Identity, an [OIDC Identity Provider](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc.html) for each cluster must be created in your AWS account before it can be used. The `oidcProviderUrl` for the OIDC Identity Provider is in the status of the cluster. 1. **Create OIDC Identity Provider in AWS** 1. Retrieve the cluster's **oidcProviderUrl**: CLI ``` cpln mk8s get -o json $cluster_name | jq -r '.status.oidcProviderUrl' ``` UI 1. From the Managed Kubernetes cluster in the UI, select `View` from the `Actions` drop down in the upper right corner. A new window will open showing the content of the Managed Kubernetes object in the Control Plane API. 2. Toggle the `Slim` button so it is turned off. 3. The `providerUrl` is shown in the object at `.status.addOns.awsWorkloadIdentity.oidcProviderConfig` 2. Access the AWS console and navigate to **IAM**. In the left panel, under `Access management`, select `Identity providers` and then click `Add provider`. 3. Select `OpenID Connect`, paste the `Provider URL` obtained in the previous step, and click `Get thumbprint`. 4. In the `Audience` field, enter `sts.amazonaws.com`. ## Usage Instructions After enabling AWS Workload Identity, your Managed Kubernetes cluster becomes an identity provider for your Pods. To grant a Pod access, ensure it uses a [Kubernetes Service Account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) with the annotation: `eks.amazonaws.com/role-arn: "arn:aws:iam:::role/IAM-ROLE-HERE"`. This setup is compatible with all [Kubernetes Workloads](https://kubernetes.io/docs/concepts/workloads/), as they ultimately provision Pods. Follow these steps below to configure. ### Steps to configure access 1. **Retrieve and Save the Trust Policy JSON** 1. Obtain the trust policy template: ``` cpln mk8s get -o json $cluster_name | jq -r '.status.addOns.awsWorkloadIdentity.trustPolicy' ``` 2. Save the obtained policy to a file named `example-trust-policy.json`. Then, modify the trust policy by replacing `` and `` with the appropriate values: * For ``, use `default`. * For ``, use `mk8s-identity-example`. 2. **Create an IAM Role** * You can create a new IAM Role using the AWS Console, or use an existing one. For creating a role via CLI, follow this example: * Use the obtained trust policy from the previous step as `default-trust-policy.json` * Run the following command: ``` aws iam create-role --role-name "mk8s-identity-example" --assume-role-policy-document file://default-trust-policy.json ``` 3. **Create Kubernetes Service Account and a Pod**\ Create the Kubernetes Service Account and a Pod in your Managed Kubernetes cluster. For guidance on accessing your cluster, refer to the documentation page of your Provider. * Replace `` with your AWS Account ID in the following YAML configuration: ```yaml YAML theme={null} apiVersion: v1 kind: ServiceAccount metadata: name: mk8s-identity-example namespace: default annotations: eks.amazonaws.com/role-arn: 'arn:aws:iam:::role/mk8s-identity-example' --- apiVersion: v1 kind: Pod metadata: name: identity-example namespace: default spec: terminationGracePeriodSeconds: 0 serviceAccountName: mk8s-identity-example containers: - command: - sleep - '99d' image: amazon/aws-cli:2.13.35 name: shell ``` * The Pod `identity-example` can now access AWS resources using the IAM role `arn:aws:iam:::role/mk8s-identity-example`. # Azure ACR Source: https://docs.controlplane.com/mk8s/add-ons/azure_acr Enable Managed Kubernetes clusters to pull container images from private Azure Container Registry instances. ## Overview The Azure ACR add-on facilitates access to [Azure ACR](https://azure.microsoft.com/en-us/products/container-registry) for managed Kubernetes clusters. ## Supported Providers * All providers are supported ## Prerequisites * The [Azure Workload Identity](/mk8s/add-ons/azure_workload_identity) add-on must be enabled for your cluster. ## How to Enable You can enable the AWS ECR add-on for your Kubernetes cluster either during the cluster creation process or at any time afterwards. The following sections outline the methods for enabling the add-on: ### At Cluster Creation #### Azure Provider Accessing Private ECR in the Same Account * **Through Cluster Manifest**: Add the following snippet to your cluster manifest when creating the cluster: ```yaml YAML theme={null} spec: ... addOns: azureACR: {} ... ``` * **Using the Console**: If you're creating the cluster through the console, navigate to `Add-ons`, find the `Azure ACR` add-on in the list of available add-ons, and toggle it on. ### After Cluster Creation If the AWS ECR add-on was not enabled during the cluster creation, you can still enable it using the following methods: # Azure Workload Identity Source: https://docs.controlplane.com/mk8s/add-ons/azure_workload_identity Assign Azure AD service principals to Kubernetes pods, enabling secure access to Azure resources from Managed Kubernetes clusters. ## Overview Azure workload identity add-on can assign Azure AD service principals to Kubernetes pods. ## Supported Providers * All providers # CPLN Platform Source: https://docs.controlplane.com/mk8s/add-ons/byok Turn your Managed Kubernetes cluster into a BYOK location by enabling the CPLN Platform add-on for centralized management through Control Plane. ## Overview Turn your Kubernetes cluster into a BYOK location by enabling the CPLN Platform add-on. This allows you to use the Control Plane Platform to manage the cluster. ## How to Enable The CPLN Platform add-on can be enabled for your Kubernetes cluster either during the cluster creation process or at any time thereafter. The following sections outline the methods for enabling the add-on: ### At Cluster Creation * **Through Cluster Manifest**: Add the following snippet to your cluster manifest when creating the cluster: ```yaml YAML theme={null} spec: ... addOns: byok: { location: //location/ } ... ``` * **Using the Console**: If you're creating the cluster through the console, navigate to `Add-ons`, find the `CPLN Platform` add-on in the list of available add-ons, and toggle it on. ### After Cluster Creation If the CPLN Platform add-on was not enabled during the cluster creation, you can still enable it using either method. ## Uninstall Procedure To uninstall BYOK from a cluster, follow this guide: [Uninstall Procedure](/byok/overview#uninstall-procedure) ## Configuration The CPLN Platform add-on supports extensive configuration options to customize the behavior of various components. Below is a brief overview of available settings along with links to the comprehensive guides: ### Top-level Configuration | Field | Type | Description | | --------------- | ------- | ---------------------------------------------------------------------------------- | | `ignoreUpdates` | boolean | Whether to ignore automatic software updates from the Control Plane Platform | | `location` | string | **Required.** The CPLN Platform location link (e.g., `//location/`) | ### Component Configurations #### Actuator Configuration **Path:** `byok.config.actuator` The Actuator component receives events from Control Plane and uses the metadata provided in the events to instantiate the desired configuration on the kubernetes cluster. This can be Kubernetes deployments, domain routing configuration, storage devices, etc. See [Actuator Settings](/byok/settings/actuator) for all available options. #### Additional CPLN PLatform Options See [CPLN Platform Settings](/byok/settings/agent#general-configurations) for a detailed description of additional settings. ### Notes * The `location` field is required and must be set to a valid CPLN BYOK location. * You CANNOT change the `location` field after it has been set # Kubernetes Dashboard (Deprecated) Source: https://docs.controlplane.com/mk8s/add-ons/dashboard Enable the Kubernetes Dashboard add-on to manage and monitor your Managed Kubernetes cluster through a web-based user interface. This add-on is deprecated, we suggest using [Headlamp](headlamp). ## Overview The Kubernetes Dashboard add-on provides a user-friendly interface, enabling users to manage and monitor their Kubernetes clusters directly from a web interface. This add-on simplifies cluster management tasks and offers a comprehensive view of the cluster's state. For more details, please refer to the official [Kubernetes Dashboard documentation](https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/). ## How to Enable The Dashboard add-on can be enabled for your Kubernetes cluster either during the cluster creation process or at any time thereafter. The following sections outline the methods for enabling the add-on: ### At Cluster Creation * **Through Cluster Manifest**: Add the following snippet to your cluster manifest when creating the cluster: ```yaml YAML theme={null} spec: ... addOns: dashboard: {} ... ``` * **Using the Console**: If you're creating the cluster through the console, navigate to `Add-ons`, find the `Dashboard` add-on in the list of available add-ons, and toggle it on. ### After Cluster Creation If the Dashboard add-on was not enabled during the cluster creation, you can still enable it using either of the following methods: #### Using Manifest Under `spec.addOns` in the YAML manifest of the cluster, you can edit it either by navigating to the cluster in the Console and using the `Edit & Apply` option for the cluster, or by applying the entire manifest using the `cpln apply >_` option in the upper right corner or by using the `cpln` [CLI](/cli-reference/overview). Add the following: ```yaml YAML theme={null} spec: --- addOns: dashboard: {} ``` #### Using UI 1. **Navigate to Control Plane Console**: Visit the [Control Plane Console](https://console.cpln.io/console/). 2. **Navigate to the Kubernetes cluster**: In the Control Plane Console, navigate to `Kubernetes` in the left sidebar panel and click on the Kubernetes cluster for which you want to enable the dashboard. 3. **Enable the Dashboard**: Choose `Add-ons` and locate the `Dashboard` add-on from the list of available add-ons, then toggle it on. ## How to Access Once enabled, the Dashboard add-on can be used as follows: 1. **Navigate to Control Plane Console**: Visit the [Control Plane Console](https://console.cpln.io/console/). 2. **Access the Dashboard**: In the Control Plane Console, navigate to `Kubernetes` in the left sidebar panel and click on `Open` under `Dashboard` for the cluster. # Headlamp Dashboard Source: https://docs.controlplane.com/mk8s/add-ons/headlamp Enable Headlamp, an extensible Kubernetes web UI, as the recommended dashboard for managing your Managed Kubernetes cluster. ## Overview Headlamp is an easy-to-use and extensible Kubernetes web UI. Headlamp was created to blend the traditional feature set of other web UIs/dashboards (i.e., to list and view resources) with added functionality. For more details, please refer to the official [Headlamp documentation](https://github.com/kubernetes-sigs/headlamp). ## How to Enable The Headlamp dashboard add-on can be enabled for your Kubernetes cluster either during the cluster creation process or at any time thereafter. The following sections outline the methods for enabling the add-on: ### At Cluster Creation * **Through Cluster Manifest**: Add the following snippet to your cluster manifest when creating the cluster: ```yaml theme={null} spec: addOns: headlamp: {} ``` * **Using the Console**: If you're creating the cluster through the console, navigate to `Add-ons`, find the `Headlamp` add-on in the list of available add-ons, and toggle it on. ### After Cluster Creation If the Headlamp add-on was not enabled during the cluster creation, you can still enable it using either of the following methods: #### Using Manifest Under `spec.addOns` in the YAML manifest of the cluster, you can edit it either by navigating to the cluster in the Console and using the `Edit & Apply` option for the cluster, or by applying the entire manifest using the `cpln apply >_` option in the upper right corner or by using the `cpln` [CLI](/cli-reference/overview). Add the following: ```yaml YAML theme={null} spec: addOns: headlamp: {} ``` #### Using UI 1. **Navigate to Control Plane Console**: Visit the [Control Plane Console](https://console.cpln.io/console/). 2. **Navigate to the Kubernetes cluster**: In the Control Plane Console, navigate to `Kubernetes` in the left sidebar panel and click on the Kubernetes cluster for which you want to enable the dashboard. 3. **Enable Headlamp**: Choose `Add-ons` and locate the `Headlamp` add-on from the list of available add-ons, then toggle it on. ## How to Access Once enabled, the Headlamp add-on can be used as follows: 1. **Navigate to Control Plane Console**: Visit the [Control Plane Console](https://console.cpln.io/console/). 2. **Access Headlamp**: In the Control Plane Console, navigate to `Kubernetes` in the left sidebar panel and click on `Open` under `Headlamp` for the cluster. # KubeVirt (VM Workloads) Source: https://docs.controlplane.com/mk8s/add-ons/kubevirt Enable VM workloads on a Managed Kubernetes (mk8s) cluster by installing KubeVirt and CDI, dedicating a node pool with hardware virtualization, and providing storage for boot disks. ## Overview The KubeVirt add-on lets an mk8s cluster run [VM workloads](/reference/workload/vm) (`spec.type: vm`). It installs the **KubeVirt** and **CDI** (Containerized Data Importer) operators and configures them for Control Plane: VM components are scoped to a dedicated VM node pool, the feature gates the platform needs are enabled, and boot-disk imports are wired through CDI. Once the add-on is enabled and at least one VM-capable node is present, the [location](/reference/location) reports `vm` in its capabilities and VM workloads can be scheduled there. ## Supported providers Any provider whose nodes can expose hardware virtualization (`/dev/kvm`) — that means **bare-metal nodes** or instances with **nested virtualization** enabled. On AWS, nested virtualization is available on 8th-generation Intel instance types (`c8i`, `m8i`, `r8i` and variants); bare-metal (`.metal`) instances also work. On [generic / BYOK](/mk8s/generic) clusters, use bare-metal hosts or VMs that pass through KVM. ## Requirements Setting up a cluster for VMs has four parts: VM nodes must be able to run KVM. On AWS, enable nested virtualization on a supported instance type via `cpuOptions.nestedVirtualization`; elsewhere use bare-metal / KVM-capable hosts. KubeVirt's components and the VMs themselves are scoped to nodes labelled `cpln.io/nodeType: vm`. Add that label to the node pool. A matching taint `cpln.io/nodeType=vm:NoSchedule` is recommended to keep ordinary pods off the VM nodes — KubeVirt's VM placement tolerates it. Add `kubevirt` to `addOns`. This installs the KubeVirt + CDI operators and their custom resources. VM boot disks are imported by CDI into a PVC (backed by a [volume set](/reference/volumeset)), so the cluster needs a working CSI / default `StorageClass`. If your default StorageClass is **block-mode**, set `scratchSpaceStorageClass` to a filesystem-mode class so CDI has scratch space for imports. The dedicated `cpln.io/nodeType: vm` node pool is what makes a node VM-capable. Without a labelled node, the KubeVirt components have nowhere to run and no VM workload can schedule. ## How to enable ### Cluster manifest (AWS example) A dedicated VM node pool with nested virtualization, the required label and taint, plus the add-on: ```yaml YAML theme={null} spec: provider: aws: # ...existing provider config... nodePools: - name: vm instanceTypes: - c8i.xlarge # 8th-gen Intel — supports nested virtualization cpuOptions: nestedVirtualization: true labels: cpln.io/nodeType: vm taints: - key: cpln.io/nodeType value: vm effect: NoSchedule minSize: 1 maxSize: 4 bootDiskSize: 100 subnetIds: - ${SUBNET_1} addOns: kubevirt: {} nodeLocalDns: {} # recommended (see DNS note below) ``` On **generic / BYOK** clusters, label and taint the node pool the same way and ensure the hosts expose `/dev/kvm`; nested-virtualization flags are provider-specific. ### Console When creating or editing the cluster, open **Add-ons**, toggle on **KubeVirt**, and make sure you have a node pool labelled `cpln.io/nodeType: vm` on VM-capable instances. ## Configuration | Option | Description | | :------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------ | | `scratchSpaceStorageClass` | Filesystem-mode `StorageClass` CDI uses for import scratch space. **Required when the cluster default StorageClass is block-mode**; otherwise optional. | ```yaml YAML theme={null} addOns: kubevirt: scratchSpaceStorageClass: my-filesystem-sc ``` The add-on also configures the KubeVirt CR automatically — the `DataVolumes`, `BlockVolume`, and `Sidecar` feature gates, and the VM node placement — so you don't set these yourself. ## DNS VM guests reach in-cluster services through a platform DNS forwarder. The [`nodeLocalDns`](/mk8s/add-ons/node_local_dns) add-on (a per-node CoreDNS cache) is recommended alongside KubeVirt for reliable VM DNS. It is not strictly required — VM cloud-init falls back to TCP DNS (`options use-vc`) — but enabling it avoids edge cases. ## Verify After the add-on reconciles and a VM node is ready: * The cluster's [location](/reference/location) reports `vm` under its workload-type capabilities. * A [VM workload](/reference/workload/vm) targeting this location schedules, imports its boot disk (watch `Importing boot disk: NN%` in the deployment status), and boots. ## Console access For day-to-day access, use the platform paths documented on the [VM workload](/reference/workload/vm) page — they work the same on your own cluster and require no `kubectl`: * **SSH (Linux):** `cpln workload connect` / `cpln workload exec`. * **RDP (Windows) or any TCP service:** [`cpln port-forward`](/guides/cli/cpln-port-forward) to the guest port, then connect to `localhost`. When a guest will not boot far enough to accept SSH or RDP — a kernel panic, a UEFI failure, or a stalled first boot — you need the raw graphical console. On a cluster you operate (mk8s or [BYOK](/mk8s/generic)) you have direct access, so use KubeVirt's [`virtctl`](https://kubevirt.io/user-guide/user_workloads/virtctl_client_tool/) against the cluster: VM instances are labelled with their workload name and run in the [GVC](/reference/gvc) namespace: ```bash theme={null} kubectl get vmi -A -l cpln/workload= ``` Point `virtctl` at the instance (use the `NAME` and `NAMESPACE` from the previous step). `virtctl` launches your local VNC viewer: ```bash theme={null} virtctl vnc -n --kubeconfig ``` If you have no local VNC client, run with `--proxy-only`; `virtctl` prints a local `127.0.0.1:` address you can point any VNC client at. `virtctl` must match the cluster's KubeVirt version, and your kubeconfig user needs access to the `virtualmachineinstances/vnc` subresource. The serial console (boot log, login banner) is also available as `virtctl console -n `. ## Troubleshooting **Symptom:** The workload deploys but stays not-ready, and `kubectl get vmi -A -l cpln/workload=` returns nothing — or the VM instance is stuck `Pending`/`Scheduling`. The [location](/reference/location) may not report `vm` in its capabilities. **Cause:** No VM-capable node, or KubeVirt's components have nowhere to run. VMs and the KubeVirt control plane are scoped to nodes labelled `cpln.io/nodeType: vm`. **Fix:** Confirm at least one ready, labelled node exists: ```bash theme={null} kubectl get nodes -l cpln.io/nodeType=vm ``` If the list is empty, add the label (and the matching taint) to a node pool on KVM-capable instances as shown in [How to enable](#how-to-enable), and verify the `kubevirt` add-on has reconciled. **Symptom:** The deployment status sits at `Importing boot disk: NN%` and never reaches running. **Cause:** CDI imports the boot disk into a PVC before the VM can boot. Imports stall when there is no working default `StorageClass`, or when the default StorageClass is **block-mode** and CDI has no filesystem-mode scratch space. **Fix:** Ensure a default `StorageClass` exists. If it is block-mode, set `scratchSpaceStorageClass` to a filesystem-mode class: ```yaml YAML theme={null} addOns: kubevirt: scratchSpaceStorageClass: my-filesystem-sc ``` Inspect the importer pod for the underlying error: ```bash theme={null} kubectl logs -n -l app=containerized-data-importer ``` **Symptom:** The VM never reaches the OS; the VNC/serial console shows a UEFI error such as `failed to load Boot0001 ... from PciRoot(0x0)`. **Cause:** Control Plane runs these VMs with non-persistent EFI NVRAM, so the firmware boots with an empty variable store. The image must place the Windows boot manager at the UEFI fallback path (`\EFI\BOOT\BOOTX64.EFI`); without it the firmware has nothing to boot. Enabling Secure Boot causes the same class of failure because it requires persistent NVRAM. **Fix:** Rebuild the Windows image with the fallback bootloader (see [Publishing & converting VM images](/guides/vm-images)). Do **not** set `firmware.secureBoot` — it is [not yet available](/reference/workload/vm#scaling--lifecycle) for this reason. **Symptom:** A VM (often Windows) reaches another workload's internal endpoint fine over HTTP, but a plain TCP protocol (SQL, RDP, custom TCP) connects only intermittently and otherwise resets. **Cause:** Istio's DNS proxy auto-allocates a virtual IP per ServiceEntry that can resolve to a non-serving endpoint; HTTP re-routes by `Host` header, raw TCP does not. **Fix:** Enable the [`MESH_DISABLE_IP_AUTOALLOCATE`](/byok/settings/actuator#mesh_disable_ip_autoallocate) actuator setting for that location, then reconcile the workload and re-establish the connection. **Symptom:** SSH or RDP to the VM times out from outside the cluster. **Cause:** A VM has no public endpoint by default; its `ports` only govern in-cluster service-mesh traffic. **Fix:** Reach the guest with [`cpln port-forward`](/guides/cli/cpln-port-forward) — it tunnels directly to a replica and the target port does **not** need to be listed in the workload's `ports`: ```bash theme={null} cpln port-forward 13389:3389 --gvc --location ``` Then connect your client to `localhost:13389`. For public HTTP access, attach a [domain](/reference/domain) instead. **Symptom:** Inside the guest, `*.cpln.local` names or cross-location peers fail to resolve. **Cause:** VM guests resolve in-cluster names through a platform DNS forwarder; large answers can be truncated over UDP on some guests. **Fix:** Enable the [`nodeLocalDns`](/mk8s/add-ons/node_local_dns) add-on (recommended alongside KubeVirt). On Linux guests you can also force TCP DNS by adding `options use-vc` to `/etc/resolv.conf` from [cloud-init](/reference/workload/vm#cloud-init--platform-injection). ## Next steps Configure and run a VM Build boot images Storage for boot and data disks Managed Kubernetes basics # Local Path Storage Source: https://docs.controlplane.com/mk8s/add-ons/local_path_storage Enable the Rancher Local Path Provisioner to automatically create persistent volumes backed by hostPath or local storage on cluster nodes. ## Overview The Local Path Storage add-on enables the [Rancher Local Path Provisioner](https://github.com/rancher/local-path-provisioner). This feature allows users to automatically create persistent volumes based on either `hostPath` or `local` storage on the node. Before using this feature, be aware that Local Volumes have their own limitations. It's important to familiarize yourself with `hostPath` and `local` volumes, including their purposes and limitations. For more information, refer to the [Kubernetes Documentation](https://kubernetes.io/docs/concepts/storage/volumes/). ## Supported Providers * All providers are supported ## How to Enable The Local Path Storage add-on can be enabled for your Kubernetes cluster either during the cluster creation process or at any time thereafter. The following sections outline the methods for enabling the add-on: ### At Cluster Creation * **Through Cluster Manifest**: Add the following snippet to your cluster manifest when creating the cluster: ```yaml YAML theme={null} spec: ... addOns: localPathStorage: {} ... ``` * **Using the Console**: If you're creating the cluster through the console, navigate to `Add-ons`, find the `Local Path Storage` add-on in the list of available add-ons, and toggle it on. ### After Cluster Creation If the Local Path Storage add-on was not enabled during the cluster creation, you can still enable it using either of the following methods: #### Using Manifest To enable the Local Path Storage add-on after cluster creation, add the following to your cluster's YAML manifest: * **Direct Edit & Apply**: Navigate to your cluster in the Console, and use the `Edit & Apply` option. * **CLI Application**: Apply the entire manifest using the `cpln apply >_` command or through the `cpln` [CLI](/cli-reference/overview). ```yaml YAML theme={null} spec: ... addOns: localPathStorage: {} ... ``` #### Using the UI 1. **Navigate to the Control Plane Console**: Open [Control Plane Console](https://console.cpln.io/console/). 2. **Select Your Kubernetes Cluster**: In the Control Plane Console, go to `Kubernetes` in the left sidebar, and click on the cluster you wish to configure. 3. **Activate the Dashboard**: Choose `Add-ons`, find the `Local Path Storage` add-on in the list, and toggle it on. ## Usage Instructions It is now possible to create Pods backed by `hostPath` and `local` volumes. ### Create a StatefulSet that uses a local volume for the storage of its pods Create the following StatefulSet in your Managed Kubernetes cluster. For guidance on accessing your cluster, refer to the documentation page of your provider. ```yaml YAML theme={null} --- apiVersion: v1 kind: Service metadata: name: nginx-localpath labels: app: nginx-localpath spec: ports: - port: 80 name: web type: LoadBalancer selector: app: nginx-localpath --- apiVersion: apps/v1 kind: StatefulSet metadata: name: nginx-localpath labels: app: nginx-localpath spec: serviceName: 'nginx-localpath' selector: matchLabels: app: nginx-localpath replicas: 1 template: metadata: labels: app: nginx-localpath spec: containers: - name: nginx-localpath image: registry.k8s.io/nginx-slim:0.8 ports: - containerPort: 80 name: web volumeMounts: - name: www-localpath mountPath: /usr/share/nginx/html volumeClaimTemplates: - metadata: name: www-localpath spec: accessModes: ['ReadWriteOnce'] storageClassName: local-path resources: requests: storage: 1Gi ``` # Logs Source: https://docs.controlplane.com/mk8s/add-ons/logs Send pod logs and Kubernetes audit records from your Managed Kubernetes cluster to Control Plane for centralized querying in the web UI or Grafana. ## Overview The Logs add-on, once enabled, will send logs from pods running on the cluster, and optionally, [Kubernetes audit records](https://kubernetes.io/docs/tasks/debug/debug-cluster/audit/), to Control Plane. This will allow you to query the logs and audit records from your clusters managed by the Control Plane centrally in the web UI or in Grafana. ## Supported Providers * [AWS](../aws) * [Hetzner](../hetzner) * [Generic](../generic) ## How to Enable The Logs add-on can be enabled for your Kubernetes cluster either during the cluster creation process or at any time thereafter. The following sections outline the methods for enabling the add-on: ### At Cluster Creation * **Through Cluster Manifest**: Add the following snippet to your cluster manifest when creating the cluster: ```yaml YAML theme={null} spec: ... addOns: logs: {} ... ``` * **Using the Console**: If you're creating the cluster through the console, navigate to `Add-ons`, find the `Logs` add-on in the list of available add-ons, and toggle it on. ### After Cluster Creation If the Logs add-on was not enabled during the cluster creation, you can still enable it using either of the following methods: #### Using Manifest Under `spec.addOns` in the YAML manifest of the cluster, you can edit it either by navigating to the cluster in the Console and using the `Edit & Apply` option for the cluster, or by applying the entire manifest using the `cpln apply >_` option in the upper right corner or by using the `cpln` [CLI](/cli-reference/overview). Add the following: ```yaml YAML theme={null} spec: --- addOns: logs: {} ``` #### Using UI 1. **Navigate to Control Plane Console**: Visit the [Control Plane Console](https://console.cpln.io/console/). 2. **Navigate to the Kubernetes cluster**: In the Control Plane Console, navigate to `Kubernetes` in the left sidebar panel and click on the Kubernetes cluster for which you want to enable logging. 3. **Enable Logs**: Choose `Add-ons` and locate the `Logs` add-on from the list of available add-ons, then toggle it on. ## Configuring Logs add-on The configuration is optional. You can configure the following: * List of namespaces to include for logging collection * List of namespaces to exclude for logging collection * Enable/Disable audit You can make the configuration in the UI for your cluster, as displayed in the picture below, or by modifying the clusters object spec. Logs add-on configuration UI showing namespace inclusion/exclusion filters and audit log toggle Alternatively, directly edit the clusters object spec: ```yaml YAML theme={null} spec: --- addOns: logs: auditEnabled: true excludeNamespaces: kube-system, istio-system includeNamespaces: default, fronted, app1 ``` ## Analyzing Logs Once enabled, you can analyze the logs and audit events. For LogQL query examples and Grafana integration, see the [Logs](/core/logs) documentation. 1. **Navigate to Control Plane Console**: Visit the [Control Plane Console](https://console.cpln.io/console/). 2. **Analyze logs**: In the Control Plane Console, navigate to `Logs` in the left sidebar panel.\ Use [LogQL](https://grafana.com/docs/loki/latest/query/) for query. Example query: `{cluster_name="aws-us-east-2", namespace="default"}` Control Plane Console logs view with LogQL query bar and filtered log entries for a Kubernetes cluster Click on `Explore On Grafana` and analyze logs in Grafana: Grafana Explore view showing Kubernetes cluster logs with time-series graph and log detail panel # Metrics Source: https://docs.controlplane.com/mk8s/add-ons/metrics Send Prometheus metrics from your Managed Kubernetes cluster to Control Plane for centralized monitoring in the web UI or Grafana. ## Overview The Metrics add-on, once enabled, will send metrics from to Control Plane. This will allow you to query the Prometheus metrics managed by the Control Plane centrally in the web UI or in Grafana. ## Supported Providers * All providers are supported ## How to Enable The Metrics add-on can be enabled for your Kubernetes cluster either during the cluster creation process or at any time thereafter. The following sections outline the methods for enabling the add-on: ### At Cluster Creation * **Through Cluster Manifest**: Add the following snippet to your cluster manifest when creating the cluster: ```yaml YAML theme={null} spec: ... addOns: metrics: {} ... ``` * **Using the Console**: If you're creating the cluster through the console, navigate to `Add-ons`, find the `Metrics` add-on in the list of available add-ons, and toggle it on. ### After Cluster Creation If the Metrics add-on was not enabled during the cluster creation, you can still enable it using either of the following methods: #### Using Manifest Under `spec.addOns` in the YAML manifest of the cluster, you can edit it either by navigating to the cluster in the Console and using the `Edit & Apply` option for the cluster, or by applying the entire manifest using the `cpln apply >_` option in the upper right corner or by using the `cpln` [CLI](/cli-reference/overview). Add the following: ```yaml theme={null} spec: addOns: metrics: {} ``` #### Using UI 1. **Navigate to Control Plane Console**: Visit the [Control Plane Console](https://console.cpln.io/console/). 2. **Navigate to the Kubernetes cluster**: In the Control Plane Console, navigate to `Kubernetes` in the left sidebar panel and click on the Kubernetes cluster for which you want to enable metrics collection. 3. **Enable Metrics**: Choose `Add-ons` and locate the `Metrics` add-on from the list of available add-ons, then toggle it on. ## Configuring Metrics add-on The configuration is optional though by default nothing is collected/scraped. Here is a summary of configuration parameters available for the add-on. ```yaml theme={null} ... spec: addOns: metrics: # enables metrics collection kubeState: false # collect kube-state metrics coreDns: false # collect metrics from CoreDNS service kubelet: false # collect low-level kublet metrics apiserver: false # collect API server-level metrics nodeExporter: false # deploy node-exporter and collect host-level metrics cadvisor: false # collect low-levelo container metrics from the kubelet scrapeAnnotated: # scrape any pod annotated with prometheus.io/scrape intervalSeconds: 30 # how often to do do includeNamespaces: # only in these namespaces excludeNamespaces: # exclude certain namespaces (applied after includeNamespaces) retainLabels: # pod labels to retain, by default retains all ``` ## Analyzing Metrics For every cluster created, a new Prometheus datasource will appear in Grafana. You can use PromQL to query metrics and build dashboards. # Node-Local DNS Source: https://docs.controlplane.com/mk8s/add-ons/node_local_dns Run a per-node DNS cache on a Managed Kubernetes (mk8s) cluster to lower DNS latency, reduce load on cluster DNS, and avoid UDP conntrack races. ## Overview The Node-Local DNS add-on runs a small DNS cache on every node in the cluster as a DaemonSet. Pods send their DNS queries to the local cache on the node instead of reaching the central cluster DNS service over the network, which: * **Lowers DNS latency** — most lookups are answered from the on-node cache. * **Reduces load on cluster DNS** — only cache misses are forwarded to the cluster DNS service. * **Avoids UDP `conntrack` races** — a well-known source of intermittent 5-second DNS timeouts in Kubernetes. The cache listens on the standard link-local address (`169.254.20.10`), caches in-cluster (`cluster.local`) and reverse-lookup zones, and forwards everything else to the cluster DNS service. It also exposes Prometheus metrics, which the [Metrics](/mk8s/add-ons/metrics) add-on collects when enabled. This add-on is **recommended alongside the [KubeVirt](/mk8s/add-ons/kubevirt) add-on**, where reliable in-cluster name resolution for VM guests matters most. ## Supported Providers * All providers are supported ## How to Enable The Node-Local DNS add-on can be enabled during cluster creation or at any time thereafter. It takes no configuration — enabling it is a simple toggle. ### At Cluster Creation * **Through Cluster Manifest**: Add the following snippet to your cluster manifest when creating the cluster: ```yaml YAML theme={null} spec: ... addOns: nodeLocalDns: {} ... ``` * **Using the Console**: If you're creating the cluster through the console, navigate to `Add-ons`, find the `Node-Local DNS` add-on in the list of available add-ons, and toggle it on. ### After Cluster Creation If the Node-Local DNS add-on was not enabled during cluster creation, you can still enable it using either of the following methods: #### Using Manifest To enable the Node-Local DNS add-on after cluster creation, add the following to your cluster's YAML manifest: * **Direct Edit & Apply**: Navigate to your cluster in the Console, and use the `Edit & Apply` option. * **CLI Application**: Apply the entire manifest using the `cpln apply >_` command or through the `cpln` [CLI](/cli-reference/overview). ```yaml YAML theme={null} spec: ... addOns: nodeLocalDns: {} ... ``` #### Using the UI 1. **Navigate to the Control Plane Console**: Open [Control Plane Console](https://console.cpln.io/console/). 2. **Select Your Kubernetes Cluster**: In the Control Plane Console, go to `Kubernetes` in the left sidebar, and click on the cluster you wish to configure. 3. **Enable the Add-on**: Choose `Add-ons`, find the `Node-Local DNS` add-on in the list, and toggle it on. Once the add-on reconciles, the per-node cache is rolled out as a DaemonSet across the cluster's nodes. No changes to your workloads are required — pods automatically use the local cache for DNS resolution. ## Next steps Recommended companion add-on for VM DNS Collect the cache's Prometheus metrics Managed Kubernetes basics # Registry Mirroring Source: https://docs.controlplane.com/mk8s/add-ons/registry_mirror Build a P2P network across cluster nodes to speed up container image pulls by sharing cached layers between nodes. ## Overview This addons build a P2P network from all the nodes on the cluster possibly speeding up image pulls - ideally an image will be pulled once per cluster and not once per node. There is a small increase in disk usage because layers are kept for longer in case they are needed on a different node. ## How to Enable The Registry mirror dashboard add-on can be enabled for your Kubernetes cluster either during the cluster creation process or at any time thereafter. The following sections outline the methods for enabling the add-on: ### At Cluster Creation * **Through Cluster Manifest**: Add the following snippet to your cluster manifest when creating the cluster: ```yaml theme={null} spec: addOns: registryMirror: {} ``` * **Using the Console**: If you're creating the cluster through the console, navigate to `Add-ons`, find the `Registry Mirror` add-on in the list of available add-ons, and toggle it on. ### After Cluster Creation If the Headlamp add-on was not enabled during the cluster creation, you can still enable it using either of the following methods: #### Using Manifest Under `spec.addOns` in the YAML manifest of the cluster, you can edit it either by navigating to the cluster in the Console and using the `Edit & Apply` option for the cluster, or by applying the entire manifest using the `cpln apply >_` option in the upper right corner or by using the `cpln` [CLI](/cli-reference/overview). Add the following: ```yaml YAML theme={null} spec: addOns: registryMirror: {} ``` If you are running a public registry mirror yourself, you can use like this: ```yaml theme={null} spec: addOns: registryMirror: - registry: docker.io mirrors: - https://my-mirror-1 - https://my-mirror-2 ``` By default (if no configuration is there) Control Plane uses only Google's Docker mirror (mirror.gcr.io) #### Using UI 1. **Navigate to Control Plane Console**: Visit the [Control Plane Console](https://console.cpln.io/console/). 2. **Navigate to the Kubernetes cluster**: In the Control Plane Console, navigate to `Kubernetes` in the left sidebar panel and click on the Kubernetes cluster for which you want to enable the dashboard. 3. **Enable Registry Mirror**: Choose `Add-ons` and locate the `Registry Mirror` add-on from the list of available add-ons, then toggle it on. ## How to Access Registry mirror has no endpoints - it is used internally by the kubelets. # Sysbox Source: https://docs.controlplane.com/mk8s/add-ons/sysbox Enable the Sysbox container runtime to improve container isolation and run system-level software like Docker, Kubernetes, and systemd inside containers. ## Overview Sysbox is an open-source and free container runtime (a specialized "runc"), originally developed by Nestybox (acquired by Docker on 05/2022), that enhances containers in two key ways: * Improves container isolation * Enables containers to run same workloads as VMs: With Sysbox, containers can run system-level software such as systemd, Docker, Kubernetes, K3s, buildx (including multi-arch builds), legacy apps, multi-arch apps, and more seamlessly & securely. This software can run inside Sysbox containers without modification and without using special versions of the software (e.g., rootless variants). No privileged containers, no complex images, no tricky entrypoints, no special volume mounts, etc. ## Resources * [Sysbox Github](https://github.com/nestybox/sysbox) # AWS Provider Source: https://docs.controlplane.com/mk8s/aws Configure and deploy Managed Kubernetes clusters on AWS with automated node scaling, load balancer provisioning, version upgrades, and storage management. The AWS Provider for Managed Kubernetes is designed to facilitate the setup of Kubernetes clusters on AWS Cloud. It automates key cloud component management, enabling easy and rapid creation and maintenance of scalable, production-grade Kubernetes clusters. This provider simplifies various cloud infrastructure tasks, including node autoscaling, load balancer provisioning, version upgrades, and storage management. It enables centralized management of a fleet of Kubernetes clusters across single or multiple AWS accounts. ## Requirements * [AWS Account](https://aws.amazon.com/console/) ## Supported add-ons * [Dashboard:](/mk8s/add-ons/dashboard) Provides a Kubernetes dashboard UI for the cluster. * [AWS Workload Identity:](/mk8s/add-ons/aws_workload_identity) Allows your pods to assume AWS IAM Roles. * [AWS ECR:](/mk8s/add-ons/aws_ecr) Allows pulling images from private ECR registries. * [AWS EFS:](/mk8s/add-ons/aws_efs) Provides support for persistent volumes using AWS Elastic File System. * [Local Path Storage:](/mk8s/add-ons/local_path_storage) Create PVCs backed by local volumes. * [Logs:](/mk8s/add-ons/logs) Enable logging for pods and cluster auditing. ## Step 1 - Preparing the AWS environment **Ensure your AWS environment includes:** * **A VPC with**: * At least one public subnet, or * At least one private subnet with a NAT gateway for egress traffic * [Guide on creating a VPC with AWS Console (External)](https://www.youtube.com/watch?v=ApGz8tpNLgo) Tailored instructions for IAM Role creation are available in the [Control Plane Console](https://console.cpln.io/console/) when creating an AWS Kubernetes cluster using the UI. Alternatively, you can follow the instructions below: * **IAM Role:** 1. Create an *IAM Role* with the naming pattern `cpln-mk8s-${ORG}`. For example, `cpln-mk8s-testing` if the organization name is `testing`. 2. Create an *IAM Policy* with the naming pattern `cpln-mk8s-${ORG}`. For example, `cpln-mk8s-testing` if the organization name is `testing`. ```json Control Plane Connector Policy theme={null} { "Version": "2012-10-17", "Statement": [ { "Sid": "CreateIamResourcesForCplnPrefix", "Effect": "Allow", "Action": [ "iam:Get*", "iam:CreateRole", "iam:DeleteRole", "iam:PassRole", "iam:CreateInstanceProfile", "iam:DeleteInstanceProfile", "iam:AddRoleToInstanceProfile", "iam:RemoveRoleFromInstanceProfile", "iam:CreatePolicy", "iam:DeletePolicy", "iam:CreatePolicyVersion", "iam:DeletePolicyVersion", "iam:AttachRolePolicy", "iam:DetachRolePolicy", "iam:UpdateRoleDescription", "iam:UpdateAssumeRolePolicy", "sts:AssumeRole" ], "Resource": [ "arn:aws:iam::*:instance-profile/cpln-*", "arn:aws:iam::*:policy/cpln-*", "arn:aws:iam::*:role/cpln-*" ] }, { "Sid": "ValidateConfiguration", "Effect": "Allow", "Action": [ "kms:ListKeys", "kms:DescribeKey", "iam:ListPolicies", "iam:ListPolicyVersions", "iam:ListRolePolicies", "ec2:DescribeKeyPairs", "ec2:DescribeSubnets", "ec2:DescribeVpcs", "ec2:DescribeSecurityGroups", "iam:ListAttachedRolePolicies", "iam:ListInstanceProfilesForRole", "iam:SimulatePrincipalPolicy" ], "Resource": "*" }, { "Sid": "PrepareCluster", "Effect": "Allow", "Action": [ "autoscaling:CreateAutoScalingGroup", "ec2:CreateLaunchTemplate", "ec2:ModifyLaunchTemplate", "ec2:CreateSecurityGroup", "ec2:CreateLaunchTemplate", "ec2:CreateTags", "autoscaling:CreateOrUpdateTags", "ec2:RunInstances", "ec2:DescribeLaunchTemplates", "ec2:DescribeLaunchTemplateVersions", "ec2:DescribeImages", "autoscaling:UpdateAutoScalingGroup", "autoscaling:StartInstanceRefresh", "ec2:CreateLaunchTemplateVersion", "ec2:AuthorizeSecurityGroupIngress", "ec2:AuthorizeSecurityGroupEgress", "ec2:DescribeSecurityGroups" ], "Resource": "*" }, { "Sid": "Cleanup", "Effect": "Allow", "Action": [ "autoscaling:DeleteAutoScalingGroup", "autoscaling:DescribeAutoScalingGroups", "autoscaling:DeleteLaunchConfiguration", "ec2:DeleteLaunchTemplate", "ec2:DeleteSecurityGroup", "ec2:DeleteVolume", "ec2:DescribeLaunchTemplates", "ec2:DescribeSecurityGroups", "ec2:DescribeVolumes", "elasticloadbalancing:DeleteLoadBalancer", "elasticloadbalancing:DescribeLoadBalancers", "elasticloadbalancing:DescribeTags" ], "Resource": "*" }, { "Sid": "AmiLookupInSSM", "Effect": "Allow", "Action": [ "ssm:GetParameters", "ssm:DescribeParameters", "ssm:GetParameter" ], "Resource": [ "arn:aws:ssm:*::parameter/aws/service/ami-amazon-linux-latest/*", "arn:aws:ssm:*::parameter/aws/service/canonical/*", "arn:aws:ssm:*::parameter/aws/service/eks/*" ] } ] } ``` 3. Attach the connector policy to the `cpln-mk8s-${ORG}` *IAM Role*. 4. Edit the trust policy of the `cpln-mk8s-${ORG}` *IAM Role*. **Important:** Replace `${ACCOUNT_ID}` with your own account id. ```json Trust Policy theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Federated": "arn:aws:iam::${ACCOUNT_ID}:oidc-provider/oidc.mk8s.cpln.io/federate" }, "Action": "sts:AssumeRoleWithWebIdentity", "Condition": { "StringEquals": { "oidc.mk8s.cpln.io/federate:aud": "sts.amazonaws.com", "oidc.mk8s.cpln.io/federate:sub": "observer-org" } } } ] } ``` 5. Save the updates and copy the ARN of the *IAM Role* for use in the next step. ## Step 2 - Create a Managed Kubernetes Cluster Using a Manifest File 1. **Update the manifest below**: Modify the following `aws-mk8s-template.yaml` YAML manifest below with actual values. Customize the file as needed. Replace placeholders for `${ROLE_ARN}`, `${SSH_KEY_NAME}`, `${SUBNET_1}` and `${SUBNET_2}` and `${VPC_ID}`. Use the Role ARN of the IAM Role created in the previous step in place of `${ROLE_ARN}`. ```yaml YAML theme={null} kind: mk8s name: aws-mk8s-example description: aws-mk8s-example cluster tags: {} spec: provider: aws: deployRoleArn: ${ROLE_ARN} image: recommended: ubuntu/jammy-22.04 # keyPair: ${SSH_KEY_NAME} # Optional nodePools: - name: general bootDiskSize: 25 extraSecurityGroupIds: [] instanceTypes: - m7g.large - m6gd.large - c7g.xlarge labels: {} maxSize: 4 minSize: 1 onDemandBaseCapacity: 0 onDemandPercentageAboveBaseCapacity: 0 # overrideImage: # recommended: amazon/amzn2 spotAllocationStrategy: lowest-price subnetIds: - ${SUBNET_1} - ${SUBNET_2} taints: [] region: eu-central-1 securityGroupIds: [] skipCreateRoles: false vpcId: ${VPC_ID} addOns: awsECR: {} dashboard: {} localPathStorage: {} awsWorkloadIdentity: {} firewall: - description: default sourceCIDR: 0.0.0.0/0 version: 1.30.3 ``` This example creates a managed Kubernetes cluster in AWS with the following configurations: * **[Add-ons](#supported-add-ons)**: Includes Dashboard, Local Path Storage, AWS Workload Identity and awsECR. * **Location**: The cluster's Kubernetes control plane is managed in the `eu-central-1` region. Placing worker nodes close to the control plane is recommended for optimal performance. Full list of supported regions: `[af-south-1, ap-east-1, ap-northeast-1, ap-northeast-2, ap-northeast-3, ap-south-1, ap-south-2, ap-southeast-1, ap-southeast-2, ap-southeast-3, ap-southeast-4, ca-central-1, eu-central-1, eu-central-2, eu-north-1, eu-south-1, eu-south-2, eu-west-1, eu-west-2, eu-west-3, me-central-1, me-south-1, sa-east-1, us-east-1, us-east-2, us-west-1, us-west-2]`. * **Kubernetes API Firewall**: Utilizes the `Default` rule, allowing public access to the Kubernetes API. It is advisable to restrict API access to a known IP range for security purposes. * **Kubernetes Version**: 1.30.3. * **Node Pool**: A single `general` node pool, scaling on-demand between 1 and 4 nodes. * **Server Image**: ubuntu/jammy-22.04. 2. **Create the Cluster**: Deploy the `aws-mk8s-example` cluster by applying the manifest. * **Console**: Apply the `aws-mk8s-template.yaml` file using the `cpln apply >_` option in the upper right corner. * **CLI**: Execute `cpln apply -f aws-mk8s-template.yaml --org YOUR_ORG_HERE`. **Wait until the cluster is initialized.** ## Step 3 - Accessing the Cluster ### 1. Using the Terminal 1. **Obtain the Cluster's Kubeconfig File**: Execute the command `cpln mk8s kubeconfig aws-mk8s-example -f /tmp/aws-mk8s-example-conf`. 2. **Access the Cluster with `kubectl`**: Use the obtained kubeconfig file by running `export KUBECONFIG=/tmp/aws-mk8s-example-conf` for the current shell session. ### 2. Using Kubernetes Dashboard 1. **Navigate to Control Plane Console**: Visit the [Control Plane Console](https://console.cpln.io/console/). 2. **Access the Dashboard**: In the Control Plane Console, navigate to `Kubernetes` in the left sidebar panel and click on `Open` under `Dashboard` for the cluster `aws-mk8s-example`. ## Advanced Configuration Options ### Deploy Role Chain AWS accounts with high security requirements may not allow a direct trust policy to allow direct access from Control Plane for setup and management. In this situation, you can configure the initial login from Control Plane into a different intermediary account and add a chain of roles that will be assumed to reach the target account. ### `.spec.provider.aws.deployRoleChain` Each record in the `deployRoleChain` array is assumed **after** the initial login into the `deployRole`. The chain of roles is processed in the order listed in the array, from top to bottom. Each step in the chain must have a trust policy in place to allow `sts:assumeRole` from the previous step. | Property | Type | Description | | ------------------- | ----------------- | -------------------------------------------------------------------------------------------------- | | `roleArn` | string | The role that will be assumed using `sts:assumeRole`. | | `externalId` | string (optional) | The externalId to use during the assume role. | | `sessionNamePrefix` | string (optional) | A prefix to use for the session name. The rest of the session will be generated to make it unique. | ```json example trust policy theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::333377778888:role/ExampleTrustedRole" }, "Action": "sts:AssumeRole", "Condition": { "StringEquals": { "sts:ExternalId": "ExampleExternalId" } } } ] } ``` ### Instance rotation/refresh Sometimes an explicit rotation (or refresh) of the instances in the Auto-Scaling Group is needed. For example to get the latest image or new boot disk size. To do this per node pool, add a label `cpln.io/refreshToken` with an arbitrary valid value. Every time the value of this label changes Control Plane will start a conservative instance replacement (1 instance at a time). If you remove that label it's the same as having an empty value and that will trigger refresh too. # GCP Provider Source: https://docs.controlplane.com/mk8s/gcp Deploy Managed Kubernetes worker nodes in your GCP project. Requires only an existing network and subnet as prerequisites. The GCP provider lets you deploy worker nodes in your GCP project. As a prerequisite, only a network and a subnet need to exist. ## Requirements * [GCP Project](https://docs.cloud.google.com/resource-manager/docs/creating-managing-projects) ## Supported add-ons * [Dashboard:](/mk8s/add-ons/dashboard) Provides a Kubernetes dashboard UI for the cluster. * [Headlamp:](/mk8s/add-ons/headlamp) The recommended dashboard for the cluster * [AWS Workload Identity:](/mk8s/add-ons/aws_workload_identity) Allows your pods to assume AWS IAM Roles. * [AWS ECR:](/mk8s/add-ons/aws_ecr) Allows pulling images from private ECR registries. * [Local Path Storage:](/mk8s/add-ons/local_path_storage) Create PVCs backed by local volumes. * [Logs:](/mk8s/add-ons/logs) Enable logging for pods and cluster auditing. * [Metrics:](/mk8s/add-ons/metrics) Enable metrics collections. ## Step 1 - Preparing the GCP project **Ensure your GCP project has:** * **A VPC with a subnet in the desired region** * **Service account withe these roles** | Role | Description | | --------------------------------- | ------------------------------------------- | | `roles/compute.instanceAdmin.v1` | Instances, instance groups, templates | | `roles/compute.loadBalancerAdmin` | Forwarding rules, target pools, addresses | | `roles/compute.storageAdmin` | Disks and snapshots | | `roles/compute.securityAdmin` | Firewall rules | | `roles/compute.networkViewer` | Networks/subnets discovery (for validation) | Create a JSON key for the service account and store it in Control Plane as a secret of type `GCP`. Permissions can further be restricted using expressions and limiting the scope of the role. Yet Control Plane must be able ## Step 2 - Create a Managed Kubernetes Cluster Using a Manifest File 1. **Update the manifest below**: Modify the following `gcp-mk8s-template.yaml` YAML manifest below with actual values. Customize the file as needed. Replace placeholders for `${PROJECT_ID}`, `${NETWORK}`, `${SUBNET}`, `${SECRET}` and `${ZONE}`. ```yaml theme={null} kind: mk8s name: ${NAME} spec: provider: gcp: projectId: ${PROJECT_ID} region: ${REGION} image: recommended: ubuntu/noble-24.04 # COS linux not yet supported saKeyLink: //secret/${SECRET} # points to the secret with the SA JSON key network: mk8s labels: # labels to attach to created GCP resources my-google-label: x123 metadata: # metadata to attach to created instances my-google-meta1: hello world tags: # tags to attach to created instances - my-google-tag1 - my-google-tag2 nodePools: - name: general bootDiskSize: 30 machineType: ${MACHINE_TYPE} localPersistentDisks: 1 minSize: 1 maxSize: 2 subnet: ${SUBNET} zone: ${ZONE} addOns: headlamp: {} version: 1.32.9 ``` This example creates a managed Kubernetes cluster in your project with the following configurations: * **Kubernetes Version**: 1.32.9. * **[Add-ons](#supported-add-ons)**: Only the headlamp add-on is enabled * **Node Pool**: A single `general` node pool, scaling on-demand between 1 and 2 nodes. * **Server Image**: ubuntu/noble-24.04. 2. **Create the Cluster**: Deploy the `gcp-mk8s-example` cluster by applying the manifest. * **Console**: Apply the `gcp-mk8s-template.yaml` file using the `cpln apply >_` option in the upper right corner. * **CLI**: Execute `cpln apply -f gcp-mk8s-template.yaml --org YOUR_ORG_HERE`. **Wait until the cluster is initialized.** ## Step 3 - Accessing the Cluster ### 1. Using the Terminal 1. **Obtain the Cluster's Kubeconfig File**: Execute the command `cpln mk8s kubeconfig gcp-mk8s-example -f /tmp/gcp-mk8s-example-conf`. 2. **Access the Cluster with `kubectl`**: Use the obtained kubeconfig file by running `export KUBECONFIG=/tmp/gcp-mk8s-example-conf` for the current shell session. ### 2. Using Kubernetes Dashboard 1. **Navigate to Control Plane Console**: Visit the [Control Plane Console](https://console.cpln.io/console/). 2. **Access the Dashboard**: In the Control Plane Console, navigate to `Kubernetes` in the left sidebar panel and click on `Open` under `Headlamp` for the cluster `gcp-mk8s-example`. ## Advanced Configuration Options ### Instance rotation/refresh Sometimes an explicit rotation (or refresh) of the instances in the Auto-Scaling Group is needed. For example to get the latest image or new boot disk size. To do this per node pool, add a label `cpln.io/refreshToken` with an arbitrary valid value. Every time the value of this label changes Control Plane will start a conservative instance replacement (1 instance at a time). If you remove that label it's the same as having an empty value and that will trigger refresh too. # Generic Provider Source: https://docs.controlplane.com/mk8s/generic Convert any Linux-based server into a Managed Kubernetes worker node, whether deployed in public clouds, private clouds, or on-premises environments. The Generic provider for Managed Kubernetes allows Linux-based servers to act as worker nodes of a Kubernetes cluster, irrespective of their deployment in public clouds (such as AWS, Azure, GCP, Hetzner, etc.) or in private cloud or on-premises environments. Once integrated, these servers join a Kubernetes cluster as worker nodes, enabling the deployment of Kubernetes workloads through the Kubernetes API. ## Supported Add-Ons * [Headlamp](/mk8s/add-ons/headlamp): Provides a Kubernetes dashboard UI for the cluster. * [AWS Workload Identity](/mk8s/add-ons/aws_workload_identity): Allows your pods to assume AWS IAM Roles. * [AWS ECR](/mk8s/add-ons/aws_ecr): Allows pulling images from private ECR registries. * [Local Path Storage](/mk8s/add-ons/local_path_storage): Creates PVCs backed by local volumes. * [Logs](/mk8s/add-ons/logs): Enables logging for pods and cluster auditing. ## Step 1 - Server Setup 1. **Prepare the servers**: Create at least one server that meets the following server requirements: * Server requirements: * Linux kernel version 5.x.x or higher * Minimum CPU: 1 core * Minimum RAM: 512 MB * Servers require internet egress access. * Ensure you have SSH access to the servers. ## Step 2 - Create a Managed Kubernetes Cluster with a Manifest 1. **Optionally update the manifest below and save it as `generic-mk8s-template.yaml`.** ```yaml YAML theme={null} kind: mk8s name: generic-mk8s-example description: generic-mk8s-example mk8s in gcp-us-east1 tags: {} spec: provider: generic: location: gcp-us-east1 nodePools: - name: general labels: environment: generic-mk8s-example taints: [] addOns: headlamp: {} localPathStorage: {} awsWorkloadIdentity: {} firewall: - description: default sourceCIDR: 0.0.0.0/0 version: 1.35.3 ``` This example creates a generic cluster with the following configurations: * **[Add-ons](#supported-add-ons)**: Includes Headlamp, Local Path Storage, and AWS Workload Identity. * **Location**: The cluster's Kubernetes control plane will be managed in the `gcp-us-east1` location. Placing worker nodes close to the control plane is recommended for optimal performance. * **Kubernetes API Firewall**: Uses the `default` rule, allowing public access to the Kubernetes API. It is advisable to restrict API access to a known IP range for security purposes. * **Kubernetes Version**: 1.35.3. * **Node Pool**: A single `general` node pool. 2. **Create the Cluster**: Deploy the `generic-mk8s-example` cluster by applying the manifest. * **CLI**: Execute `cpln apply -f generic-mk8s-template.yaml --org YOUR_ORG_HERE`. * **Console**: Apply the `generic-mk8s-template.yaml` file using the `>_ apply` option in the upper right corner. **Wait until the cluster is initialized.** ## Step 3 - Connect the Servers 1. **Obtain the Join Script**: * **Using the CLI**: a. **Save the Join Script to a file**: Execute the command `cpln mk8s join generic-mk8s-example --type join-script --options nodePoolName=general > join_general.sh`. * **Using the UI**: a. **Navigate to the Control Plane Console**: Visit the [Control Plane Console](https://console.cpln.io/console/). b. **Access the Dashboard**: In the Control Plane Console, navigate to `Kubernetes` in the left sidebar panel, click on the `generic-mk8s-example` cluster, and go to `Node Pools`. Click on `More` on the right side of the `general` Node Pool and select `Generate Join Script`. 2. **Copy and Execute the Script on the Server**: Connect to the remote server and execute the join script. After the execution, the server should be joined to the cluster as a node under the `general` node pool. Repeat these steps for all necessary servers. ## Step 4 - Accessing the Cluster ### 1. Using the Terminal 1. **Obtain the cluster’s kubeconfig file**: Execute the command `cpln mk8s kubeconfig generic-mk8s-example -f /tmp/generic-mk8s-example-conf`. 2. **Access the cluster with `kubectl`**: Use the obtained kubeconfig file by running `export KUBECONFIG=/tmp/generic-mk8s-example-conf` for the current shell session. ### 2. Using Headlamp 1. **Navigate to the Control Plane Console**: Visit the [Control Plane Console](https://console.cpln.io/console/). 2. **Open Headlamp**: In the Control Plane Console, navigate to `Kubernetes` in the left sidebar panel and click on `Headlamp` under `Dashboard` for the cluster `generic-mk8s-example`. # Hetzner Provider Source: https://docs.controlplane.com/mk8s/hetzner Set up Managed Kubernetes clusters on Hetzner Cloud with automated infrastructure management and cost-effective pricing compared to major hyperscalers. The Hetzner Provider for Managed Kubernetes utilizes Hetzner Cloud to set up Kubernetes clusters. This provider automates the management of essential cloud components on Hetzner, enabling the creation of scalable and production-ready Kubernetes clusters. With its competitive pricing, Hetzner is an advantageous choice for users seeking to optimize cloud expenses compared to larger hyperscalers such as AWS, Azure, and GCP. ## Requirements * [Hetzner Cloud](https://www.hetzner.com/) account and [hcloud CLI](https://github.com/hetznercloud/cli) ## Supported add-ons * [Dashboard:](/mk8s/add-ons/dashboard) Provides a Kubernetes dashboard UI for the cluster. * [AWS Workload Identity:](/mk8s/add-ons/aws_workload_identity) Allows your pods to assume AWS IAM Roles. * [AWS ECR:](/mk8s/add-ons/aws_ecr) Allows pulling images from private ECR registries. * [Local Path Storage:](/mk8s/add-ons/local_path_storage) Create PVCs backed by local volumes. * [Logs:](/mk8s/add-ons/logs) Enable logging for pods and cluster auditing. ## Step 1 - Preparing the Hetzner Cloud Environment 1. **Log in to Hetzner Cloud Account**: Access your [Hetzner Cloud account](https://accounts.hetzner.com/login). 2. **Create a New Project**: Establish a new project named `hetzner-mk8s-example`. 3. **Create an API Token**: * Navigate to your newly created project. * Click on `Security` in the left sidebar, then proceed to `API tokens`. * Click on `Generate API Token`. * Grant `Read & Write` permissions to the token and copy it. 4. **Store the Token in Control Plane Secrets Manager**: * Log in to the [Control Plane Console](https://console.cpln.io/console/). * Click on `Secrets` in the left sidebar and then select `New`. * Choose `Opaque` as the secret type and name it `hetzner-mk8s-example`. * Store the API token's value in this secret. 5. **Create Network and Security Features in Project `hetzner-mk8s-example` on Hetzner**: * **Network (Required)**: * Click on `Networks` in the left sidebar. * Create a new network by selecting `Create Network`. The network zone should match the location you will use for your servers. For the Locations table, see the [Hetzner Cloud Locations documentation](https://docs.hetzner.com/cloud/general/locations/). * **Firewall (Recommended)**: * Click on `Firewalls` in the left sidebar. * Create a new firewall by selecting `Create Firewall`. * **SSH Key (Recommended)**: * Click on `Security` in the left sidebar. * Go to `SSH Keys` and click on `Add SSH Key`. The steps above can be automated using the API or CLI tools of [Hetzner](https://docs.hetzner.com/cloud/) and [Control Plane](/cli-reference/overview). ## Step 2 - Create a Managed Kubernetes Cluster Using a Manifest File 1. **Connect with hcloud CLI**: Use the command `hcloud context create hetzner-mk8s-example` and insert the `API key` from the previous step. 2. **Capture the IDs**: Obtain the ID of the `network`, and optionally the `firewall` and the `ssh-key` created in the previous step using the [hcloud CLI](https://github.com/hetznercloud/cli). * Network: Use `hcloud network list`. * Firewall: Use `hcloud firewall list`. * SSH key: Use `hcloud ssh-key list`. 3. **Update the manifest below**: Modify the following `hetzner-mk8s-example.yaml` YAML manifest with actual values from above. Customize the file as needed. Replace the placeholders for `firewallId`, `networkId`, and `sshKey`. ```yaml YAML theme={null} kind: mk8s name: hetzner-mk8s-example description: An example of Hetzner mk8s tags: {} spec: provider: hetzner: dedicatedServerNodePools: [] firewallId: '1111111' image: ubuntu-22.04 networkId: '2222222' nodePools: - name: general labels: environment: hetzner-mk8s-example maxSize: 4 minSize: 1 serverType: cpx31 taints: [] region: hel1 sshKey: '333333' tokenSecretLink: //secret/hetzner-mk8s-example addOns: dashboard: {} localPathStorage: {} awsWorkloadIdentity: {} firewall: - description: Default sourceCIDR: 0.0.0.0/0 version: 1.28.2 ``` This example creates a Managed Kubernetes cluster in Hetzner Cloud with the following configurations: * **[Add-ons](#supported-add-ons)**: Includes Dashboard, Local Path Storage, and AWS Workload Identity. * **Location**: The cluster's Kubernetes control plane will be managed in the `gcp-us-east1` location. Placing worker nodes close to the control plane is recommended for optimal performance. * **Kubernetes API Firewall**: Utilizes the `Default` rule, allowing public access to the Kubernetes API. It is advisable to restrict API access to a known IP range for security purposes. * **Kubernetes Version**: 1.28.2. * **Node Pool**: A single `general` node pool, scaling on-demand between 1 and 4 nodes. * **Server Image**: Ubuntu 22.04. 4. **Create the Cluster**: Deploy the `hetzner-mk8s-example` cluster by applying the manifest. * **Console**: Apply the `hetzner-mk8s-example.yaml` file using the `cpln apply >_` option in the upper right corner. * **CLI**: Execute `cpln apply -f hetzner-mk8s-example.yaml --org YOUR_ORG_HERE`. **Wait until the cluster is initialized.** ## Step 3 - Accessing the Cluster ### 1. Using the Terminal 1. **Obtain the Cluster's Kubeconfig File**: Execute the command `cpln mk8s kubeconfig hetzner-mk8s-example -f /tmp/hetzner-mk8s-example-conf`. 2. **Access the Cluster with `kubectl`**: Use the obtained kubeconfig file by running `export KUBECONFIG=/tmp/hetzner-mk8s-example-conf` for the current shell session. ### 2. Using Kubernetes Dashboard 1. **Navigate to Control Plane Console**: Visit the [Control Plane Console](https://console.cpln.io/console/). 2. **Access the Dashboard**: In the Control Plane Console, navigate to `Kubernetes` in the left sidebar panel and click on `Open` under `Dashboard` for the cluster `hetzner-mk8s-example`. # Managed Kubernetes Source: https://docs.controlplane.com/mk8s/overview Overview of Control Plane's Managed Kubernetes service for creating and managing Kubernetes clusters across multiple cloud platforms and private cloud environments. Control Plane's Managed Kubernetes service is designed to simplify the creation and management of Kubernetes clusters, including upgrades and scaling, across multiple cloud platforms and private cloud environments. By offering a unified view for Kubernetes cluster management, this service enables the centralized management of numerous clusters on various cloud platforms, private clouds, and across accounts. Managed Kubernetes handles the tasks required to maintain a healthy Kubernetes cluster. In addition, it manages autoscaling according to user preferences, optimizes costs by using spot instances when enabled, and allows seamless upgrades based on the user's selected version. If you already have Kubernetes clusters and want to integrate them with Control Plane rather than creating new managed clusters, see [CPLN Platform (BYOK)](/byok/overview). ## CNCF Certified Managed Kubernetes is a CNCF-certified product. [Learn more](https://www.cncf.io/training/certification/software-conformance/). ### Conformance Tests * [1.32](https://github.com/cncf/k8s-conformance/pull/3610) * [1.33](https://github.com/cncf/k8s-conformance/pull/4010) * [1.34](https://github.com/cncf/k8s-conformance/pull/4011) CNCF Certified ## Requirements In order to use Managed Kubernetes, make sure these requirements are met: * Linux OS. * A recent kernel version (> 5.4). * Internet access (for mk8s to download components). * Member nodes of the cluster must be able to communicate with each other (same VPC, same L2 network, etc.). Contact support if you want to run mk8s in an air-gapped environment or have a hard dependency on a particular Linux flavor or kernel. ## Supported Kubernetes versions * 1.35.3 * 1.34.2 * 1.33.5 * 1.32.9 * 1.31.13 * 1.30.3 * 1.29.7 * 1.28.4 * 1.27.3 * 1.26.4 ## Infrastructure Providers Click a provider name for details. 1. [Generic](/mk8s/generic) 2. [AWS](/mk8s/aws) 3. [GCP](/mk8s/gcp) 4. [Hetzner](/mk8s/hetzner) 5. [Triton](/mk8s/triton) Also available *(documentation coming soon)*: 1. Azure 2. Linode 3. DigitalOcean 4. Lambda Contact support if you are an infrastructure provider and would like to see a native integration with your product or service. ## Add-Ons The following optional features can be enabled or disabled. * [AWS ECR](/mk8s/add-ons/aws_ecr): Allows pulling images from private ECR (Elastic Container Registry). * [AWS EFS](/mk8s/add-ons/aws_efs): Provides support for persistent volumes using AWS Elastic File System. * [AWS Workload Identity](/mk8s/add-ons/aws_workload_identity): Allows your pods to assume AWS IAM Roles. * [Azure ACR](/mk8s/add-ons/azure_acr): Allows pulling images from private ACR (Azure Container Registry). * [Azure Workload Identity](/mk8s/add-ons/azure_workload_identity): Allows your pods to impersonate Azure Service Principals. * [Headlamp](/mk8s/add-ons/headlamp): Provides a Kubernetes dashboard based on open-source [Headlamp](https://github.com/kubernetes-sigs/headlamp). * [Local Path Storage](/mk8s/add-ons/local_path_storage): Creates PVCs backed by local volumes. * [Logs](/mk8s/add-ons/logs): Enables logging for pods and cluster auditing. * [Metrics](/mk8s/add-ons/metrics): Collects metrics and ships them to Control Plane for long-term storage. * [Registry Mirroring](/mk8s/add-ons/registry_mirror): Builds a P2P network from the nodes to speed up image pulls. Based on the Spegel OSS project. * [Sysbox](/mk8s/add-ons/sysbox): Deploys the [Sysbox](https://github.com/nestybox/sysbox) project, improving container isolation. * [Kubernetes Dashboard (Deprecated)](/mk8s/add-ons/dashboard): Provides the standard Kubernetes dashboard UI for the cluster. # Triton Provider Source: https://docs.controlplane.com/mk8s/triton The Triton Provider for Managed Kubernetes utilizes Triton Private Cloud to provision Kubernetes clusters. Because native load balancers are not supported, you must configure a managed load balancer instance during the cluster creation process. Logs are captured via [Syslog](/external-logging/syslog). ## Requirements * A Triton account with API access * An OpenSSH private key as a Control Plane opaque secret * A private network configured in your datacenter * `triton-moirai` images available in your datacenter ## Supported Add-ons * [Dashboard:](/mk8s/add-ons/dashboard) Provides a Kubernetes dashboard UI for the cluster. * [Headlamp:](/mk8s/add-ons/headlamp) The recommended dashboard for the cluster * [Local Path Storage:](/mk8s/add-ons/local_path_storage) Create PVCs backed by local volumes. * [Logs:](/mk8s/add-ons/logs) Enable logging for pods and cluster auditing. * [Metrics:](/mk8s/add-ons/metrics) Enable metrics collections. ## Load Balancing Triton does not include a native load balancer. To expose cluster endpoints, you must configure a manual load balancer while configuring the cluster, and Control Plane deploys and configures the instance in your datacenter. The load balancer acts as a gateway and it must be attached to the `publicNetworkId` to receive traffic and the `privateNetworkIds` to reach the worker nodes. `triton-moirai` images must already be built and available in your Triton datacenter before creating the cluster. ## Logging When using a load balancer, logs are written to syslog. Choose one of the following options: * **`externalSyslog`** — Forward the log sink to a syslog server outside the cluster, specified as `host:port`. * **`nodePort`** — If you run a syslog service inside the cluster and expose it via a Kubernetes NodePort, point the log sink to that port number (valid range: `30000`–`65535`). ## Step 1 — Prepare Your Triton Environment Your Triton environment must include: 1. **`Datacenter capacity`**: Verify that your datacenter has enough capacity for the number of nodes you're planning to run. 2. **`Private network`**: Set up at least one private network for backend traffic between worker nodes. 3. **`Instance types`**: Make sure the instance types you need are available for both the node pool and the load balancer. 4. **`triton-moirai images`**: Have your triton-moirai machine images ready and accessible in your datacenter. ## Step 2 — Create a Managed Kubernetes Cluster Using a Manifest File 1. **Update the manifest below**: Modify the following `triton-mk8s-template.yaml` YAML manifest with actual values. Replace the \$ values in the YAML below with your actual values before applying. ```yaml YAML theme={null} kind: mk8s name: ${NAME} spec: provider: triton: connection: account: ${ACCOUNT} privateKeySecretLink: //secret/${SECRET} url: ${URL} imageId: ${IMAGE_ID} loadBalancer: manual: tags: my-key-sample: my-value-sample cnsInternalDomain: ${CNS_INTERNAL_DOMAIN} cnsPublicDomain: ${CNS_PUBLIC_DOMAIN} count: 1 imageId: ${LB_IMAGE_ID} metadata: my-meta-key: my-meta-value packageId: ${LB_PACKAGE_ID} privateNetworkIds: - ${LB_PRIVATE_NETWORK_ID} publicNetworkId: ${LB_PUBLIC_NETWORK_ID} location: ${LOCATION} networking: podNetwork: ${POD_NETWORK_CIDR} serviceNetwork: ${SERVICE_NETWORK_CIDR} nodePools: # At least one node pool is required. - name: private labels: cpln.io/nodeType: core internet: false maxSize: 4 minSize: 1 packageId: ${PRIVATE_PACKAGE_ID} tritonTags: hello: world privateNetworkId: ${PRIVATE_NETWORK_ID} addOns: headlamp: {} firewall: - description: public access for testing purposes sourceCIDR: 0.0.0.0/0 version: 1.34.2 ``` 2. **Create the Cluster**: Deploy the cluster by applying the manifest. * **Console**: Apply the `triton-mk8s-template.yaml` file using the `cpln apply >_` option in the upper right corner. * **CLI**: Execute `cpln apply -f triton-mk8s-template.yaml --org YOUR_ORG_HERE`. **Wait until the cluster is initialized.** ## Step 3 — Accessing the Cluster ### 1. Using the Terminal 1. **Obtain the Cluster's Kubeconfig File**: Execute the command `cpln mk8s kubeconfig ${NAME} -f /tmp/triton-mk8s-conf`. 2. **Access the Cluster with `kubectl`**: Use the obtained kubeconfig file by running `export KUBECONFIG=/tmp/triton-mk8s-conf` for the current shell session. ### 2. Using Headlamp 1. **Navigate to Control Plane Console**: Visit the [Control Plane Console](https://console.cpln.io/console/). 2. **Access Headlamp**: In the Control Plane Console, navigate to `Kubernetes` in the left sidebar panel and click on `Open` under `Headlamp` for the cluster. ## Advanced Configuration Options ### Instance Rotation/Refresh Sometimes an explicit rotation (or refresh) of the instances is needed, for example to get the latest image or a new boot disk size. To do this per node pool, add a label `cpln.io/refreshToken` with an arbitrary valid value. Every time the value of this label changes, a conservative instance replacement will start (1 instance at a time). Removing that label is the same as having an empty value and will trigger a refresh too. # 1. Deploy Your First Workload Source: https://docs.controlplane.com/quickstart/quick-start-1-deploy-workload Create a GVC, deploy a sample web application as a workload, and access it via a globally load-balanced TLS endpoint. ## Overview This quickstart guides you through deploying your first application on Control Plane. You'll create a Global Virtual Cloud ([GVC](/reference/gvc)), deploy a [Workload](/reference/workload), and access it via a globally load-balanced endpoint. **What you'll accomplish:** * Create a GVC spanning multiple cloud providers and locations. * Deploy a sample web application as a workload. * Access your application via a TLS-secured, geo-routed global endpoint. ## Prerequisites * [Sign up](https://console.cpln.io/signup) for Control Plane and create a [billing account](/concepts/billing). * An existing [org](/reference/org), or permission to [create an org](/guides/create-org). ## Step 1: Log in to the Console 1. Navigate to [console.cpln.io](https://console.cpln.io). 2. Select your SSO provider and authenticate. 3. You'll be directed to your organization's dashboard. ## Step 2: Create a GVC A [GVC](/reference/gvc) defines where your workloads run across cloud providers and regions. Click the `Create` dropdown in the upper right corner and select `GVC`. Enter a name for your GVC (e.g., `quickstart-gvc`). Click `Locations`, then click `Add Location`. Choose the locations where your workload will be deployed. For this quickstart, select: * `aws-us-west-2` * `gcp-us-east1` This deploys your application across both AWS and GCP. Click `OK`. Click `Create`. You can click `Set as Current Context` to make this GVC your current context. This allows you to browse its workloads, identities, and volumesets from the sidebar. ## Step 3: Create a Workload A [workload](/reference/workload) is your application running on Control Plane. Click `Workloads` in the left menu, then click `New`. Enter a name (e.g., `hello-world`). Make sure the GVC dropdown under the description field has `quickstart-gvc` selected. Click `Containers`. For the image source, select `External`, then enter: ```text theme={null} gcr.io/knative-samples/helloworld-go ``` Under `Ports`, ensure there is a port configured with: * Protocol: `http` * Number: `8080` Click `Firewall`, then click the `Make Public` button at the top to allow external access. Click `Create`. The workload summary page opens. ## Step 4: Access Your Application After 1-2 minutes, the workload health shows `Ready`. In the summary, under `Endpoints`, click the link next to `Canonical Endpoint`. Your application opens in a new window, served from the location nearest to you. Click `Deployments` in the left pane to see each location's endpoint. Click `Open` next to any location to access that specific deployment. ## Prerequisites * [Sign up](https://console.cpln.io/signup) for Control Plane and create a [billing account](/concepts/billing). * An existing [org](/reference/org), or permission to [create an org](/guides/create-org). ## Step 1: Install the CLI Choose your preferred installation method: ```bash theme={null} npm install -g @controlplane/cli ``` Requires [Node.js](https://nodejs.org/en/download/) version 18+ (an active LTS release is recommended). For other installation options, see [Installation](/cli-reference/installation). ```bash theme={null} brew tap controlplane-com/cpln && brew install cpln ``` For other installation options, see [Installation](/cli-reference/installation). See the [Installation page](/cli-reference/installation#binary) to download the binary for your platform. ## Step 2: Verify the Installation Confirm the CLI is installed and accessible: ```bash theme={null} cpln --version ``` You should see the CLI version number. ## Step 3: Authenticate Log in to Control Plane: ```bash theme={null} cpln login ``` The CLI prints a login URL and a six-digit confirmation code, then waits. Open the URL in a browser on any device, sign in, and enter the code. Once the CLI confirms the login, you can close the browser. Set your default organization: ```bash theme={null} cpln profile update default --org YOUR_ORG_NAME ``` ## Step 4: Create a GVC Create a GVC with locations in AWS and GCP: ```bash theme={null} cpln gvc create --name quickstart-gvc \ --location aws-us-west-2 \ --location gcp-us-east1 ``` Set it as your default GVC: ```bash theme={null} cpln profile update default --gvc quickstart-gvc ``` ## Step 5: Create and deploy a workload Create a public workload with the sample application: ```bash theme={null} cpln workload create --name hello-world \ --image gcr.io/knative-samples/helloworld-go \ --port 8080 \ --public ``` ## Step 6: Access Your Application Wait for the workload to become ready: ```bash theme={null} cpln workload get hello-world ``` Once the `READY` column shows `true`, open the application: ```bash theme={null} cpln workload open hello-world ``` You can also view the workload's global endpoint URL in the output of `cpln workload get`. ### View Deployment Status Check deployments across all locations: ```bash theme={null} cpln workload get-deployments hello-world ``` ## Prerequisites * [Sign up](https://console.cpln.io/signup) for Control Plane and create a [billing account](/concepts/billing). * An existing [org](/reference/org), or permission to [create an org](/guides/create-org). * [Terraform](https://www.terraform.io/downloads.html) installed. ## Step 1: Authenticate The Terraform provider needs credentials to interact with Control Plane. Choose one of the following methods: If you have the [Control Plane CLI](/cli-reference/installation) installed, log in first: ```bash theme={null} cpln login ``` This creates a profile named `default` with your credentials. You'll reference this profile in the provider configuration. For CI/CD pipelines or environments without the CLI, create a service account token: 1. In the [Console](https://console.cpln.io), click `Service Accounts` in the left menu, then `New`. 2. Enter a name, select `superusers` from the `Assign to Group` dropdown, and click `Create`. 3. Click `Keys`, add a description, and click `Add`. 4. **Copy the generated key**. It won't be shown again. Set the token as an environment variable: ```bash theme={null} export CPLN_TOKEN=your-service-account-key ``` ## Step 2: Configure the Provider Create a new directory and add `main.tf` with the provider configuration: ```hcl theme={null} terraform { required_providers { cpln = { source = "controlplane-com/cpln" } } } provider "cpln" { org = "YOUR_ORG_NAME" profile = "default" } ``` ```hcl theme={null} terraform { required_providers { cpln = { source = "controlplane-com/cpln" } } } provider "cpln" { org = "YOUR_ORG_NAME" # Token is read from CPLN_TOKEN environment variable } ``` Initialize Terraform: ```bash theme={null} terraform init ``` For more authentication options, see [Terraform Provider](/iac/terraform). ## Step 3: Define the GVC Add the GVC resource to `main.tf`: ```hcl theme={null} resource "cpln_gvc" "quickstart" { name = "quickstart-gvc" description = "Quickstart GVC" locations = [ "aws-us-west-2", "gcp-us-east1" ] } ``` ## Step 4: Define the workload Add the workload resource to `main.tf`: ```hcl theme={null} resource "cpln_workload" "hello_world" { gvc = cpln_gvc.quickstart.name name = "hello-world" type = "standard" container { name = "helloworld-go" image = "gcr.io/knative-samples/helloworld-go" cpu = "50m" memory = "128Mi" ports { number = 8080 protocol = "http" } } options { capacity_ai = true timeout_seconds = 5 autoscaling { metric = "disabled" target = 95 min_scale = 1 max_scale = 1 } } firewall_spec { external { inbound_allow_cidr = ["0.0.0.0/0"] } } } output "canonical_endpoint" { value = cpln_workload.hello_world.status[0].canonical_endpoint } ``` ## Step 5: Deploy Review the plan: ```bash theme={null} terraform plan ``` Apply the configuration: ```bash theme={null} terraform apply ``` Type `yes` when prompted. After deployment completes, the canonical endpoint URL is displayed. ## Step 6: Access Your Application Open the canonical endpoint URL in your browser. To view it again: ```bash theme={null} terraform output ``` View the full list of workload configuration options in the [Terraform Registry documentation](https://registry.terraform.io/providers/controlplane-com/cpln/latest/docs/resources/workload). ## Prerequisites * [Sign up](https://console.cpln.io/signup) for Control Plane and create a [billing account](/concepts/billing). * An existing [org](/reference/org), or permission to [create an org](/guides/create-org). * [Pulumi CLI](https://www.pulumi.com/docs/iac/download-install/) installed. ## Step 1: Authenticate The Pulumi provider needs credentials to interact with Control Plane. Choose one of the following methods: If you have the [Control Plane CLI](/cli-reference/installation) installed, log in first: ```bash theme={null} cpln login ``` This creates a profile named `default` with your credentials. For CI/CD pipelines or environments without the CLI, create a service account token: 1. In the [Console](https://console.cpln.io), click `Service Accounts` in the left menu, then `New`. 2. Enter a name, select `superusers` from the `Assign to Group` dropdown, and click `Create`. 3. Click `Keys`, add a description, and click `Add`. 4. **Copy the generated key**. It won't be shown again. Set the token as an environment variable: ```bash theme={null} export CPLN_TOKEN=your-service-account-key ``` ## Step 2: Create a New Project Create a new directory and initialize a Pulumi project: ```bash theme={null} mkdir cpln-quickstart && cd cpln-quickstart pulumi new typescript ``` Follow the prompts in the terminal to complete project setup. Then install the Control Plane provider: ```bash theme={null} npm install @pulumiverse/cpln ``` ```bash theme={null} pnpm add @pulumiverse/cpln ``` ```bash theme={null} yarn add @pulumiverse/cpln ``` ```bash theme={null} bun add @pulumiverse/cpln ``` ```bash theme={null} mkdir cpln-quickstart && cd cpln-quickstart pulumi new python ``` Follow the prompts in the terminal to complete project setup. Activate the virtual environment and install the Control Plane provider: ```bash theme={null} source venv/bin/activate pip install pulumiverse-cpln ``` ```bash theme={null} poetry add pulumiverse-cpln ``` ```bash theme={null} source .venv/bin/activate uv add pulumiverse-cpln ``` ```bash theme={null} mkdir cpln-quickstart && cd cpln-quickstart pulumi new go ``` Follow the prompts in the terminal to complete project setup. Then install the Control Plane provider: ```bash theme={null} go get github.com/pulumiverse/pulumi-cpln/sdk/go/cpln ``` ```bash theme={null} mkdir cpln-quickstart && cd cpln-quickstart pulumi new csharp ``` Follow the prompts in the terminal to complete project setup. Then install the Control Plane provider: ```bash theme={null} dotnet add package Pulumiverse.Cpln --version 1.0.0 ``` ## Step 3: Configure the Provider Set your organization and authentication method: ```bash theme={null} pulumi config set cpln:org YOUR_ORG_NAME pulumi config set cpln:profile default ``` ```bash theme={null} pulumi config set cpln:org YOUR_ORG_NAME pulumi config set --secret cpln:token $CPLN_TOKEN ``` Or paste the token directly: ```bash theme={null} pulumi config set cpln:org YOUR_ORG_NAME pulumi config set --secret cpln:token your-service-account-key ``` ## Step 4: Define the Infrastructure Replace `index.ts` with: ```typescript theme={null} import * as cpln from "@pulumiverse/cpln"; // Create a GVC const gvc = new cpln.Gvc("quickstart-gvc", { name: "quickstart-gvc", description: "Quickstart GVC", locations: ["aws-us-west-2", "gcp-us-east1"], }); // Create a Workload const workload = new cpln.Workload("hello-world", { gvc: gvc.name, name: "hello-world", type: "standard", containers: [ { name: "helloworld-go", image: "gcr.io/knative-samples/helloworld-go", cpu: "50m", memory: "128Mi", ports: [ { number: 8080, protocol: "http", }, ], }, ], options: { capacityAi: true, timeoutSeconds: 5, autoscaling: { metric: "disabled", target: 95, minScale: 1, maxScale: 1, }, }, firewallSpec: { external: { inboundAllowCidrs: ["0.0.0.0/0"], }, }, }); // Export the canonical endpoint export const canonical_endpoint = workload.statuses.apply( (s) => s?.[0]?.canonicalEndpoint ); ``` Replace `__main__.py` with: ```python theme={null} import pulumiverse_cpln as cpln import pulumi # Create a GVC gvc = cpln.Gvc("quickstart-gvc", name="quickstart-gvc", description="Quickstart GVC", locations=["aws-us-west-2", "gcp-us-east1"]) # Create a Workload workload = cpln.Workload("hello-world", gvc=gvc.name, name="hello-world", type="standard", containers=[cpln.WorkloadContainerArgs( name="helloworld-go", image="gcr.io/knative-samples/helloworld-go", cpu="50m", memory="128Mi", ports=[cpln.WorkloadContainerPortArgs( number=8080, protocol="http", )], )], options=cpln.WorkloadOptionsArgs( capacity_ai=True, timeout_seconds=5, autoscaling=cpln.WorkloadOptionsAutoscalingArgs( metric="disabled", target=95, min_scale=1, max_scale=1, ), ), firewall_spec=cpln.WorkloadFirewallSpecArgs( external=cpln.WorkloadFirewallSpecExternalArgs( inbound_allow_cidrs=["0.0.0.0/0"], ), )) # Export the canonical endpoint pulumi.export("canonical_endpoint", workload.statuses.apply( lambda s: s[0].canonical_endpoint if s else None )) ``` Replace `main.go` with: ```go theme={null} package main import ( "github.com/pulumi/pulumi/sdk/v3/go/pulumi" "github.com/pulumiverse/pulumi-cpln/sdk/go/cpln" ) func main() { pulumi.Run(func(ctx *pulumi.Context) error { // Create a GVC gvc, err := cpln.NewGvc(ctx, "quickstart-gvc", &cpln.GvcArgs{ Name: pulumi.String("quickstart-gvc"), Description: pulumi.String("Quickstart GVC"), Locations: pulumi.StringArray{ pulumi.String("aws-us-west-2"), pulumi.String("gcp-us-east1"), }, }) if err != nil { return err } // Create a Workload workload, err := cpln.NewWorkload(ctx, "hello-world", &cpln.WorkloadArgs{ Gvc: gvc.Name, Name: pulumi.String("hello-world"), Type: pulumi.String("standard"), Containers: cpln.WorkloadContainerArray{ &cpln.WorkloadContainerArgs{ Name: pulumi.String("helloworld-go"), Image: pulumi.String("gcr.io/knative-samples/helloworld-go"), Cpu: pulumi.String("50m"), Memory: pulumi.String("128Mi"), Ports: cpln.WorkloadContainerPortArray{ &cpln.WorkloadContainerPortArgs{ Number: pulumi.Int(8080), Protocol: pulumi.String("http"), }, }, }, }, Options: &cpln.WorkloadOptionsArgs{ CapacityAi: pulumi.Bool(true), TimeoutSeconds: pulumi.Int(5), Autoscaling: &cpln.WorkloadOptionsAutoscalingArgs{ Metric: pulumi.String("disabled"), Target: pulumi.Int(95), MinScale: pulumi.Int(1), MaxScale: pulumi.Int(1), }, }, FirewallSpec: &cpln.WorkloadFirewallSpecArgs{ External: &cpln.WorkloadFirewallSpecExternalArgs{ InboundAllowCidrs: pulumi.StringArray{pulumi.String("0.0.0.0/0")}, }, }, }) if err != nil { return err } // Export the canonical endpoint ctx.Export("canonical_endpoint", workload.Statuses.Index(pulumi.Int(0)).CanonicalEndpoint()) return nil }) } ``` Replace `Program.cs` with: ```csharp theme={null} using Pulumi; using Pulumiverse.Cpln; using Pulumiverse.Cpln.Inputs; using System.Collections.Generic; return await Deployment.RunAsync(() => { // Create a GVC var gvc = new Gvc("quickstart-gvc", new GvcArgs { Name = "quickstart-gvc", Description = "Quickstart GVC", Locations = new[] { "aws-us-west-2", "gcp-us-east1" } }); // Create a Workload var workload = new Workload("hello-world", new WorkloadArgs { Gvc = gvc.Name, Name = "hello-world", Type = "standard", Containers = new[] { new WorkloadContainerArgs { Name = "helloworld-go", Image = "gcr.io/knative-samples/helloworld-go", Cpu = "50m", Memory = "128Mi", Ports = new[] { new WorkloadContainerPortArgs { Number = 8080, Protocol = "http" } } } }, Options = new WorkloadOptionsArgs { CapacityAi = true, TimeoutSeconds = 5, Autoscaling = new WorkloadOptionsAutoscalingArgs { Metric = "disabled", Target = 95, MinScale = 1, MaxScale = 1 } }, FirewallSpec = new WorkloadFirewallSpecArgs { External = new WorkloadFirewallSpecExternalArgs { InboundAllowCidrs = new[] { "0.0.0.0/0" } } } }); // Export the canonical endpoint return new Dictionary { ["canonical_endpoint"] = workload.Statuses.Apply(s => s[0].CanonicalEndpoint) }; }); ``` ## Step 5: Deploy Preview the changes: ```bash theme={null} pulumi preview ``` Deploy the infrastructure: ```bash theme={null} pulumi up ``` Select `yes` when prompted. After deployment, the canonical endpoint URL is displayed. ## Step 6: Access Your Application Open the canonical endpoint URL in your browser. To view it again: ```bash theme={null} pulumi stack output ``` If the canonical endpoint doesn't appear, run `pulumi up` again to refresh the state. View the full list of resources in the [Pulumi Registry documentation](https://www.pulumi.com/registry/packages/cpln/). ## What You Deployed Your application is now: * **Globally distributed** across AWS and GCP. * **Automatically load balanced** with geo-routing to the nearest healthy location. * **TLS secured** with automatic certificate management. * Running in each selected location with one replica per location. ```text aws-us-west-2 theme={null} Hello World! Provider: aws Location: /org/YOUR_ORG/location/aws-us-west-2 ``` ```text gcp-us-east1 theme={null} Hello World! Provider: gcp Location: /org/YOUR_ORG/location/gcp-us-east1 ``` ## Continue Build and deploy your own containerized application to Control Plane. Install the **Control Plane AI Plugin** for Claude Code, Codex, or Antigravity CLI. It knows the workload types, secret access chain, firewall defaults, and deploy waits — so you can describe what you want instead of writing manifests. ## Clean Up If you want to stop here instead, delete the resources created in this quickstart: Navigate to your GVC and click `Delete` from the `Actions` pulldown menu. This removes the GVC and all associated workloads. ```bash theme={null} cpln gvc delete quickstart-gvc ``` ```bash theme={null} terraform destroy ``` ```bash theme={null} pulumi destroy ``` # 2. Deploy Your Own Application Source: https://docs.controlplane.com/quickstart/quick-start-2-deploy-application Containerize your own application, push it to the Control Plane private image registry, and deploy it as a workload. ## Overview This quickstart guides you through building and deploying your own application to Control Plane. You'll containerize a sample Node.js application, push it to your org's private image registry on Control Plane, and deploy it as a workload. **What you'll accomplish:** * Install the Control Plane CLI * Containerize a sample application using Buildpacks * Push the image to your organization's private registry * Deploy the application as a workload ## Prerequisites * Completed [Quickstart 1](/quickstart/quick-start-1-deploy-workload) (or have an existing [org](/guides/create-org) with a GVC) * [Docker](https://www.docker.com/get-started) installed and running ## Step 1: Install the CLI The CLI is required to build and push images to your org's private registry in Control Plane. ```bash theme={null} npm install -g @controlplane/cli ``` Requires [Node.js](https://nodejs.org/en/download/) version 18+ (an active LTS release is recommended). ```bash theme={null} brew tap controlplane-com/cpln && brew install cpln ``` See the [Installation page](/cli-reference/installation#binary) to download the binary for your platform. Verify the installation: ```bash theme={null} cpln --version ``` ## Step 2: Authenticate Log in to Control Plane: ```bash theme={null} cpln login ``` The CLI prints a login URL and a six-digit confirmation code. Open the URL in a browser on any device, sign in, and enter the code. After a successful login, set your default organization: ```bash theme={null} cpln profile update default --org YOUR_ORG_NAME ``` ## Step 3: Download the Sample Application Download and extract the sample Node.js application: Sample Application Sample Application Extract the archive and navigate to the directory: ```bash theme={null} tar -xvf cpln_app.tgz && cd cpln_app ``` ```powershell theme={null} Expand-Archive cpln_app.zip && cd cpln_app ``` The application is a web app that displays runtime environment variables, including Control Plane-injected variables such as cloud provider and location. ## Step 4: Build and Push the Image Build and push the image to Control Plane's private registry: ```bash theme={null} cpln image build --name my-app:1.0 --push ``` The `cpln image build` command uses [Buildpacks](https://buildpacks.io) to automatically detect your application type and build the image. No Dockerfile is required. If your application has an existing Dockerfile, the command uses that instead. This build runs through Docker on your machine. Without Docker, replace `--push` with `--remote` to build remotely; the image is pushed for you. The image is now available in your organization's private registry as `//image/my-app:1.0`. ## Step 5: Create the Workload Click `Workloads` in the left menu, then click `New`. 1. Enter the name `my-app` 2. Select `quickstart-gvc` from the GVC dropdown 1. Click `Containers` in the left pane 2. Select `Control Plane` as the image source 3. In the select image dropdown, type `my-app` and select `my-app:1.0` 4. Under `Ports`, set Protocol to `http` and Number to `8080` 1. Click `Firewall` in the left pane 2. Click `Make Public` 3. Click `Create` ## Step 6: Access Your Application The workload status changes to show `Ready` under Health after 1-2 minutes. Under `Endpoints`, click the link next to `Canonical Endpoint`. Your application displays the Control Plane environment variables, including the location and provider. ## Step 5: Create the Workload If you completed Quickstart 1, use the existing GVC: ```bash theme={null} cpln workload create --name my-app \ --image //image/my-app:1.0 \ --gvc quickstart-gvc \ --port 8080 \ --public ``` The `//image/` prefix tells Control Plane to pull the image from your organization's private registry. ## Step 6: Access Your Application Wait for the workload to become ready: ```bash theme={null} cpln workload get my-app --gvc quickstart-gvc ``` Once ready, open the application: ```bash theme={null} cpln workload open my-app --gvc quickstart-gvc ``` ## Step 5: Define the Workload Add to your existing `main.tf` from Quickstart 1: ```hcl theme={null} resource "cpln_workload" "my_app" { gvc = cpln_gvc.quickstart.name name = "my-app" type = "standard" container { name = "main" image = "/org/YOUR_ORG_NAME/image/my-app:1.0" cpu = "50m" memory = "128Mi" ports { protocol = "http" number = 8080 } } options { capacity_ai = true timeout_seconds = 5 autoscaling { metric = "disabled" target = 95 min_scale = 1 max_scale = 1 } } firewall_spec { external { inbound_allow_cidr = ["0.0.0.0/0"] } } } output "my_app_endpoint" { value = cpln_workload.my_app.status[0].canonical_endpoint } ``` Replace `YOUR_ORG_NAME` with your organization name. The full image path is required for Terraform. Apply the configuration: ```bash theme={null} terraform apply ``` ## Step 6: Access Your Application Open the endpoint URL from the Terraform output: ```bash theme={null} terraform output ``` ## Step 5: Define the Workload Add to your existing Pulumi project from Quickstart 1: ```typescript theme={null} // My App workload const myApp = new cpln.Workload("my-app", { gvc: gvc.name, name: "my-app", type: "standard", containers: [ { name: "main", image: "/org/YOUR_ORG_NAME/image/my-app:1.0", cpu: "50m", memory: "128Mi", ports: [{ protocol: "http", number: 8080 }], }, ], options: { capacityAi: true, timeoutSeconds: 5, autoscaling: { metric: "disabled", target: 95, minScale: 1, maxScale: 1, }, }, firewallSpec: { external: { inboundAllowCidrs: ["0.0.0.0/0"], }, }, }); export const myAppEndpoint = myApp.statuses.apply( (s) => s?.[0]?.canonicalEndpoint ?? "pending" ); ``` ```python theme={null} # My App workload my_app = cpln.Workload("my-app", gvc=gvc.name, name="my-app", type="standard", containers=[cpln.WorkloadContainerArgs( name="main", image="/org/YOUR_ORG_NAME/image/my-app:1.0", cpu="50m", memory="128Mi", ports=[cpln.WorkloadContainerPortArgs( protocol="http", number=8080, )], )], options=cpln.WorkloadOptionsArgs( capacity_ai=True, timeout_seconds=5, autoscaling=cpln.WorkloadOptionsAutoscalingArgs( metric="disabled", target=95, min_scale=1, max_scale=1, ), ), firewall_spec=cpln.WorkloadFirewallSpecArgs( external=cpln.WorkloadFirewallSpecExternalArgs( inbound_allow_cidrs=["0.0.0.0/0"], ), )) pulumi.export("my_app_endpoint", my_app.statuses[0].canonical_endpoint) ``` ```go theme={null} // My App workload myApp, err := cpln.NewWorkload(ctx, "my-app", &cpln.WorkloadArgs{ Gvc: gvc.Name, Name: pulumi.String("my-app"), Type: pulumi.String("standard"), Containers: cpln.WorkloadContainerArray{ &cpln.WorkloadContainerArgs{ Name: pulumi.String("main"), Image: pulumi.String("/org/YOUR_ORG_NAME/image/my-app:1.0"), Cpu: pulumi.String("50m"), Memory: pulumi.String("128Mi"), Ports: cpln.WorkloadContainerPortArray{ &cpln.WorkloadContainerPortArgs{ Protocol: pulumi.String("http"), Number: pulumi.Int(8080), }, }, }, }, Options: &cpln.WorkloadOptionsArgs{ CapacityAi: pulumi.Bool(true), TimeoutSeconds: pulumi.Int(5), Autoscaling: &cpln.WorkloadOptionsAutoscalingArgs{ Metric: pulumi.String("disabled"), Target: pulumi.Int(95), MinScale: pulumi.Int(1), MaxScale: pulumi.Int(1), }, }, FirewallSpec: &cpln.WorkloadFirewallSpecArgs{ External: &cpln.WorkloadFirewallSpecExternalArgs{ InboundAllowCidrs: pulumi.StringArray{pulumi.String("0.0.0.0/0")}, }, }, }) if err != nil { return err } ctx.Export("my_app_endpoint", myApp.Statuses.Index(pulumi.Int(0)).CanonicalEndpoint()) ``` ```csharp theme={null} // My App workload var myApp = new Workload("my-app", new WorkloadArgs { Gvc = gvc.Name, Name = "my-app", Type = "standard", Containers = new[] { new WorkloadContainerArgs { Name = "main", Image = "/org/YOUR_ORG_NAME/image/my-app:1.0", Cpu = "50m", Memory = "128Mi", Ports = new[] { new WorkloadContainerPortArgs { Protocol = "http", Number = 8080 } } } }, Options = new WorkloadOptionsArgs { CapacityAi = true, TimeoutSeconds = 5, Autoscaling = new WorkloadOptionsAutoscalingArgs { Metric = "disabled", Target = 95, MinScale = 1, MaxScale = 1 } }, FirewallSpec = new WorkloadFirewallSpecArgs { External = new WorkloadFirewallSpecExternalArgs { InboundAllowCidrs = new[] { "0.0.0.0/0" } } } }); ``` Add to your return dictionary: ```csharp theme={null} ["my_app_endpoint"] = myApp.Statuses.Apply(s => s[0].CanonicalEndpoint) ``` Replace `YOUR_ORG_NAME` with your organization name. The full image path is required for Pulumi. Deploy the configuration: ```bash theme={null} pulumi up ``` ## Step 6: Access Your Application Open the endpoint URL from the Pulumi output: ```bash theme={null} pulumi stack output ``` ## Updating Your Application To deploy a new version, update your code and build with a new image tag: ```bash theme={null} cpln image build --name my-app:1.1 --push ``` Then update your workload to use the new image: Navigate to your workload, click `Containers`, select the new tag, and click `Update`. ```bash theme={null} cpln workload update my-app --gvc quickstart-gvc --set spec.containers.my-app.image=//image/my-app:1.1 ``` Update the `image` attribute in your Terraform configuration and run `terraform apply`. Update the `image` property in your Pulumi code and run `pulumi up`. ## What You've Learned * **Private registry** stores your images securely within your organization * **Image references** use `//image/` prefix for CLI or full path for IaC tools * **Buildpacks** automatically containerize your application without a Dockerfile * **Environment variables** like `CPLN_LOCATION` and `CPLN_PROVIDER` are injected automatically ## Continue Map your own domain to your workload for production-ready URLs. ## Clean Up To delete just the `my-app` workload created in this quickstart: Navigate to `Workloads`, select `my-app`, and from `Actions` click `Delete`. ```bash theme={null} cpln workload delete my-app --gvc quickstart-gvc ``` Remove the `cpln_workload.my_app` resource from your `main.tf` and run: ```bash theme={null} terraform apply ``` Remove the `my-app` workload from your code and run: ```bash theme={null} pulumi up ``` To delete images, navigate to `Images` in the Console, select the image name, and from `Actions` click `Delete` to remove all tags. With the CLI, delete each tag individually: `cpln image delete my-app:1.0`. # 3. Configure a Custom Domain Source: https://docs.controlplane.com/quickstart/quick-start-3-custom-domain Map your own domain to a workload with automatic TLS certificates, path-based routing, and geo-routing. ## Overview This quickstart guides you through configuring a custom domain for your workload. You'll map your own domain to your application with automatic TLS certificates, load balancing, and geo-routing. **What you'll accomplish:** * Register and verify your apex domain * Configure path-based routing to your workload * Access your application via your custom domain ## Prerequisites * Completed [Quickstart 1](/quickstart/quick-start-1-deploy-workload) with a running [workload](/guides/create-workload) * A domain name you own with access to its DNS settings This quickstart uses `example.com` as an example domain. Replace it with your own domain throughout the guide. So far, you've created a GVC (`quickstart-gvc`) and deployed a workload (`hello-world`). Now you'll configure a custom domain that routes traffic to your application. ## Step 1: Register Your Apex Domain An apex domain (e.g., `example.com`) must be verified before creating subdomains. Even if you only use subdomains, verify the apex first. Click `Domains` in the left menu, then click `New`. Click `Advanced`, then enter your apex domain (e.g., `example.com`). You'll be prompted to verify ownership by adding a TXT record to your DNS. Add the displayed TXT record to your DNS provider and wait a few minutes for propagation. Scroll down to `Routing Mode` and select `None` because you're only verifying ownership of the apex domain. Click `Create`. Create the apex domain in your production org as a best practice. ## Step 2: Configure Your Subdomain Now configure a subdomain (e.g., `app.example.com`) to route to your workload. Click `Domains` > `New`. Enter your subdomain (e.g., `app.example.com`). Select the `hello-world` workload from the `quickstart-gvc` GVC. Add the displayed CNAME record to your DNS provider. Once the DNS record is configured, click `Create`. Control Plane automatically provisions TLS certificates after the DNS propagates and your workload is ready. This may take a few minutes. ## Step 3: Test Your Domain Open `https://app.example.com` in your browser. Your application loads with a valid TLS certificate. ## Step 1: Register Your Apex Domain First, attempt to create the apex domain: ```bash theme={null} cpln domain create --name example.com ``` The command returns an error with the TXT record you need to add to verify ownership. Add one of the displayed TXT records to your DNS provider and wait a few minutes for propagation, then run the command again: ```bash theme={null} cpln domain create --name example.com ``` Once ownership is verified, the domain is created successfully. ## Step 2: Create Domain Configuration Create a YAML file `domain.yaml` with your domain configuration: ```yaml theme={null} kind: domain name: app.example.com spec: dnsMode: cname ports: - number: 443 protocol: http2 routes: - prefix: / workloadLink: //gvc/quickstart-gvc/workload/hello-world ``` Apply the configuration: ```bash theme={null} cpln apply -f domain.yaml ``` ## Step 3: Configure DNS Get the GVC alias: ```bash theme={null} cpln gvc get quickstart-gvc -o yaml ``` Find the `alias` field in the output: ```yaml theme={null} alias: abc123xyz ``` Add a CNAME record to your DNS provider with the following values: | Type | Host | Value | TTL | | ----- | ---- | ------------------------- | --- | | CNAME | app | `.t.cpln.app` | 300 | Replace `` with the alias value from the output above. ## Step 4: Verify After DNS propagates, access your application at `https://app.example.com`. ## Step 1: Define the Apex Domain Add the following to your `main.tf` to verify domain ownership: ```hcl theme={null} resource "cpln_domain" "apex" { name = "example.com" description = "Apex domain for verification" spec { dns_mode = "cname" ports { number = 443 protocol = "http2" tls {} } } } ``` Apply the configuration to retrieve the TXT records you need to add to your DNS: ```bash theme={null} terraform apply ``` The output contains the TXT record you need to add to your DNS provider to prove ownership. Add the TXT record and wait a few minutes for propagation, then run `terraform apply` again to create the domain. ## Step 2: Define the Subdomain and Route Add the subdomain and routing configuration: ```hcl theme={null} resource "cpln_domain" "app" { depends_on = [cpln_domain.apex] name = "app.example.com" description = "Application domain" spec { dns_mode = "cname" ports { number = 443 protocol = "http2" tls {} } } } resource "cpln_domain_route" "route" { depends_on = [cpln_domain.app] domain_link = cpln_domain.app.self_link domain_port = 443 prefix = "/" workload_link = cpln_workload.hello_world.self_link } output "domain_endpoint" { value = "https://app.example.com" } output "dns_cname_record" { value = "Type: CNAME | Host: app | Value: ${cpln_gvc.quickstart.alias}.t.cpln.app | TTL: 300" } ``` ## Step 3: Apply and Configure DNS ```bash theme={null} terraform apply ``` The `dns_cname_record` output shows the CNAME record you need to add to your DNS provider. The value will be in the format `.t.cpln.app`. ## Step 4: Verify After DNS propagates, access your application at `https://app.example.com`. View the full domain resource options in the [Terraform Registry documentation](https://registry.terraform.io/providers/controlplane-com/cpln/latest/docs/resources/domain). ## Step 1: Define the Apex Domain Add to your `index.ts`: ```typescript theme={null} // Verify apex domain ownership const apexDomain = new cpln.Domain("apex-domain", { name: "example.com", description: "Apex domain for verification", spec: { dnsMode: "cname", ports: [ { number: 443, protocol: "http2", tls: {}, }, ], }, }); ``` Add to your `__main__.py`: ```python theme={null} # Verify apex domain ownership apex_domain = cpln.Domain("apex-domain", name="example.com", description="Apex domain for verification", spec=cpln.DomainSpecArgs( dns_mode="cname", ports=[cpln.DomainSpecPortArgs( number=443, protocol="http2", tls=cpln.DomainSpecPortTlsArgs(), )], )) ``` Add to your `main.go`: ```go theme={null} // Verify apex domain ownership apexDomain, err := cpln.NewDomain(ctx, "apex-domain", &cpln.DomainArgs{ Name: pulumi.String("example.com"), Description: pulumi.String("Apex domain for verification"), Spec: &cpln.DomainSpecArgs{ DnsMode: pulumi.String("cname"), Ports: cpln.DomainSpecPortArray{ &cpln.DomainSpecPortArgs{ Number: pulumi.Int(443), Protocol: pulumi.String("http2"), Tls: &cpln.DomainSpecPortTlsArgs{}, }, }, }, }) if err != nil { return err } _ = apexDomain // Used in Step 2 ``` Add to your `Program.cs`: ```csharp theme={null} // Verify apex domain ownership var apexDomain = new Domain("apex-domain", new DomainArgs { Name = "example.com", Description = "Apex domain for verification", Spec = new DomainSpecArgs { DnsMode = "cname", Ports = new[] { new DomainSpecPortArgs { Number = 443, Protocol = "http2", Tls = new DomainSpecPortTlsArgs {} } } } }); ``` Deploy the configuration to retrieve the TXT records you need to add to your DNS: ```bash theme={null} pulumi up ``` The output contains the TXT record you need to add to your DNS provider to prove ownership. Add the TXT record and wait a few minutes for propagation, then run `pulumi up` again to create the domain. ## Step 2: Define the Subdomain and Route ```typescript theme={null} // Configure subdomain const appDomain = new cpln.Domain("app-domain", { name: "app.example.com", description: "Application domain", spec: { dnsMode: "cname", ports: [ { number: 443, protocol: "http2", tls: {}, }, ], }, }, { dependsOn: [apexDomain] }); // Configure route const route = new cpln.DomainRoute("route", { domainLink: appDomain.selfLink, domainPort: 443, prefix: "/", workloadLink: workload.selfLink, }, { dependsOn: [appDomain] }); export const domain_endpoint = "https://app.example.com"; export const dns_cname_record = gvc.alias.apply( alias => `Type: CNAME | Host: app | Value: ${alias}.t.cpln.app | TTL: 300` ); ``` ```python theme={null} # Configure subdomain app_domain = cpln.Domain("app-domain", name="app.example.com", description="Application domain", spec=cpln.DomainSpecArgs( dns_mode="cname", ports=[cpln.DomainSpecPortArgs( number=443, protocol="http2", tls=cpln.DomainSpecPortTlsArgs(), )], ), opts=pulumi.ResourceOptions(depends_on=[apex_domain])) # Configure route route = cpln.DomainRoute("route", domain_link=app_domain.self_link, domain_port=443, prefix="/", workload_link=workload.self_link, opts=pulumi.ResourceOptions(depends_on=[app_domain])) pulumi.export("domain_endpoint", "https://app.example.com") pulumi.export("dns_cname_record", gvc.alias.apply( lambda alias: f"Type: CNAME | Host: app | Value: {alias}.t.cpln.app | TTL: 300" )) ``` ```go theme={null} // Configure subdomain appDomain, err := cpln.NewDomain(ctx, "app-domain", &cpln.DomainArgs{ Name: pulumi.String("app.example.com"), Description: pulumi.String("Application domain"), Spec: &cpln.DomainSpecArgs{ DnsMode: pulumi.String("cname"), Ports: cpln.DomainSpecPortArray{ &cpln.DomainSpecPortArgs{ Number: pulumi.Int(443), Protocol: pulumi.String("http2"), Tls: &cpln.DomainSpecPortTlsArgs{}, }, }, }, }, pulumi.DependsOn([]pulumi.Resource{apexDomain})) if err != nil { return err } // Configure route _, err = cpln.NewDomainRoute(ctx, "route", &cpln.DomainRouteArgs{ DomainLink: appDomain.SelfLink, DomainPort: pulumi.Int(443), Prefix: pulumi.String("/"), WorkloadLink: workload.SelfLink, }, pulumi.DependsOn([]pulumi.Resource{appDomain})) if err != nil { return err } ctx.Export("domain_endpoint", pulumi.String("https://app.example.com")) ctx.Export("dns_cname_record", gvc.Alias.ApplyT(func(alias string) string { return fmt.Sprintf("Type: CNAME | Host: app | Value: %s.t.cpln.app | TTL: 300", alias) }).(pulumi.StringOutput)) ``` ```csharp theme={null} // Configure subdomain var appDomain = new Domain("app-domain", new DomainArgs { Name = "app.example.com", Description = "Application domain", Spec = new DomainSpecArgs { DnsMode = "cname", Ports = new[] { new DomainSpecPortArgs { Number = 443, Protocol = "http2", Tls = new DomainSpecPortTlsArgs {} } } } }, new CustomResourceOptions { DependsOn = { apexDomain } }); // Configure route var route = new DomainRoute("route", new DomainRouteArgs { DomainLink = appDomain.SelfLink, DomainPort = 443, Prefix = "/", WorkloadLink = workload.SelfLink }, new CustomResourceOptions { DependsOn = { appDomain } }); ``` Update your return dictionary to include the new outputs: ```csharp theme={null} return new Dictionary { ["canonical_endpoint"] = workload.Statuses.Apply(s => s[0].CanonicalEndpoint), ["domain_endpoint"] = "https://app.example.com", ["dns_cname_record"] = gvc.Alias.Apply(alias => $"Type: CNAME | Host: app | Value: {alias}.t.cpln.app | TTL: 300") }; ``` ## Step 3: Deploy and Configure DNS ```bash theme={null} pulumi up ``` The `dns_cname_record` output shows the CNAME record you need to add to your DNS provider. The value will be in the format `.t.cpln.app`. ## Step 4: Verify After DNS propagates, access your application at `https://app.example.com`. View the full domain resource options in the [Pulumi Registry documentation](https://www.pulumi.com/registry/packages/cpln/api-docs/domain/). ## Routing Modes Control Plane supports two routing modes: | Mode | DNS Record | Best For | | ------------------- | ---------- | ------------------------------------------------------ | | **Path-based** | CNAME | Multiple workloads on different paths (`/api`, `/web`) | | **Subdomain-based** | NS | Unique subdomain per workload (`api.app.example.com`) | Route different paths to different workloads: * `https://app.example.com/api` → API workload * `https://app.example.com/web` → Frontend workload * `https://app.example.com/` → Default workload Each workload automatically receives its own subdomain: * `https://api.app.example.com` → API workload * `https://web.app.example.com` → Frontend workload Requires NS record delegation to Control Plane. ## What's Configured Your domain is now configured with: * **Automatic TLS** - Certificates provisioned and renewed automatically * **Global load balancing** - Traffic is routed to the nearest healthy location * **Path-based routing** - Multiple workloads can share the same domain ## Continue Learn how workloads communicate internally with mTLS encryption. ## Clean Up If you want to stop here instead, delete the resources created in this quickstart: Navigate to `Domains`, select your domain, and click `Delete`. ```bash theme={null} cpln domain delete app.example.com cpln domain delete example.com ``` ```bash theme={null} terraform destroy ``` ```bash theme={null} pulumi destroy ``` Remember to remove the DNS records from your DNS provider after deleting the domain from Control Plane. # 4. Service-to-Service Communication Source: https://docs.controlplane.com/quickstart/quick-start-4-service-to-service Configure internal firewall rules to enable secure service-to-service communication between workloads using automatic mTLS encryption. ## Overview This quickstart demonstrates how workloads communicate internally with automatic mTLS encryption. By default, workloads are isolated and reject internal traffic. You'll configure firewall rules to enable secure service-to-service communication. **What you'll accomplish:** * Deploy a new workload that communicates with an existing service * Observe the default deny behavior * Configure internal firewall rules to allow communication * Verify secure service-to-service communication ## Prerequisites * Completed [Quickstart 1](/quickstart/quick-start-1-deploy-workload) with a running [workload](/guides/create-workload) * [Permissions](/guides/policy) to create workloads in your org ## How Internal Communication Works Workloads communicate using internal endpoints that follow this pattern: ```text theme={null} http://WORKLOAD_NAME.GVC_NAME.cpln.local:PORT ``` All internal traffic is automatically encrypted with mTLS. No certificate management is required. So far, you've created a GVC (`quickstart-gvc`) and deployed a workload (`hello-world`). Now you'll create a new workload that communicates with the existing `hello-world` workload. ## Step 1: Create the Caller Workload Click `Workloads` in the left menu, then click `New`. 1. Name: `caller` 2. Make sure `quickstart-gvc` is selected in the GVC dropdown 1. Click `Containers` in the left pane 2. Select `External` and enter the image: `cplnquickstarts/service-to-service-quick-start:1.1` 3. Under `Ports`, set Protocol to `http` and Number to `8080` 1. Click `Firewall` in the left pane 2. Click `Make Public` 3. Click `Create` ## Step 2: Observe Default Deny Behavior The `caller` workload shows `Ready` in Workload Health. Navigate to the `caller` workload and click `Open` next to the Global Endpoint. Add this query parameter to the caller URL: ```text theme={null} ?url=http://hello-world.quickstart-gvc.cpln.local:8080 ``` The request will time out and fail because the `hello-world` workload blocks internal traffic by default. ## Step 3: Configure Internal Firewall Click `Workloads` in the left menu and select `hello-world`. 1. Click `Firewall` in the left pane 2. Under the `Internal` tab, select `Same GVC` for the Inbound Allow Type 3. Click `Update` ## Step 4: Verify Communication The `hello-world` workload redeploys with the updated firewall rules after 1-2 minutes. Refresh the caller page with the URL parameter. The `hello-world` workload now responds: ```text theme={null} Response from URL: --- Hello World! --- ``` ## Step 1: Create the Caller Workload ```bash theme={null} cpln workload create --name caller --gvc quickstart-gvc \ --image cplnquickstarts/service-to-service-quick-start:1.1 \ --port 8080 \ --public ``` ## Step 2: Test Default Deny Get the caller endpoint: ```bash theme={null} cpln workload get caller --gvc quickstart-gvc ``` Open the endpoint from the table in your browser and add: ```text theme={null} ?url=http://hello-world.quickstart-gvc.cpln.local:8080 ``` The request will time out and fail because the `hello-world` workload blocks internal traffic by default. ## Step 3: Enable Internal Access Update the `hello-world` firewall configuration to allow internal traffic: ```bash theme={null} cpln workload update hello-world --gvc quickstart-gvc --set spec.firewallConfig.internal.inboundAllowType=same-gvc ``` ## Step 4: Verify Communication Wait 1-2 minutes for redeployment, then refresh the caller page. The `hello-world` workload now responds: ```text theme={null} Response from URL: --- Hello World! --- ``` ## Step 1: Add the Caller Workload Add to your existing `main.tf` from Quickstart 1: ```hcl theme={null} # Caller workload resource "cpln_workload" "caller" { gvc = cpln_gvc.quickstart.name name = "caller" type = "standard" container { name = "main" image = "cplnquickstarts/service-to-service-quick-start:1.1" cpu = "50m" memory = "128Mi" ports { protocol = "http" number = 8080 } } options { capacity_ai = true timeout_seconds = 5 autoscaling { metric = "disabled" target = 95 min_scale = 1 max_scale = 1 } } firewall_spec { external { inbound_allow_cidr = ["0.0.0.0/0"] } } } output "caller_endpoint" { value = cpln_workload.caller.status[0].canonical_endpoint } ``` Apply the configuration: ```bash theme={null} terraform apply ``` ## Step 2: Test Default Deny Open the caller endpoint and add: ```text theme={null} ?url=http://hello-world.quickstart-gvc.cpln.local:8080 ``` The request will time out and fail because the `hello-world` workload blocks internal traffic by default. ## Step 3: Enable Internal Access Update your `cpln_workload.hello_world` resource to allow internal traffic. Add the `internal` block inside `firewall_spec`: ```hcl theme={null} firewall_spec { external { inbound_allow_cidr = ["0.0.0.0/0"] } # Add internal firewall to allow traffic from same GVC internal { inbound_allow_type = "same-gvc" } } ``` Apply the changes: ```bash theme={null} terraform apply ``` ## Step 4: Verify Communication Wait 1-2 minutes for redeployment, then refresh the caller page. The `hello-world` workload now responds: ```text theme={null} Response from URL: --- Hello World! --- ``` ## Step 1: Add the Caller Workload Add to your existing Pulumi project from Quickstart 1: ```typescript theme={null} // Caller workload const caller = new cpln.Workload("caller", { gvc: gvc.name, name: "caller", type: "standard", containers: [ { name: "main", image: "cplnquickstarts/service-to-service-quick-start:1.1", cpu: "50m", memory: "128Mi", ports: [{ protocol: "http", number: 8080 }], }, ], options: { capacityAi: true, timeoutSeconds: 5, autoscaling: { metric: "disabled", target: 95, minScale: 1, maxScale: 1, }, }, firewallSpec: { external: { inboundAllowCidrs: ["0.0.0.0/0"], }, }, }); export const callerEndpoint = caller.statuses.apply( (s) => s?.[0]?.canonicalEndpoint ?? "pending" ); ``` ```python theme={null} # Caller workload caller = cpln.Workload("caller", gvc=gvc.name, name="caller", type="standard", containers=[cpln.WorkloadContainerArgs( name="main", image="cplnquickstarts/service-to-service-quick-start:1.1", cpu="50m", memory="128Mi", ports=[cpln.WorkloadContainerPortArgs( protocol="http", number=8080, )], )], options=cpln.WorkloadOptionsArgs( capacity_ai=True, timeout_seconds=5, autoscaling=cpln.WorkloadOptionsAutoscalingArgs( metric="disabled", target=95, min_scale=1, max_scale=1, ), ), firewall_spec=cpln.WorkloadFirewallSpecArgs( external=cpln.WorkloadFirewallSpecExternalArgs( inbound_allow_cidrs=["0.0.0.0/0"], ), )) pulumi.export("caller_endpoint", caller.statuses[0].canonical_endpoint) ``` ```go theme={null} // Caller workload caller, err := cpln.NewWorkload(ctx, "caller", &cpln.WorkloadArgs{ Gvc: gvc.Name, Name: pulumi.String("caller"), Type: pulumi.String("standard"), Containers: cpln.WorkloadContainerArray{ &cpln.WorkloadContainerArgs{ Name: pulumi.String("main"), Image: pulumi.String("cplnquickstarts/service-to-service-quick-start:1.1"), Cpu: pulumi.String("50m"), Memory: pulumi.String("128Mi"), Ports: cpln.WorkloadContainerPortArray{ &cpln.WorkloadContainerPortArgs{ Protocol: pulumi.String("http"), Number: pulumi.Int(8080), }, }, }, }, Options: &cpln.WorkloadOptionsArgs{ CapacityAi: pulumi.Bool(true), TimeoutSeconds: pulumi.Int(5), Autoscaling: &cpln.WorkloadOptionsAutoscalingArgs{ Metric: pulumi.String("disabled"), Target: pulumi.Int(95), MinScale: pulumi.Int(1), MaxScale: pulumi.Int(1), }, }, FirewallSpec: &cpln.WorkloadFirewallSpecArgs{ External: &cpln.WorkloadFirewallSpecExternalArgs{ InboundAllowCidrs: pulumi.StringArray{pulumi.String("0.0.0.0/0")}, }, }, }) if err != nil { return err } ctx.Export("caller_endpoint", caller.Statuses.Index(pulumi.Int(0)).CanonicalEndpoint()) ``` ```csharp theme={null} // Caller workload var caller = new Workload("caller", new WorkloadArgs { Gvc = gvc.Name, Name = "caller", Type = "standard", Containers = new[] { new WorkloadContainerArgs { Name = "main", Image = "cplnquickstarts/service-to-service-quick-start:1.1", Cpu = "50m", Memory = "128Mi", Ports = new[] { new WorkloadContainerPortArgs { Protocol = "http", Number = 8080 } } } }, Options = new WorkloadOptionsArgs { CapacityAi = true, TimeoutSeconds = 5, Autoscaling = new WorkloadOptionsAutoscalingArgs { Metric = "disabled", Target = 95, MinScale = 1, MaxScale = 1 } }, FirewallSpec = new WorkloadFirewallSpecArgs { External = new WorkloadFirewallSpecExternalArgs { InboundAllowCidrs = new[] { "0.0.0.0/0" } } } }); ``` Add to your return dictionary: ```csharp theme={null} ["caller_endpoint"] = caller.Statuses.Apply(s => s[0].CanonicalEndpoint) ``` Deploy the configuration: ```bash theme={null} pulumi up ``` ## Step 2: Test Default Deny Open the caller endpoint and add: ```text theme={null} ?url=http://hello-world.quickstart-gvc.cpln.local:8080 ``` The request will time out and fail because the `hello-world` workload blocks internal traffic by default. ## Step 3: Enable Internal Access Update your `hello-world` workload to allow internal traffic. Add the `internal` block inside `firewallSpec`: ```typescript theme={null} firewallSpec: { external: { inboundAllowCidrs: ["0.0.0.0/0"], }, // Add internal firewall to allow traffic from same GVC internal: { inboundAllowType: "same-gvc", }, }, ``` ```python theme={null} firewall_spec=cpln.WorkloadFirewallSpecArgs( external=cpln.WorkloadFirewallSpecExternalArgs( inbound_allow_cidrs=["0.0.0.0/0"], ), # Add internal firewall to allow traffic from same GVC internal=cpln.WorkloadFirewallSpecInternalArgs( inbound_allow_type="same-gvc", ), ) ``` ```go theme={null} FirewallSpec: &cpln.WorkloadFirewallSpecArgs{ External: &cpln.WorkloadFirewallSpecExternalArgs{ InboundAllowCidrs: pulumi.StringArray{pulumi.String("0.0.0.0/0")}, }, // Add internal firewall to allow traffic from same GVC Internal: &cpln.WorkloadFirewallSpecInternalArgs{ InboundAllowType: pulumi.String("same-gvc"), }, }, ``` ```csharp theme={null} FirewallSpec = new WorkloadFirewallSpecArgs { External = new WorkloadFirewallSpecExternalArgs { InboundAllowCidrs = new[] { "0.0.0.0/0" } }, // Add internal firewall to allow traffic from same GVC Internal = new WorkloadFirewallSpecInternalArgs { InboundAllowType = "same-gvc" } } ``` Deploy the changes: ```bash theme={null} pulumi up ``` ## Step 4: Verify Communication Wait 1-2 minutes for redeployment, then refresh the caller page. The `hello-world` workload now responds: ```text theme={null} Response from URL: --- Hello World! --- ``` ## Internal Firewall Options | Option | Description | | ------------------ | ------------------------------------------- | | **None** (default) | Block all internal traffic | | **same-gvc** | Allow from any workload in the same GVC | | **same-org** | Allow from any workload in the organization | | **workload-list** | Allow from specific workloads only | The `workload-list` option requires `view` permission on the allowed workloads. ## Internal Endpoint Format Workloads communicate using internal DNS endpoints: ```text theme={null} http://WORKLOAD.GVC.cpln.local:PORT ``` **Examples:** * `http://api.production-gvc.cpln.local:8080` * `http://cache.quickstart-gvc.cpln.local:6379` The endpoint above load-balances across all replicas. For stateful workloads with `replicaDirect` enabled, you can also reach an individual replica directly — see [Finding Replica Hostnames](/reference/workload/general#finding-replica-hostnames). ## What You've Learned * Workloads are **isolated by default** - internal traffic is blocked * **mTLS is automatic** - no certificate configuration is required * **Firewall rules** control which workloads are allowed to communicate * Internal endpoints use the `.cpln.local` domain ## Clean Up To delete all resources created through the quickstart series: 1. Navigate to `GVCs`, select `quickstart-gvc`, and from `Actions` click `Delete` 2. Navigate to `Domains`, select your domains, and from `Actions` click `Delete` ```bash theme={null} cpln gvc delete quickstart-gvc cpln domain delete app.example.com cpln domain delete example.com ``` ```bash theme={null} terraform destroy ``` ```bash theme={null} pulumi destroy ``` Remember to remove the DNS records from your DNS provider after deleting the domains. ## Next Steps Deploy your own containerized application on Control Plane. Manage sensitive configuration for your workloads. Grant workloads access to cloud resources securely. Automate deployments with your CI/CD pipeline. # Agent Source: https://docs.controlplane.com/reference/agent Reference for Control Plane agents used to connect workloads to private networks, including protocol versions, high availability, sizing guidance, and permissions. ## Overview In situations where your [workload](/concepts/workload) needs to consume services from endpoints within a VPC or another private network, you can use a wormhole **agent**. Control Plane's wormhole technology securely connects your [workloads](/concepts/workload) to any TCP or UDP endpoints in VPCs and other private networks, including on-prem data centers and even on a developer's laptop. You establish a wormhole by running an agent VM (agent for short) inside the private network to which you are connecting your [workloads](/concepts/workload). Control Plane offers agents for both private networks and cloud providers. The agent is installed and configured within the location where it can access the applications or services that your [workload](/concepts/workload) requires. It does this by establishing a secure and persistent connection to publicly hosted Control Plane servers. Requests from your workload are tunneled to the agent, which performs the request, and then tunnels the response back. This flow occurs in a performant manner and is transparent to the [workload](/concepts/workload). Agents are scoped to an [org](/reference/org) and are used in conjunction with [identities](/reference/identity) to set up [network resources](/reference/identity#network-resources-cloud-wormhole). ## Protocol Version 2 Control Plane updated agents in April 2026 to support new features and enhance performance. If your agent was created before the update, it is running protocol version `v1`. If it was created after the update, it is running protocol version `v2`. Version `v1` is still supported but newly created agents use protocol version `v2` only. ### New Features * Faster agent deployment times. * Better VPN-layer isolation. * Load-balancing across agent instances. * No need for public IPs on your BYOK locations. * Use your preferred hardened Linux base image on supported hyperscalers. * [Bi-directional functionality](#bi-directional-functionality). ## Setup Agent ### Create and Set up an Agent v2 Use one of the following guides for step-by-step instructions on how to create an agent with a specific provider: * [Amazon Web Services (AWS)](/guides/setup-agent#aws) * [Microsoft Azure](/guides/setup-agent#azure) * [Google Cloud Platform (GCP)](/guides/setup-agent#gcp) * [Docker](/guides/setup-agent#running-locally-in-docker) (e.g., private data centers, internal web services, developer's laptop, etc.) * [Kubernetes (k8s) Cluster](/guides/setup-agent#kubernetes-k8s-cluster) ### Create and Set up an Agent v1 `v1` is deprecated. Use the updated [Configure an Agent](/guides/setup-agent) guide unless your agent was created before April 2026. Use one of the following guides for step-by-step instructions on how to create an agent with a specific provider: * [Amazon Web Services (AWS)](/guides/setup-agent-legacy#aws) * [Microsoft Azure](/guides/setup-agent-legacy#azure) * [Google Cloud Platform (GCP)](/guides/setup-agent-legacy#gcp) * [Private Network](/guides/setup-agent-legacy#private-network) (e.g., private data centers, internal web services, developer's laptop, etc.) * [Kubernetes (k8s) Cluster](/guides/setup-agent-legacy#kubernetes-k8s-cluster) If you are running a legacy version of the agent (protocol version v1 in the UI), you will need to follow the [legacy agent setup guide](/guides/setup-agent-legacy) to set up your agent. ## High Availability (HA) Version 2 agents run in active-active mode. If an agent misses a set amount of heartbeats, it is considered offline and the remaining active agents continue handling work until the instance group automatically replaces it. The recommended method to configure a highly available agent deployment is by using an instance group (also called an autoscaling group on AWS or a virtual machine scale set (VMSS) on Azure). The autoscaling group template must be configured in the same way as a single VM in terms of cloud configuration, network settings, etc. As the agent's functionality is not CPU intensive, that metric cannot be used to scale instances up and down. Instead, use a fixed-sized group with a minimum of 2 and a maximum equal to the number of zones in your infrastructure. ## Bi-directional Functionality Version 2 includes a proxy server that listens on port `3128`. It allows applications running inside the private network where the agent is deployed to call workloads running on Control Plane without opening external firewall access or allowlisting the private network’s outbound Internet IP. Use this when systems inside your private network need to initiate outbound calls to workloads running on Control Plane without exposing those workloads broadly to the Internet. Bi-directional functionality helps enforce least-privilege access to workloads running on Control Plane, which can support compliance requirements. ### Configuration To allow an agent to proxy a request to the workload running on Control Plane, follow these steps: 1. Click `Workload` in the left menu and select the workload the agent will be call. 2. Click `Firewall`, then click the `Internal` tab. 3. Click `Inbound Allow Type` and select either `Workload List` or `Same GVC & Workload List`, depending on your requirements. 4. Click `Add Agent` and select one or more agents. Click `OK`. 5. Click `Update`. Once the deployment is complete, the agent will be allowed to proxy requests to this workload. Using the CLI, add the agent's link to the workload's internal firewall `inboundAllowWorkload` list: ```yaml YAML theme={null} spec: firewallConfig: internal: inboundAllowType: workload-list inboundAllowWorkload: - //agent/AGENT_NAME ``` On [BYOK](/byok) locations, the agent reverse proxy also requires the middlebox add-on to have [`ingressReplicas`](/byok/settings/agent#middlebox) set greater than `0`. If it is `0`, the workload reports `Reverse proxy is not fully configured in this location`. ### Proxy Usage To route traffic through the proxy, configure the calling application to use the agent’s internal IP address and port `3128` as its HTTPS proxy. Obtain the agent’s internal IP address from the instance summary page for the cloud provider where the agent is deployed. ```bash Curl example theme={null} HTTPS_PROXY=http://AGENT_IP:3128 curl https://example-cpln-workload.cpln.app ``` You can reach the workload through the proxy using any of its endpoints: its canonical endpoint (`-.cpln.app`), any custom [domains](/reference/domain) that route to it, or its in-cluster name (`..cpln.local`). If you are not able to configure the application to use the proxy server, the global proxy server setting of the running environment can be configured. ```bash theme={null} export HTTPS_PROXY=http://AGENT_IP:3128 ``` Set this environment variable in the running profile if you want it to persist after the environment restarts. Even if the global proxy server is set to the agent, only requests to the selected workload on Control Plane are proxied. All other requests bypass the proxy. ## Agent Sizing Guidance When utilizing an agent, the machine type running the agent affects the bandwidth and latency exhibited by the agent. The tables below are provided to help guide your agent machine type selection. Tests were performed using [qperf](https://github.com/linux-rdma/qperf/blob/master/src/help.txt). The qperf client was configured as a container workload running on the Control Plane platform and deployed in the same region as the qperf server running on a virtual machine. Based on the results below, select an agent type suitable for your performance requirements. * **AWS** * Region tested: `aws-us-west-2` * qperf server running on: `c5.2xlarge` instance type * Test runtime: 30 seconds | Agent Instance Type | Average Bandwidth (MB/sec) | Average Latency (us) | Baseline Bandwidth (Gbps) | | :------------------ | :------------------------- | :------------------- | :------------------------ | | No Agent | 307.6 | 585.6 | n/a | | t2.micro | 21.23 | 1301 | 0.064 | | t3.small | 143.9 | 1107 | 0.128 | | c5.large | 341.1 | 629.6 | 0.75 | | c4.xlarge | 70.25 | 680.8 | 5.0 | When capacity planning the baseline bandwidth should be used instead of the burst bandwidth. Available baseline bandwidth for each instance can be found using the AWS CLI. This query shows the BaselineBandwidthInGbps for t3 instances. Adjust as needed for the instance types required. ```bash theme={null} aws ec2 describe-instance-types \ --filters "Name=instance-type,Values=t3.*" \ --query "InstanceTypes[].[InstanceType, NetworkInfo.NetworkCards[0].BaselineBandwidthInGbps] | sort_by(@,&[1])" \ --output table ------------------------- | DescribeInstanceTypes | +-------------+---------+ | t3.nano | 0.032 | | t3.micro | 0.064 | | t3.small | 0.128 | | t3.medium | 0.256 | | t3.large | 0.512 | | t3.xlarge | 1.024 | | t3.2xlarge | 2.048 | +-------------+---------+ ``` * **GCP** * Region tested: `gcp-us-east1` * qperf server running on: `e2-standard-8` machine type * Test runtime: 30 seconds | Agent Machine Type | Average Bandwidth (MB/sec) | Average Latency (us) | | :----------------- | :------------------------- | :------------------- | | No Agent | 313.4 | 251.2 | | n2-standard-2 | 250.3 | 407.7 | | n2-standard-8 | 223.3 | 350.7 | | n2-standard-4 | 217.5 | 354.1 | | n1-standard-1 | 199.9 | 409.3 | ## Find the protocol version of an agent ### UI Console 1. Click `Agents` in the left menu. 2. Click the agent name to view the agent details page. 3. The protocol version is displayed in the `Summary` section. ### CLI 1. Execute the following command to get an agent: ```bash theme={null} cpln agent get -o yaml ``` 2. The protocol version can be found in the `status.protocolVersion` field. If the field is not present, the agent is running protocol version `v1`. If the field is present and has a value of `v2`, the agent is running protocol version `v2`. ## Permissions The permissions below are used to define [policies](/reference/policy) together with one or more of the four [principal types](/concepts/access-control): | Permission | Description | Implies | | :--------- | :-------------------------- | :-------------------------------------- | | create | Create new agents | | | delete | Delete service agents | | | edit | Modify existing agents | view | | manage | Full access | create, delete, edit, manage, use, view | | use | Use an agent in an identity | view | | view | Read-only access | | ## Access Report Displays the permissions granted to principals for the agent. ## CLI To view the CLI documentation for an agent, see the [agent CLI reference](/cli-reference/commands/agent). # Audit Context Source: https://docs.controlplane.com/reference/auditctx Create and manage audit contexts that enable workloads and third-party systems to write tamper-proof audit trail entries alongside Control Plane platform events. ## Overview Control Plane provides a tamper-proof audit trail service for both Control Plane and custom workload actions. To use this feature, create a unique **audit context** for your workload. The `cpln` audit context is pre-provisioned and captures all native Control Plane platform activity. See the [audit trail](/core/audittrail) reference page for details on how to query the audit trail and securely capture actions for your workloads. ## Create an Audit Context See the [Create an Audit Context](/guides/create-audit-context) guide for setup instructions. ## Permissions The following permissions can be assigned through [policies](/reference/policy) to supported [principal types](/concepts/access-control): | Permission | Description | Implies | | :--------- | :----------------------- | :------------------------------------------------ | | create | Create new contexts | | | edit | Modify existing contexts | view | | manage | Full access | create, edit, manage, readAudit, view, writeAudit | | readAudit | Read from this context | view | | view | Read-only view | | | writeAudit | Write to this context | view | ## Access Report Displays the permissions granted to principals for the audit context. ## Writing Audit Records from a Workload 1. Make sure the workload is assigned an identity that is granted `writeAudit` permission on the target audit context. 2. Write events using the internal audit endpoint: ```bash theme={null} # Minimal example curl -H "Content-Type: application/json" -X POST http://127.0.0.1:43000/audit/org/${CPLN_ORG}/auditctx/custom-audit-context?async=true -d '{"resource": {"id": "anyid123", "type": "anytype"}}' ``` ## CLI To view the CLI documentation for audit contexts, see the [Audit Context CLI reference](/cli-reference/commands/auditctx). # Cloud Account Source: https://docs.controlplane.com/reference/cloudaccount Cloud account mappings to AWS, Azure, and GCP. Covers integration methods, role configuration, and credential management for cloud provider access. ## Overview A **cloud account** establishes a secure integration between your [org](/reference/org) and an external cloud provider. When creating a cloud account, you are guided to create a provider-side bridge role or service principal with minimum access permissions. This bridge identity allows Control Plane to securely manage and query cloud resources on behalf of your [org](/reference/org). Cloud accounts are scoped to an [org](/reference/org) and are used in conjunction with [identities](/reference/identity) to set up [cloud access](/reference/identity#cloud-access-universal-cloud-identity) rules. Your [workload](/concepts/workload) can then be associated with an [identity](/reference/identity) and will have access to permitted cloud provider resources without additional configuration. For example, if an application uses AWS S3 for storage and Azure Cosmos DB for its database, a workload can access both services regardless of the [location](/reference/location) where the workload is deployed. The [workload](/concepts/workload) interacts with each service as if it were directly connected, while the [identity](/reference/identity) handles secure routing and credential exchange transparently. ## Create a Cloud Account See the [Create a Cloud Account](/guides/create-cloud-account) guide for setup instructions. ## AWS Details * Cloud Account Creation * When registering a cloud account targeting AWS, your AWS administrator must create a new AWS role (named `cpln-ORG_NAME`) with the following policies: * A `cpln-connector` policy granting permissions to create and manage IAM roles. * `ReadOnlyAccess` policy for read-only access to AWS services and resources. * A "trust relationship" must be configured to allow Control Plane to assume this role. * Identity Creation * When an [identity](/reference/identity) targets AWS, Control Plane creates an IAM role with least-privilege permissions for the requested services. * When a [workload](/concepts/workload) requests AWS access via its [identity](/reference/identity), Control Plane issues temporary credentials by assuming the IAM role and injecting them into the [workload](/concepts/workload) at runtime. * Removing an AWS Cloud Account * If you no longer require Control Plane to create identities targeting AWS: * Delete all cloud access rules targeting AWS from existing workload [identities](/reference/identity). This will delete any associated roles. * Delete the cloud account. * Your AWS administrator will need to remove the `cpln-connector` policy and the `cpln-ORG_NAME` role. ## Azure Details When registering an Azure cloud account, you can choose between the following integration methods: * [Azure SDK](#azure-sdk) * Access tokens are minted using an Azure service principal. * [Azure Connector](#azure-connector) * An [Azure Function App](https://azure.microsoft.com/en-us/services/functions/) acts as a bridge that calls internal Azure APIs to obtain access tokens. During Azure cloud account setup, credentials generated via Azure CLI are securely stored and used by Control Plane to provision identities and manage resources. These credentials are uploaded and stored securely as an [Azure-SDK secret](/reference/secret#azure-sdk) or [Azure-Connector secret](/reference/secret#azure-connector) and can be viewed by clicking `Secrets` from the left menu in the console after creating an Azure cloud account. If you don't have the Azure CLI installed locally, you can run the commands in [Azure Cloud Shell](https://shell.azure.com/bash) — a browser-based shell with `az` preinstalled and authenticated against your signed-in account. ### Azure SDK * Cloud Account Creation * When registering a cloud account targeting Azure using the Azure SDK, an Azure service principal is created by your Azure administrator using the Azure CLI. The output of the Azure CLI command contains credentials that are uploaded, stored securely, and assigned to the cloud account. * The name of this service principal is the name of your [org](/reference/org) prefixed with `cpln-` (i.e., cpln-ORG\_NAME). * The only Azure API permission required by this principal is `Application.ReadWrite.OwnedBy` (which belongs to the Microsoft Graph API). * By granting this permission, the cloud account can create and manage app registrations and query available Azure scopes and roles during the creation of an [identity](/reference/identity). The permissions `Application.ReadWrite.OwnedBy` "allows the app to create other applications, and fully manage those applications (read, update, update application secrets and delete), without a signed-in user. It cannot update any apps that it is not an owner of". For every [identity](/reference/identity) created, Control Plane creates an app registration, generates short-lived credentials, and injects them into your [workload](/concepts/workload) using the native cloud provider identity interface. Regardless of where your workload is running, Control Plane conveys the identity information correctly to the consumed services. Control Plane mints tokens for the [identity](/reference/identity) bound to the workloads. * Identity Creation * When creating an [identity](/reference/identity), [cloud access](/reference/identity#cloud-access-universal-cloud-identity) rules targeting Azure can define the minimum access needed by the identity. When the [identity](/reference/identity) is saved in the console, the Azure cloud account uses the service principal to create a new app registration with the configured access. * Any [workload](/concepts/workload) can be configured to use this [identity](/reference/identity) and will have access to the defined resources. * Expired / Compromised Credentials * If the service principal credentials assigned to the cloud account expire or have been compromised, perform the following: * From the Azure portal: * Click `Azure Active Directory` and click `App registrations` in the left menu. * Click `All applications` and then click the app registration named `cpln-ORG_NAME`. * Click `Certificates & secrets` and then click `New client secret` and follow the wizard. * Copy the value of the new secret to the clipboard. * Delete the old secret. * From the Control Plane console: * Click `Secrets` in the left menu and select the `azure-sdk` secret that belongs to the cloud account (it will be named `CLOUD_ACCOUNT_NAME-access`). * Click the `Edit Data` button and then click the eye icon. * Update the `clientSecret` property with the value of the new secret by pasting it from the clipboard. * Until a valid secret value has been updated, Control Plane will not be able to manage workload [identities](/reference/identity). * Removing an Azure Cloud Account * If you no longer require Control Plane to create identities targeting Azure: * Delete all cloud access rules targeting Azure from existing workload [identities](/reference/identity). This will delete any associated app registrations. * Delete the cloud account. * Delete the [azure secret](/reference/secret#azure-sdk) that was mapped to the cloud account (named `CLOUD_ACCOUNT-access`). * Your Azure administrator will need to delete the service principal from Azure by running the following Azure CLI command: ```bash theme={null} az ad sp delete --id http://cpln-ORG_NAME ``` ### Azure Connector * Cloud Account Creation * When registering a cloud account targeting Azure using the console, your organization's Azure administrator will use the Azure CLI to perform the following: * Create an `Azure Function App` using an existing resource group and storage account. * Download the Control Plane Azure connector code. * Deploy the code to the function app. * Obtain the URL of the deployed function. * Obtain the code/key of the deployed function. * After the registration is completed, a secret of type [azure-connector](/reference/secret#azure-connector) will be created. This secret can be reused to create another cloud account (using the same Function App) or if an existing cloud account was inadvertently deleted. * Functionality * The Function App that is deployed has a function called `iam-broker`. This function is called by Control Plane to obtain and inject the access token on behalf of the calling workload. * The Function App is set as an `Owner` of the subscription. This role assignment is needed to create the managed identities. * Identity Creation * When an [identity](/reference/identity) is created targeting Azure resources, a managed identity is assigned to the function so that the function can act on its behalf. This [identity](/reference/identity) has only the minimum permissions required to access the targeted services. When an [identity](/reference/identity) is assigned to a [workload](/concepts/workload) that requests access to an Azure resource, Control Plane obtains a temporary access token that impersonates the roles and injects it into the [workload](/concepts/workload), granting it access. * Pricing * The Function App runs as a managed service and is subject to charges against your Azure subscription. * See the [Azure Function Pricing](https://azure.microsoft.com/en-us/pricing/details/functions/) page to view current pricing. * Compromised Code * If the Function App code has been compromised, perform the following: * From the Azure portal: * In the search bar, enter `Function App` and click the first result. * Click the app to update and click `Functions`. * Click `iam-broker` and then click `Function Keys`. * For the `default` Function Key, click `Renew key value`. A confirmation modal will be displayed, click `Renew`. * After the new key has been generated, click the link `Hidden value. Click to show value`. * Copy the value of the new code to the clipboard. * From the Control Plane console: * Click `Secrets` in the left menu and select the `azure-connector` secret that belongs to the cloud account (it will be named `CLOUD_ACCOUNT_NAME-access`). * Click the `Edit Data` button and then click the eye icon. * Update the `Code` property with the value of the new code by pasting it from the clipboard. * Until a valid code has been updated, Control Plane will not be able to manage workload [identities](/reference/identity). * Removing an Azure Cloud Account * If you no longer require Control Plane to create identities targeting Azure: * Delete all cloud access rules targeting Azure from existing workload [identities](/reference/identity). This will delete any user assigned identities. * Delete the cloud account. * Delete the [azure connector secret](/reference/secret#azure-connector) that was mapped to the cloud account (named `CLOUD_ACCOUNT-access`) if it is not being used by another cloud account. * Your Azure administrator will need to delete the Function App from the Azure portal or by running the following Azure CLI command: ```bash theme={null} az functionapp delete --name FUNCTION_APP_NAME --resource-group AZURE_RESOURCE_GROUP ``` ## GCP Details * Cloud Account Creation * When registering a cloud account targeting GCP, your GCP administrator will add the Control Plane GCP service account as a member of your GCP account with the following roles: * `Viewer` * `Project IAM Admin` * `Service Account Admin` * `Service Account Token Creator` * The Control Plane GCP service account is in the format: `cpln-ORG_NAME@ENV.iam.gserviceaccount.com`. * Identity Creation * When an [identity](/reference/identity) is created targeting GCP, Control Plane creates a new service account in your GCP account with the minimum permissions required to access the targeted services. * When a [workload](/concepts/workload) assigned an [identity](/reference/identity) requests credentials to access GCP services, Control Plane obtains a temporary access token that impersonates the service account and injects it into the [workload](/concepts/workload), granting it access. * If the [identity](/reference/identity) connects with a particular GCP service, the Control Plane GCP service account needs the `Admin` role for that service. * For example, if the [identity](/reference/identity) uses Cloud Storage, the `cpln-ORG_NAME@ENV.iam.gserviceaccount.com` service account needs the `Storage Admin` role. Adding this role allows Control Plane to grant a temporary access token to the workload when reading from or writing to a storage bucket. * Control Plane cannot assign roles similar to `Compute Network User` unless `Compute Admin` is assigned to the `cpln-ORG_NAME@ENV.iam.gserviceaccount.com` service account. If your workload [identities](/reference/identity) use many GCP services, it may be easier to grant the `cpln-ORG_NAME@ENV.iam.gserviceaccount.com` service account the `Owner` role instead of granting the `Admin` role for each service. * Removing a GCP Cloud Account * If you no longer require Control Plane to create identities targeting GCP: * Delete all cloud access rules targeting GCP from existing workload [identities](/reference/identity). This will delete any associated service account. * Delete the cloud account. * Remove the Control Plane user from your GCP account. ## Permissions The permissions below are used to define [policies](/reference/policy) together with one or more of the four [principal types](/concepts/access-control): | Permission | Description | Implies | | :--------- | :----------------------------- | :----------------------------------------- | | browse | Browse account contents | view | | create | Create new cloud accounts | | | delete | Delete existing cloud accounts | | | edit | Modify existing cloud accounts | view, browse | | manage | Full access | browse, create, delete, edit, manage, view | | view | Read-only access | | ## Access Report Displays the permissions granted to principals for the cloud account. ## CLI To view the CLI documentation for cloud accounts, see the [Cloud Account CLI reference](/cli-reference/commands/cloudaccount). # Domain Source: https://docs.controlplane.com/reference/domain Domain configuration for workloads with TLS certificates, geo-routing, path-based routing, and DNS verification modes (CNAME and NS delegation). ## Domain Overview [Workloads](/concepts/workload) managed by Control Plane can be configured to serve requests using domains that you own. Domains are scoped to an [org](/reference/org) and are associated with **one** [GVC](/reference/gvc). The domain can then be configured to handle requests for one or more [workloads](/concepts/workload) within that [GVC](/reference/gvc). Domains configured in Control Plane are automatically secured with TLS certificates, load balanced, and DNS geo-routed to the nearest healthy [location](/reference/location) assigned to the [GVC](/reference/gvc). Geo-routing behavior can be customized per location using [location routing options](/reference/gvc#location-routing-options) to configure priority-based failover and latency adjustments. ### Domain Name Validation Domain names must conform to standard DNS naming rules and use a valid TLD. The domain name validation follows standard DNS domain name rules. ## Default Domain Names If a domain is not associated with a [GVC](/reference/gvc), the following default endpoints are available to [workloads](/concepts/workload): * Canonical endpoints (Global): `cpln.app` * Individual location endpoints: `controlplane.us` ## Configure a Domain See the [Configure a Domain](/guides/configure-domain) guide for setup instructions. ## Domain Configuration Example The following example demonstrates a complete domain configuration: ```yaml theme={null} kind: domain name: my-domain spec: dnsMode: cname # or "ns" certChallengeType: http01 # or "dns01" gvcLink: //gvc/my-gvc ports: - number: 443 protocol: http2 tls: minProtocolVersion: TLSV1_2 cipherSuites: - ECDHE-ECDSA-AES256-GCM-SHA384 - ECDHE-RSA-AES256-GCM-SHA384 clientCertificate: secretLink: //secret/client-ca-cert serverCertificate: secretLink: //secret/server-cert cors: allowCredentials: true maxAge: 24h allowOrigins: - exact: https://example.com - regex: "https://.*\\.example\\.com" allowMethods: - GET - POST - PUT - DELETE allowHeaders: - Authorization - Content-Type exposeHeaders: - X-Custom-Header routes: - prefix: /api/ replacePrefix: /v2/api/ port: 443 workloadLink: //gvc/my-gvc/workload/api-service headers: request: set: X-Custom-Header: "value" X-Client-IP: "%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%" - prefix: /web port: 3000 workloadLink: //gvc/my-gvc/workload/web-service canaries: - workloadLink: //gvc/my-gvc/workload/web-service-v2 weight: 20 - prefix: / port: 8080 workloadLink: //gvc/my-gvc/workload/default-service mirror: - workloadLink: //gvc/my-gvc/workload/mirror-service percent: 50 ``` ## Domain Verification An apex domain, also known as a root domain, refers to a domain name that does not have any subdomain prefix (e.g., **example.com**). If you want to use subdomains configured in NS mode, you must create and verify the apex domain in the org. Even if the apex domain is not used by workloads, it must still be verified before subdomains can be created. If multiple [orgs](/reference/org) create subdomains using the same apex domain, apex domain verification only needs to be performed in one of the orgs. Subdomains in orgs that do not have the apex domain require a TXT record to be added to DNS for verification. The verification TXT record can be named `_cpln.` or `_verify.` and must have the value of the org name or org id. When creating domains with the CLI, the operation fails if the required TXT and CNAME records are missing from your DNS. The error message prints the exact records that must be added; update your DNS with those values and rerun the command. As a best practice, create the apex domain in the org designated for your production environment. ## Hostname Behavior The `Host` header forwarded to the target workload depends on both the workload type and the domain used for the request. For **serverless** workloads, even if the request was served using a custom domain, the `Host` header will be the canonical endpoint of the workload. For **standard** or **stateful** workloads, the `Host` header will be the domain name used for the request. Whenever the `Host` header is manipulated, the `X-Forwarded-Host` header will be appended with the original `Host` from the request. ## Routing Modes Control Plane provides two routing modes for directing requests to your domain: 1. [Path-based routing](#path-based-routing) 2. [Subdomain-based routing](#subdomain-based-routing) ### Path-Based Routing Path-based routing allows requests matching a specific path prefix to be routed to a specific workload. Multiple paths can be defined, but at least one path must be defined. This is accomplished by adding a **CNAME** record to DNS and configuring the domain with `dnsMode: cname`. Advantages of using path-based routing: * You maintain full control of DNS for the domain. * CDN / WAF compatible. **Example URL endpoints:** * If the domain `sub.example.com` is configured to point to the GVC that contains `workload_one` and `workload_two`, the path `workload_one` can route requests to `workload_one`, and the path `workload_two` can route requests to `workload_two`. * **[https://sub.example.com/workload\_one](https://sub.example.com/workload_one)** * **[https://sub.example.com/workload\_two](https://sub.example.com/workload_two)** All path prefix values must be unique and there must be at least one path defined. Routes are automatically sorted by prefix length (longest first), then by host prefix length, then by trailing slash preference. #### Path Prefix Replacement The `replacePrefix` property allows a path prefix to be replaced when forwarding the request to the [workload](/concepts/workload). **For example:** A request to the URL `https://sub.example.com/users/` can have the path prefix `/users/` replaced with the path prefix `/v2/users/` when forwarding the request internally to the target [workload](/concepts/workload). This does not work for regex-based paths. #### Root Path Prefix The `/` path will match any unmatched path prefixes for the subdomain. #### Path Order The order of the path prefix list is adjustable. When a request to the domain is received, the first match will be processed. When combining the `/` path prefix with other path prefixes, the `/` path will automatically be placed last in the list because it matches all requests. #### Port Selection The `port` property allows each route to forward traffic to a specific port exposed by the running workload. For **serverless** workloads, assigning the port is optional since only one port can serve traffic. For **standard** and **stateful** workloads that serve traffic on multiple ports, specific ports can be assigned on a route. If no port is assigned, external requests will be routed to the first port in the container's port list (in declaration order). #### Replica Selection The `replica` property allows **stateful** workloads with replicaDirect enabled to specify a replica number to route traffic directly to a specific replica. The replica number must be an integer greater than or equal to 0. If no replica is specified, traffic will be routed to all replicas of the workload. #### Header Operations The `headers` property allows routes to modify HTTP headers for all requests processed by that route. Header operations allow you to set or override headers before forwarding requests to the workload. **Supported Operations:** * **`set`**: Sets or overrides headers for all HTTP requests processed by this route **Header Value Wildcards:** Header values support the following wildcards: * `%REQUESTED_SERVER_NAME%`: The server name requested by the client * `%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%`: The client IP address without port * `%START_TIME%`: The request start time **Example:** ```yaml theme={null} headers: request: set: X-Custom-Header: "value" X-Client-IP: "%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%" X-Server-Name: "%REQUESTED_SERVER_NAME%" ``` When a domain is serving path-based requests, the [domain port protocol](#protocol) must be compatible with the selected protocol of the port configured on the container. For example, HTTP2 is compatible with HTTP2 and gRPC, HTTP is compatible with HTTP, and so on. #### Mirror Routing The `mirror` property allows a route to be configured to mirror requests to another workload. **For example:** A request to the URL `https://sub.example.com/` can be mirrored to the workload `api-service-v2` 50% of the time. This is commonly used for testing new workload versions before promoting them to production traffic. An optional `port` property can be added to the mirror configuration to specify which port the mirrored request should be sent to on the target workload. If not specified, it defaults to the first listed port on the target workload. **Example:** ```yaml theme={null} routes: - prefix: /api workloadLink: //gvc/my-gvc/workload/api-service mirror: - workloadLink: //gvc/my-gvc/workload/api-service-v2 percent: 50 - workloadLink: //gvc/my-gvc/workload/api-service-v3 percent: 25 port: 8081 ``` The mirrored workload must be in the same GVC as the main workload. If you need to mirror to a workload in a different GVC, contact support. #### Canary Routing The `canaries` property splits traffic for a route across one or more additional workloads by weight. This is commonly used to gradually shift production traffic onto a new workload version, or to perform a blue/green cutover. Each canary entry requires a `workloadLink` and a `weight` (an integer between 1 and 100). An optional `port` specifies which port the canary traffic is sent to on the target workload; if not specified, it defaults to the first configured port on that workload. The combined weight of all canaries on a route must not exceed 100. The remaining weight is sent to the route's primary workload. A combined weight of 100 sends no traffic to the primary workload, which performs a full blue/green cutover. **For example:** the route below sends 25% of traffic to `api-service-v2`, 10% to `api-service-v3`, and the remaining 65% to the primary workload `api-service`. **Example:** ```yaml theme={null} routes: - prefix: /api workloadLink: //gvc/my-gvc/workload/api-service canaries: - workloadLink: //gvc/my-gvc/workload/api-service-v2 weight: 25 - workloadLink: //gvc/my-gvc/workload/api-service-v3 weight: 10 port: 8081 ``` If a canary's workload does not exist or is not deployed in a given [location](/reference/location), the canary is omitted in that location and its weight is returned to the primary workload. Canary workloads must be in the same GVC as the route's primary workload. Canaries are supported only on `http` and `http2` ports, and are not supported when the route's primary workload or a canary workload is **serverless**. ### Regex-Based Routing Instead of specifying routes based on a path prefix, paths can be specified using [regex](https://github.com/google/re2/wiki/Syntax). **For example:** A path regex can be specified as `/user/.*/profile` to match routes like `/user/bob/profile` or `/user/mary/profile`. If at least one regex path is found, routes will not be sorted. You must specify either a prefix or regex for each route, but not both. ### Subdomain-Based Routing Subdomain-based routing maps a domain to all workloads within a [GVC](/reference/gvc). This works in either [DNS mode](#dns-modes). In **NS mode** (`dnsMode: ns`), add **NS** records that delegate DNS to Control Plane for this domain **only**, and Control Plane creates every subdomain record automatically. In **CNAME mode** (`dnsMode: cname` with a `gvcLink`), you create one **CNAME** record per workload — see [Subdomain-Based Routing in CNAME Mode](#cname-mode-dnsmode-cname). Advantages of using subdomain-based routing: * Best choice when a unique DNS subdomain is required for each workload. * One-time configuration. * Works for all current and future workloads in a GVC. **Example URL endpoints:** * If the domain `sub.example.com` is configured for the GVC that contains workloads named `workload_one` and `workload_two`, Control Plane will create the following subdomains and route requests to the respective workloads: * **[https://workload\_one.sub.example.com](https://workload_one.sub.example.com)** * **[https://workload\_two.sub.example.com](https://workload_two.sub.example.com)** ## DNS Modes Control Plane supports two DNS modes for domain configuration: ### CNAME Mode (`dnsMode: cname`) In CNAME mode, Control Plane will configure workloads to accept traffic for the domain but will not manage DNS records for the domain. Users configure CNAME records in their DNS provider that point to the canonical workload endpoint. **Certificate Challenge Types:** * `certChallengeType: http01`: Uses HTTP-01 challenge for certificate validation * `certChallengeType: dns01`: Uses DNS-01 challenge for certificate validation **Subdomain-Based Routing in CNAME Mode** When a `gvcLink` is configured, each workload in the GVC is served at `.`. Create one **CNAME** record per workload pointing `.` to `.cpln.app`. The exact records to create are listed in the domain's [`status.dnsConfig`](#dns-configuration-records). Certificate handling depends on `certChallengeType`: * `dns01`: Control Plane issues a **wildcard** certificate covering all subdomains. Also create the `_acme-challenge` CNAME record shown in `status.dnsConfig` to delegate the challenge to Control Plane. * `http01` (or unset): Control Plane does not issue a certificate for subdomain-based routing — configure a [custom server certificate](#custom-server-certificate) on every port that enables TLS. **Constraints:** * A CNAME domain cannot be created as a subdomain of a domain that is already configured in NS mode. ### NS Mode (`dnsMode: ns`) In NS mode, Control Plane manages subdomains and automatically creates the required DNS records. End users configure an NS record to forward DNS requests to the Control Plane managed DNS servers. HTTP-01 challenge type is not supported for NS mode domains. ## Certificate Challenge Type The `certChallengeType` property controls both the certificate validation method and whether a wildcard certificate is generated: * **`dns01`**: Uses DNS-01 challenge and generates a wildcard certificate * **`http01`**: Uses HTTP-01 challenge and generates a standard certificate for just the domain name (not supported for NS mode) * **unset**: Defaults to wildcard certificate for NS domains, standard certificate for CNAME domains ## Apex Domain Considerations * An apex domain (e.g., **example.com**) can serve requests to workloads using either [path-based](#path-based-routing) or [subdomain-based](#subdomain-based-routing) routing. * If subdomain-based routing is desired, set `certChallengeType: dns01` for a Control Plane-managed wildcard certificate, or configure a [custom server certificate](#custom-server-certificate) and use `http01`. * If your DNS provider does not allow apex domains to point to a CNAME record, a service such as [CloudFront](https://docs.aws.amazon.com/cloudfront/index.html) or [Cloudflare](https://cloudflare.com) must be used to proxy the apex domain to Control Plane. * Apex domains can only use CNAME mode (`dnsMode: cname`). These DNS providers **do not** allow apex domains to have a CNAME record: * GoDaddy * Route53 ## Internal Domains A domain whose name ends in `.internal` (for example, `example.internal`) is a private domain. Control Plane configures its workloads to accept and route traffic for the domain but does **not** manage or publish any public DNS for it — you are responsible for resolving the domain in your own environment. **Resolution:** Point your own (private or split-horizon) DNS at the Control Plane endpoint with a CNAME record. On [BYOK](/byok) locations, the CNAME can resolve to a private address if you prefer to keep traffic off the public internet. **Constraints:** * Only CNAME mode is supported (`dnsMode: cname`, which is also the default). * Subdomain-based routing (`gvcLink`) is not supported. Route to workloads using [path-based](#path-based-routing) or [regex-based](#regex-based-routing) routes. * `certChallengeType` is not supported. Control Plane cannot issue a certificate for a private domain, so you must supply your own via `tls.serverCertificate.secretLink` on every port that enables TLS. * An apex `.internal` domain and all of its subdomains can be used by only one Org. **Example:** ```yaml YAML theme={null} kind: domain name: example.internal description: example.internal spec: dnsMode: cname ports: - number: 443 tls: serverCertificate: secretLink: //secret/TLS_SECRET routes: - prefix: / workloadLink: //gvc/GVC_NAME/workload/WORKLOAD_NAME ``` ## Port Configuration In the Console UI, the port configuration section is available when editing a domain in `Advanced Mode`. ### External Port The `number` property specifies the external port number. Typically, TLS requests to a configured domain are served on the standard TLS port 443. **Default Configuration:** * Port number: `443` (default) * Protocol: `http2` (default) See the [dedicated load balancer](#dedicated-load-balancer-options) section for the external ports that can be used when dedicated load balancing is enabled. ### Protocol The `protocol` property specifies the protocol used for the port. The following protocols are supported: 1. **`http2`** (default) 2. **`http`** 3. **`tcp`** (when using a [dedicated load balancer](#dedicated-load-balancer-options)) ### TLS Settings The default TLS protocol version is **`minProtocolVersion: TLSV1_2`**. The minimum TLS protocol version is **`TLSV1_0`**. The minimum TLS version should be configured as high as compatibility requirements allow, with a maximum supported value of **`TLSV1_3`**. All modern browsers support the default of **`TLSV1_2`**. Supported TLS protocol versions: * **`TLSV1_3`** (TLS 1.3) * **`TLSV1_2`** (TLS 1.2) - Default * **`TLSV1_1`** (TLS 1.1) * **`TLSV1_0`** (TLS 1.0) #### Cipher Suites The `cipherSuites` property configures the allowed cipher suites for TLS connections. The following cipher suites are added by default and can be removed or re-added: 1. ECDHE-ECDSA-AES256-GCM-SHA384 2. ECDHE-ECDSA-CHACHA20-POLY1305 3. ECDHE-ECDSA-AES128-GCM-SHA256 4. ECDHE-RSA-AES256-GCM-SHA384 5. ECDHE-RSA-CHACHA20-POLY1305 6. ECDHE-RSA-AES128-GCM-SHA256 7. AES256-GCM-SHA384 8. AES128-GCM-SHA256 Additional ciphers that can be added: 1. DES-CBC3-SHA 2. ECDHE-RSA-AES128-SHA 3. ECDHE-RSA-AES256-SHA 4. AES128-SHA 5. AES256-SHA If you need additional cipher suites, contact support on Slack or email [support@controlplane.com](mailto:support@controlplane.com). If there is an attempt to disable the TLS settings on a domain, it will always revert to the default configuration unless the domain is using a non-standard port and a non-HTTP protocol (e.g., using TCP with a [dedicated load balancer](#dedicated-load-balancer-options)). #### Client Certificate Forwarding The `clientCertificate` property configures client certificate forwarding for TLS connections. Client certificates included in a request to a domain can be configured to be forwarded to the destination [workload](/concepts/workload). The `x-forwarded-client-cert` (XFCC) HTTP header will contain the client certificate details. The certificate authority PEM, stored as a [secret](/reference/secret) of type `keypair`, can be associated with the domain through the `secretLink` property and used to verify the authority of the client certificate. The secret must contain a client certificate authority certificate in PEM format used to verify requests that include client certificates. The key subject must match the domain and the key usage properties must be configured for client certificate authorization. If a certificate authority PEM is not associated with a domain, no verification is performed. By default, supplying a client certificate is optional — a request without one is still served, and any certificate presented is forwarded in the XFCC header. To **require** a valid client certificate, set the [`cpln/clientCertificateValidation`](#special-tags) tag to `enabled` on the domain. Control Plane then verifies each client certificate against the certificate authority configured in `clientCertificate.secretLink` and rejects any connection that does not present a valid, trusted certificate. A `clientCertificate.secretLink` must be configured for this to take effect. CRL lists are not verified, but they can be checked by the [workload](/concepts/workload) by keeping a list of allowed or revoked client certificate hashes. When a request is received by the [workload](/concepts/workload), the hash field in the XFCC header can be checked against the allowed or revoked list and an allow or deny decision can be made. To generate the certificate hash, execute the following command: ```bash theme={null} openssl x509 -noout -fingerprint -sha256 -inform pem -in MyClientCert.pem | awk -F= '{print $2}' | tr -d ':' | tr '[:upper:]' '[:lower:]'` ``` Verify that the CA certificate includes the correct x509 key usage fields (critical, digitalSignature, keyEncipherment) + extendedKeyUsage = serverAuth and that the CN of the client certificate matches the domain name selected. #### Custom Server Certificate The `serverCertificate` property configures a custom server certificate for the domain. A custom server certificate can be assigned to a domain by selecting an existing [secret](/reference/secret) of type `keypair` through the `secretLink` property. This certificate is used when configuring: 1. An apex domain that is configured with [subdomain-based routing](#subdomain-based-routing). 2. A domain that is fronted by a proxy (such as Cloudflare). 3. A domain that prefers not to use a certificate generated by Control Plane. The secret must contain PEM-encoded content and be of type `keypair`. When the port number is 443 and this is not supplied, a certificate is provisioned automatically. If a custom server certificate is configured on a domain, it is the responsibility of the user to ensure that the certificate is valid and not expired. ### CORS Settings **CORS** stands for **Cross-Origin Resource Sharing**. It is a mechanism that allows web browsers to securely make requests to a different domain or origin than the one from which the web page was served. By default, web browsers enforce a policy called the Same-Origin Policy, which restricts JavaScript code running in a web page from making requests to a different domain. CORS provides a way to relax this policy and enable cross-origin requests, but in a controlled and secure manner. When a web page makes a cross-origin request, the server needs to include specific CORS headers in its response to indicate which origins are allowed to access its resources. These headers include information such as the allowed methods, allowed headers, and whether credentials (such as cookies or HTTP authentication) can be included in the request. CORS helps to prevent malicious scripts from performing unauthorized actions on behalf of a user, while still allowing legitimate cross-origin requests between trusted domains. It plays a crucial role in enabling modern web applications to interact with APIs and services hosted on different domains. The domain can enforce CORS settings, though the application being served can also configure these settings. The following CORS properties can be configured: * **`allowCredentials`** * Determines whether the client-side code (typically running in a web browser) is allowed to include credentials (such as cookies, HTTP authentication, or client-side SSL certificates) in cross-origin requests. * **`maxAge`** * Maximum amount of time that a preflight request result can be cached by the client browser. Must match the regex pattern: `^[\d\.]+[wdhm]+$` (e.g., "24h", "1d", "3600s") * **`allowOrigins`** * Determines which origins are allowed to access a particular resource on a server from a web browser. * Each origin can be specified as either: * `exact`: A specific origin string (e.g., `https://example.com`) * `regex`: A regex pattern to match origins * Examples: * Wildcard Origin: `*` * Specific Origin: `https://example.com` * **`allowMethods`** * Specifies the HTTP methods (such as GET, POST, PUT, DELETE, etc.) that are allowed for a cross-origin request to a specific resource. * **`allowHeaders`** * Specifies the custom HTTP headers that are allowed in a cross-origin request to a specific resource. Header names are automatically converted to lowercase. * **`exposeHeaders`** * Specifies which response headers are exposed to the client-side code (typically running in a web browser) in a cross-origin request. Header names are automatically converted to lowercase. ## Dedicated Load Balancer Options When a GVC has the dedicated load balancer option enabled, additional settings are available for any domains using it. See the [GVC dedicated load balancer](/reference/gvc#dedicated-load-balancer) reference page for more information. ### NumTrustedProxies The `numTrustedProxies` property gives control over the number of trusted proxies that are configured in front of Control Plane. Changing this value controls the source IP address used for request logging, firewall settings, and for the X-Envoy-External-Address header passed to workloads. If set to 1, then the last address in an existing X-Forwarded-For header will be used in place of the source client IP address. If set to 2, then the second-to-last address in an existing X-Forwarded-For header will be used in place of the source client IP address. When set to 2, any request where the XFF header does not have at least two addresses or does not exist uses the source client IP address instead. ### Custom Ports Any custom port number can be used with the exception of the following excluded ports: * 8012 * 8022 * 9090 * 9091 * 15000 * 15001 * 15006 * 15020 * 15021 * 15090 * 41000 Contact support on Slack or email [support@controlplane.com](mailto:support@controlplane.com) if you need to use a port that is not listed here. ### Dedicated Load Balancer Configuration Options When a dedicated load balancer is enabled on a GVC, domains that serve workloads within that GVC can use the following options: 1. [`acceptAllHosts`](#acceptallhosts) 2. [`acceptAllSubdomains`](#acceptallsubdomains) 3. [`hostPrefix`](#hostprefix) 4. [`hostRegex`](#hostregex) #### acceptAllHosts The `acceptAllHosts` property configures the domain to allow all traffic (i.e. wildcard support) to the configured workloads, regardless of the Host header or SNI in the request. At most one domain should be configured with this setting for a single GVC. If there are multiple, requests will only route for one of the domains. #### acceptAllSubdomains The `acceptAllSubdomains` property configures the domain to allow all traffic to `*.${domain}` for the domain. No other domains in the same GVC should be configured for any subdomains of this domain because their routing rules will be ignored. #### hostPrefix For domains using [path-based routing](#path-based-routing), the `hostPrefix` property will be enabled and can be used for each path when the domain has the `acceptAllHosts` or `acceptAllSubdomains` property enabled. This option allows forwarding traffic for different host header prefixes to specific workloads. The host prefix must match the regex pattern: `^[0-9a-zA-Z-\._]*$` You cannot use both `hostPrefix` and `hostRegex` on the same route. #### hostRegex For domains using [path-based routing](#path-based-routing), the `hostRegex` property will be enabled and can be used for each path when the domain has the `acceptAllHosts` or `acceptAllSubdomains` property enabled. This option allows forwarding traffic for different host headers to specific workloads by regular expression. You cannot use both `hostPrefix` and `hostRegex` on the same route. ## Replica Direct Routing Domains can be configured, in conjunction with a `stateful` workload, for replica-direct endpoints. These endpoints route traffic directly to a specific workload replica. Control Plane also provides built-in per-replica endpoints under `controlplane.us` when `spec.loadBalancer.replicaDirect` is enabled on the workload — no custom domain required. See [Replica-Direct Endpoints](/reference/workload/general#replica-direct-endpoints) in the workload reference. ### Configuration To configure a domain for replica-direct routing, you need to set the `workloadLink` property on the domain to the link of the workload. The domain must also use `dnsMode: cname` with the `dns01` certificate challenge type. The domain must define at least one port, and each port must contain exactly one route. **Validation Rules:** * When `workloadLink` is configured, `certChallengeType` cannot be `http01` * Every port must have exactly one route * All routes must reference the same workload as the domain's `workloadLink` * The domain cannot have both `gvcLink` and `workloadLink` configured #### Example ```yaml theme={null} kind: domain name: my-domain spec: certChallengeType: dns01 dnsMode: cname workloadLink: //gvc/my-gvc/workload/my-workload ports: - number: 443 protocol: http2 routes: - port: 8080 prefix: / workloadLink: //gvc/my-gvc/workload/my-workload # must match the top-level workloadLink ``` ### DNS names When a domain is configured for replica-direct routing, the DNS names for each replica will be: `--.` #### Example `my-workload-0-aws-us-west-2.example.com` ## DNS records Replica direct domains require specific DNS records in order to work correctly. 1. One **CNAME** record to redirect the `dns01` certificate challenge to Control Plane | Host | TTL | Type | Value | | :--------------- | :-- | :---- | :------------------------ | | \_acme-challenge | 300 | CNAME | \_acme-challenge.cpln.app | 2. A **CNAME** record for each replica of the workload to redirect traffic to Control Plane | Host | TTL | Type | Value | | :-------------------------- | :-- | :---- | :------------------------------------------------------ | | my-workload-0-aws-us-west-2 | 300 | CNAME | my-workload-\-0.aws-us-west-2.controlplane.us | | my-workload-1-aws-us-west-2 | 300 | CNAME | my-workload-\-1.aws-us-west-2.controlplane.us | | my-workload-2-aws-us-west-2 | 300 | CNAME | my-workload-\-2.aws-us-west-2.controlplane.us | ## Permissions The permissions below are used to define [policies](/reference/policy) together with one or more of the four [principal types](/concepts/access-control): | Permission | Description | Implies | | :--------- | :------------------------------------------------- | :-------------------------------------- | | create | Create new domain | | | delete | Delete a domain | | | edit | Modify existing domains (only tags can be changed) | view, use | | manage | Full access | create, delete, edit, manage, use, view | | use | Allow a principal to use this domain | view | | view | Read-only access | | ## Access Report Displays the permissions granted to principals for the domain. ## Domain Status The domain status field indicates the current state of the domain configuration and deployment. **Supported Status Values:** * `initializing`: Domain is being set up * `ready`: Domain is fully configured * `pendingDnsConfig`: Waiting for DNS configuration to be verified, this may happen if DNS is updated but has not yet propagated * `pendingCertificate`: Waiting for certificate provisioning * `usedByGvc`: Domain is being used by a GVC with legacy configuration * `warning`: Domain has configuration warnings * `errored`: Domain configuration has errors **Status Fields:** * `endpoints`: Array of endpoint URLs and their associated workload links * `warning`: Warning message describing any issues * `locations`: Array of location-specific certificate status information * `fingerprint`: Unique identifier for the domain configuration * `dnsConfig`: Array of DNS configuration records required for the domain ### DNS Configuration Records The `dnsConfig` field contains the DNS records that must be configured for the domain to function properly. **DNS Record Structure:** * `type`: DNS record type (e.g., "CNAME", "NS", "TXT") * `ttl`: Time-to-live value (positive integer) * `host`: Hostname for the DNS record * `value`: DNS record value **Example DNS Configuration:** ```json theme={null} { "dnsConfig": [ { "type": "CNAME", "ttl": 300, "host": "sub.example.com", "value": "cpln.app" }, { "type": "TXT", "ttl": 300, "host": "_acme-challenge.sub.example.com", "value": "challenge-token-value" } ] } ``` ## Special Tags Control Plane supports special tags prefixed with `cpln/` that modify domain behavior. | Tag | Value | Description | | :--------------------------------- | :-------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `cpln/skipDNSCheck` | `true` | Skip DNS validation checks during domain configuration. Useful when DNS propagation is delayed or when using external DNS management. | | `cpln/wildcard` | `true` | Enable wildcard certificate for custom ingress domains. Only applicable for domains with custom ingress enabled. | | `cpln/clientCertificateValidation` | `enabled` | Require a valid client certificate (mTLS) on the domain. Connections must present a certificate that is verified against the CA in `tls.clientCertificate.secretLink`; requests without a valid, trusted certificate are rejected. When unset, client certificates are optional and accepted without verification. | ## CLI To view the CLI documentation for domains, see the [Domain CLI reference](/cli-reference/commands/domain). ## Domain Certificate Management Control Plane automatically handles the creation and renewal of TLS certificates for domains. This ensures that your domains remain secured with up-to-date certificates without requiring manual intervention. The certificates issued are valid for 90 days and refresh every 60 days. ### Certificate Creation and Renewal Process Control Plane uses the Let's Encrypt [ACME](https://letsencrypt.org/docs/acme-protocol-updates/) protocol to automatically create and renew certificates for domains. Any certificates issued by Let's Encrypt are published to the [Certificate Transparency](https://www.certificate-transparency.org/) logs and discoverable by the public. If you prefer to use a different certificate authority, you can configure a [custom server certificate](#custom-server-certificate) on the domain. * **NS domains**: Control Plane uses the Let's Encrypt [DNS-01](https://letsencrypt.org/docs/challenge-types/#dns-01-challenge) verification process. This involves creating a DNS TXT record at `_acme-challenge.${domain}` to prove domain ownership, allowing Let's Encrypt to issue a certificate. Control Plane manages the creation of the DNS record, and the certificate is issued by Let's Encrypt. See [Certificate Challenge Type](#certificate-challenge-type) for details on wildcard vs standard certificate behavior. * **CNAME domains**: Control Plane uses the Let's Encrypt [HTTP-01](https://letsencrypt.org/docs/challenge-types/#http-01-challenge) verification process. This involves serving a specific HTTP resource to prove domain ownership at `http://${domain}/.well-known/acme-challenge/`, allowing Let's Encrypt to issue a certificate. Control Plane configures the domain with a redirect to our HTTP-01 solver, and then the certificate is issued by Let's Encrypt. Wildcard certificates cannot be created using the HTTP-01 verification process. To attach a CNAME domain to a GVC for [subdomain-based routing](#subdomain-based-routing) (`gvcLink`) with a Control Plane-managed certificate, set `certChallengeType: dns01` so a wildcard certificate is issued via [DNS-01](https://letsencrypt.org/docs/challenge-types/#dns-01-challenge) — this requires adding the `_acme-challenge` CNAME record shown in `status.dnsConfig`. Otherwise, configure a custom server certificate. If you choose to use a custom server certificate, ensure you monitor its expiration and renew it as necessary to avoid service disruptions. ## Domain Validation Rules The following validation rules apply to domain configuration: ### Route Limits * Maximum of 150 routes per domain * Routes must be unique (no duplicate prefix/regex and host combinations) * All routes must reference workloads within the same GVC ### Port Configuration Limits * Maximum of 10 ports per domain * Each port can have a maximum of 150 routes ### DNS Mode Constraints * Apex domains can only use CNAME mode (`dnsMode: cname`) * NS mode requires DNS-01 certificate challenge type * CNAME mode with subdomain routing requires custom server certificate ### Workload Link Constraints * Cannot configure both `gvcLink` and `workloadLink` on the same domain * When `workloadLink` is configured: * Cannot use HTTP-01 certificate challenge type * Every port must have exactly one route * All routes must reference the same workload ### Wildcard Support Constraints * Cannot enable both `acceptAllHosts` and `acceptAllSubdomains` simultaneously * `hostPrefix` and `hostRegex` can only be used when `acceptAllHosts` or `acceptAllSubdomains` is enabled ## Domain Configuration Errors During the domain configuration process, several errors might occur. These errors are viewable in the UI/API as a result of the domain status being modified and are also available in Grafana as the `domain_warning` metric. A default alert is also created to notify the default contact when any domain is in a warning state. Below are some common issues and their implications: ### Common Errors * **Certificate Renewal Blocked**: The domain's certificate renewal is blocked. This can occur if the domain is using a CNAME DNS mode and the renewal is blocked due to firewall or external CDN/WAF configurations. To test if the request from Let's Encrypt is being blocked, you can use the following curl command: ```bash theme={null} curl -v https://${domain}/.well-known/acme-challenge/ ``` If the request is blocked, you will see an error message in the response, the request will hang, or the `location:` in the response will not be targeted to `http01-solver.cpln.io`. Example of an **invalid** redirect to HTTPS: ```bash theme={null} < HTTP/1.1 301 Moved Permanently < location: https://${domain}/.well-known/acme-challenge/test ``` Example of a **valid** redirect to the Control Plane solver: ```bash theme={null} < HTTP/1.1 301 Moved Permanently < location: http://http01-solver.cpln.io/.well-known/acme-challenge/test ``` As another test, you can run the following curl which includes the `-L` flag to follow the redirect: ```bash theme={null} curl -L https://${domain}/.well-known/acme-challenge/ ``` The response should be an error message like the following, indicating that the request made it to our certificate solver: ```bash theme={null} {"message":"error downloading file: NoSuchKey: The specified key does not exist.\n\tstatus code: 404, request id: REDACTED, host id: REDACTED"} ``` Any other response indicates that the request was blocked. * **Domain Used by GVC**: This domain is currently in use by a GVC using the legacy configuration of the GVC `spec.domain` property. The domain configuration will be ignored until no GVC is using it. This results in a `usedByGvc` status with a warning message indicating the domain's current usage. * **Invalid GVC**: At least one of the configured routes does not map to a valid GVC. Check the routes and verify that the GVC and workloads targeted by the domain or routes exist. * **No Valid Routes Configured**: No valid routes are configured for the domain. Routes need to be added or updated for the domain to be usable. * **Disallowed Port or Protocol**: A port other than 443 or 80 is used, or the protocol is set to TCP and the GVC assigned is not configured with a [dedicated load balancer](#dedicated-load-balancer-options). If you need to use a port other than 443 or 80, you can enable [dedicated load balancer](#dedicated-load-balancer-options) on the GVC. * **Invalid Workload**: A route is not mapped to a valid workload and will not be included in the configuration. This requires updating the route to map to a valid workload or removing the route that is no longer needed. * **HostPrefix Ignored**: If the GVC does not have [dedicated load balancer](#dedicated-load-balancer-options) enabled and the domain is not configured for wildcard support, any hostPrefix for a route is ignored. ### Logging and Monitoring Errors and warnings are exposed through domain status fields and displayed in the UI. A Grafana metric of `domain_warning` is updated for each domain. An alarm is configured to notify the default contact using this metric when any domain is in a warning state. # Group Source: https://docs.controlplane.com/reference/group Groups organize users and service accounts for access control. Supports direct membership assignment and dynamic query-based membership rules. ## Overview A group is a membership collection that can contain [users](/reference/user) and [service accounts](/reference/serviceaccount). It is one of the [principal types](/concepts/access-control) of an [org](/reference/org). Membership in a group for a user account can be assigned directly or dynamically using a query based on a [tag](/core/misc#tags) (key/value pair) that has been labeled on a user. Membership in a group for a [service account](/reference/serviceaccount) can only be assigned directly. Groups can be used by [policies](/reference/policy) to grant access permissions to the group members. ## Create a Group Refer to the [Create a Group](/guides/create-group) guide for additional details. ## Built-in Groups Each [org](/reference/org) has the following built-in groups: | Group Name | Description | | :--------- | :-------------------------------------------------------- | | superusers | Built-in group for all administrators of the organization | | viewers | Built-in group for read-only access | ## Group Notes Groups can contain an unlimited amount of [users](/reference/user) or [service accounts](/reference/serviceaccount). Group membership can be assigned directly or dynamically (using a [query](#query-rules) based on any [tags](/core/misc#tags) that are labeled on a [user](/reference/user)). [Service Accounts](/reference/serviceaccount) can only be assigned directly. For example, a [query](#query-rules) can be created to dynamically assign all the users that log in using `microsoft.com` by using the built-in tag key `firebase/sign_in_provider` Equals `microsoft.com`. ## Query Rules To dynamically assign users to a group, a query can be defined which consists of the following: * One or more [tags](/core/misc#tags) (key/value pairs) using one of the operators: `Equals` / `Exists` / `Not Exists` * One of the following query filters: * `All`: All [tag](/core/misc#tags) items should match * `Any`: Any of the [tags](/core/misc#tags) should match * `None`: None of these [tags](/core/misc#tags) should match ## Permissions The permissions below are used to define [policies](/reference/policy) together with one or more of the four [principal types](/concepts/access-control): | Permission | Description | Implies | | :--------- | :--------------------- | :--------------------------------- | | create | Create new groups | | | delete | Delete a group | | | edit | Modify existing groups | view | | manage | Full access | create, delete, edit, manage, view | | view | Read-only view | | ## Access Report Displays the permissions granted to principals for the group. ## CLI To view the CLI documentation for groups, see the [Group CLI reference](/cli-reference/commands/group). # GVC (Global Virtual Cloud) Source: https://docs.controlplane.com/reference/gvc GVC configuration reference with YAML examples. Covers locations, pull secrets, domain assignment, environment variables, tracing, and load balancing. ## Overview Refer to the [GVC concepts](/concepts/gvc) page. ## Create a GVC Refer to the [Create a GVC](/guides/create-gvc) guide for additional details. ## GVC Configuration Example Here's a comprehensive example of a GVC configuration that includes all available options: ```yaml theme={null} kind: gvc name: my-gvc spec: staticPlacement: locationLinks: - //location/aws-us-west-2 - //location/aws-eu-west-1 locationOptions: - locationLink: //location/aws-us-west-2 routingTier: 0 - locationLink: //location/aws-eu-west-1 routingTier: 1 latencyToleranceMs: 500 pullSecretLinks: - //secret/docker-registry-secret - //secret/ecr-secret endpointNamingFormat: org tracing: provider: controlplane: {} customTags: environment: production sampling: 10 env: - name: ENVIRONMENT value: production - name: LOG_LEVEL value: info loadBalancer: dedicated: true trustedProxies: 1 redirect: class: status5xx: https://error.example.com status401: https://auth.example.com/login?return_to=%REQ(:path)% ipSet: //ipset/my-ipset keda: enabled: true identityLink: //identity/keda-identity secrets: - //secret/keda-trigger-auth-1 - //secret/keda-trigger-auth-2 ``` ## Switching between GVCs Using the console, if an [Org](/reference/org) has multiple GVCs, there will be an angle bracket `>` to the right of the current GVC name in the left menu. Click the bracket to show and select a GVC. ## GVC Namespace The GVC namespace is used when constructing the canonical endpoint and individual location endpoints to avoid naming collision between workloads with the same name in different GVCs. The namespace is also used when performing [service-to-service](/guides/service-to-service) calls. ## GVC Locations The cloud provider [locations](/reference/location) that [workloads](/concepts/workload) will be served from are mapped to a GVC. At least one location is required. The global and canonical endpoints will use DNS to route the request to the nearest healthy location. Adding a location immediately provisions it for all workloads. Removing a location gracefully terminates running replicas in that location and shifts traffic to remaining healthy locations. DNS updates propagate automatically. The available [locations](/reference/location) are scoped to an org and can be enabled/disabled globally. Any changes to the location at the org will be propagated to all GVCs using that location. ## Location Routing Options You can configure per-location routing options to control how DNS geo-routing selects locations for traffic. This enables scenarios such as primary/failover locations and fine-tuning traffic distribution across locations. Location routing options are configured in `spec.staticPlacement.locationOptions`, which is an array of objects, each specifying a location and its routing parameters. Each GVC independently controls these options, so the same location can have different routing behavior in different GVCs. ### Configuration ```yaml theme={null} spec: staticPlacement: locationLinks: - //location/aws-us-west-2 - //location/aws-eu-west-1 - //location/aws-ap-southeast-1 locationOptions: - locationLink: //location/aws-us-west-2 routingTier: 0 - locationLink: //location/aws-eu-west-1 routingTier: 0 latencyOffsetMs: -5 - locationLink: //location/aws-ap-southeast-1 routingTier: 1 latencyToleranceMs: 300 ``` ### Location Options Properties * **`locationLink`** (required): A link to the location this option applies to. Must use the same link format as `locationLinks` (e.g., `//location/aws-us-west-2`). Must be unique across all items in the `locationOptions` array. * **`routingTier`** (optional): An integer (minimum 0) that controls location priority for DNS geo-routing. Lower values indicate higher priority. Locations with the same priority form a group. Within a group, the location with the lowest measured latency is selected. If all locations in the highest-priority group are unavailable, the next priority group is used, and so on. Locations without an explicit priority are treated as lowest priority when any other location has an explicit priority set. * **`latencyOffsetMs`** (optional): An integer in milliseconds that is added to the measured latency for this location. Positive values make the location appear farther away (pushing traffic to other locations), while negative values make it appear closer (attracting more traffic). This can be used to fine-tune traffic distribution percentages across locations. Default: 0. * **`latencyToleranceMs`** (optional): An integer in milliseconds. If the measured latency to a location exceeds this threshold, the location is treated as unavailable for DNS geo-routing. This can be used to ensure that traffic is only routed to locations that meet a performance requirement. If all locations are filtered out by their latency thresholds, the thresholds are ignored and all locations remain candidates. This safety mechanism ensures that traffic is never left without a destination. ### Priority-Based Failover Example Configure primary and failover locations so that traffic only goes to the failover when all primary locations are down: ```yaml theme={null} spec: staticPlacement: locationLinks: - //location/aws-us-east-1 - //location/aws-us-west-2 - //location/aws-eu-west-1 locationOptions: - locationLink: //location/aws-us-east-1 routingTier: 0 - locationLink: //location/aws-us-west-2 routingTier: 0 - locationLink: //location/aws-eu-west-1 routingTier: 1 ``` In this configuration, `aws-us-east-1` and `aws-us-west-2` are primary locations (priority 0). Traffic is routed to whichever has the lowest latency. If both primary locations become unavailable, traffic fails over to `aws-eu-west-1` (priority 1). ### Latency Tuning Example Use `latencyOffsetMs` to shift traffic distribution without changing priority: ```yaml theme={null} spec: staticPlacement: locationLinks: - //location/aws-us-east-1 - //location/aws-us-west-2 locationOptions: - locationLink: //location/aws-us-east-1 latencyOffsetMs: -10 - locationLink: //location/aws-us-west-2 latencyOffsetMs: 10 ``` This configuration makes `aws-us-east-1` appear 10ms closer and `aws-us-west-2` appear 10ms farther, shifting more traffic toward the east coast location. ## Alias Endpoint Health Awareness Every GVC has a global alias endpoint, `${alias}.cpln.app`, that DNS geo-routes to the nearest healthy location serving the GVC. By default this DNS record resolves directly to each location that is configured for the GVC and is health-checked with a basic TCP probe. This TCP probe will succeed even when no workload in the GVC is actually able to serve traffic in that location, so the DNS can advertise locations that are unable to serve requests. Set `spec.aliasWorkloadLink` to a workload in this GVC to make the alias inherit that workload's health and routing instead. When set, `${alias}.cpln.app` is published as a CNAME to the referenced workload's canonical endpoint, so the alias inherits the workload's HTTP readiness probes and per-location geo failover. A location then only receives alias traffic while the workload is actually healthy there. ### Configuration ```yaml theme={null} spec: aliasWorkloadLink: //workload/api-gateway ``` ### Behavior * **When set**: `${alias}.cpln.app` is a CNAME to the referenced workload's canonical endpoint and follows that workload's HTTP health checks and location routing. * **When unset**: the alias resolves to cluster ingress endpoints with a TCP-only health check and no application-level health awareness. * The referenced workload must be in the same GVC. `aliasWorkloadLink` has no effect if the referenced workload does not exist, or while it is globally suspended (`defaultOptions.suspend` or `defaultOptions.autoscaling.maxScale` set to `0`); in these cases the alias falls back to ingress resolution. Per-location suspends are ignored. ## Load Balancer Configuration The `loadBalancer` property allows you to configure load balancing settings for the GVC. ### Dedicated Load Balancer When a dedicated load balancer is enabled on a GVC: * All inbound traffic is routed through a custom cloud load balancer for each enabled location * Additional charges apply per location * Any [domains](/reference/domain) configured to route traffic to this GVC will also leverage the custom load balancer * The following additional [domain](/reference/domain#dedicated-load-balancer-options) features become available: * [Custom Ports](/reference/domain#custom-ports): Route TCP traffic on a variety of ports * [Accept All Hosts](/reference/domain#accept-all-hosts): Accept traffic for any hostname Enabling or disabling the dedicated load balancer can cause a brief period of connectivity failure during DNS propagation. ### Load Balancer Properties * **`dedicated`**: Creates a dedicated load balancer in each location and enables additional Domain features: custom ports, protocols and wildcard hostnames. Charges apply for each location. * **`multiZone.enabled`**: Enable/Disable multi-zone load balancing. Cross zone charges apply. * **`trustedProxies`**: Controls the address used for request logging and for setting the X-Envoy-External-Address header. Valid values are 0, 1, or 2. * `0` (default): Use the source client IP address * `1`: Use the last address in an existing X-Forwarded-For header * `2`: Use the second to last address in an existing X-Forwarded-For header * **`ipSet`**: A link to an [IP Set](/reference/ipset) that reserves a static public IP per location for this load balancer. ### Load Balancer Access Logs You can view access logs for the load balancer by navigating to the logs page in the console and filtering by `{gvc="",workload="_loadbalancer"}`. ### Redirect Configuration When a dedicated load balancer is enabled on a GVC, you can specify URLs to redirect users for different HTTP status codes. ```yaml theme={null} kind: gvc name: example-gvc spec: loadBalancer: dedicated: true redirect: class: status5xx: https://redirect.example.com status401: https://your-oauth-server/oauth2/authorize?return_to=%REQ(:path)%&client_id=your-client-id ``` ```json theme={null} { "kind": "gvc", "name": "example-gvc", "spec": { "loadBalancer": { "dedicated": true, "redirect": { "class": { "status5xx": "https://redirect.example.com", "status401": "https://your-oauth-server/oauth2/authorize?return_to=%REQ(:path)%&client_id=your-client-id" } } } } } ``` #### Redirect Properties * **`class.status5xx`**: Specify the redirect URL for any 500 level status code. Must be a valid URI. * **`class.status401`**: An optional URL redirect for 401 responses. Supports Envoy format strings to include request information (e.g., `%REQ(:path)%`). ### Static IP Addresses By default, the public IP assigned to each location is not reserved and may change. To assign a static public IP per location, link an [IP Set](/reference/ipset) to the dedicated load balancer via `loadBalancer.ipSet`. ```yaml theme={null} kind: gvc name: example-gvc spec: loadBalancer: dedicated: true ipSet: //ipset/example ``` ```json theme={null} { "kind": "gvc", "name": "example-gvc", "spec": { "loadBalancer": { "dedicated": true, "ipSet": "//ipset/example" } } } ``` The IP Set must be [configured](/reference/ipset#gvc) to reference this GVC. ## Endpoint Naming Format Workload canonical endpoints in this GVC can be prefixed with the [org endpoint prefix](/reference/org#endpoint-prefix). This adds the prefix as a subdomain to `cpln.app`. To configure which endpoint prefix to use, set the endpointNamingFormat field: ```yaml theme={null} spec: endpointNamingFormat: org # can be "org" or "default" ``` ### Canonical Endpoint Formatting * `default`: \{workloadName}-\{gvcAlias}.cpln.app * `org`: \{workloadName}-\{gvcAlias}.\{orgEndpointPrefix}.cpln.app ## Tracing OpenTelemetry traces are supported and can be configured with the native `Control Plane` tracing provider or sent to an OpenTelemetry collector endpoint by using the `OpenTelemetry` tracing provider. ### Control Plane Tracing Provider The Control Plane tracing provider is the default method for collecting OpenTelemetry traces. They will be accessible for exploration using Grafana by accessing the `Traces` link from any workload in the Console. To enable traces using the Console, navigate to your GVC, click on `Tracing`, and choose `Control Plane` as the metric provider. Then, configure the sampling percentage and, optionally, the Custom Tags. When using the Control Plane tracing provider, workloads can forward additional Open Telemetry traces to the following endpoints: GRPC: `tracing.controlplane:80` HTTP: `tracing.controlplane:4318` For each of these endpoints, mutual tls is added automatically for requests and any clients/libraries should be configured to send requests using plain text. Here is an example of a GVC with tracing enabled for 10 percent of requests to be forwarded to the Control Plane tracing provider. Additionally, the tag `team`=`profile` will also be added to each trace. ```yaml YAML theme={null} kind: gvc name: online-boutique spec: ... tracing: provider: controlplane: {} customTags: { team: profile } sampling: 10 ``` ### OpenTelemetry Tracing Provider Similarly, traces can be sent to an OTEL collector endpoint using the `OpenTelemetry` tracing provider. For details, see the [Online Boutique](https://github.com/controlplane-com/examples/blob/main/examples/online-boutique/README.md) example. ## Pull Secrets Pull secrets are [secrets](/reference/secret) that are assigned to a GVC and used by [workloads](/concepts/workload) when authentication is required when pulling an image from a private registry. Only [Docker](/reference/secret#docker), [Amazon ECR](/reference/secret#ecr), and [GCP](/reference/secret#google-cloud-platform-gcp) secrets types are supported for pull secrets. If the image was pushed to the Control Plane registry for the same [Org](/reference/org), no pull secret is required when a workload pulls from the image from the same Org. Multiple pull secrets can be assigned to a GVC. A [workload's container](/reference/workload/containers) will use the appropriate secret when pulling the image from a private registry. If there are multiple secrets, the container will cycle through each one. If authentication fails, the deployment will not be updated and the image pull will have an exponential backoff retry starting at 10 seconds and ending at 5 minutes (e.g., 10 seconds, 20 seconds, 40 seconds, etc.). ## Environment Variables You may set environment variables at the GVC level, which can then be inherited by any of the GVC's workloads. To inherit GVC environment variables, a container must have its `inheritEnv` property set to true. For more information about how environment variables work in Control Plane, please see the environment variables section of the [workload reference](/reference/workload#environment-variables) page. ## Sticky Sessions Add the following [tags](/core/misc#tags) and desired values to a GVC to enable sticky sessions for **ALL** [Workloads](/concepts/workload) within the GVC: 1. `cpln/sessionCookie` * The name of the session cookie. 2. `cpln/sessionDuration` * The Golang duration for the maximum session length (e.g., 300s, 30m, etc.). * Review this [link](https://pkg.go.dev/time#ParseDuration) for the proper Golang duration string format. Once these tags are set, soft session affinity based on a cookie will consistently route requests to the same replica. The affinity to a particular replica will be lost if the replica restarts. ### How to add Tags using the UI 1. Browse to the [Console UI](https://console.cpln.io/) and select the desired GVC. 2. Click the `Tags` link in the middle context menu. 3. Click `Edit Tags`. 4. Enter the string `cpln/sessionCookie` in the `Tag Key` text box and enter the desired cookie name in the `Tag Value` text box. Click `Add Tag`. 5. Enter the string `cpln/sessionDuration` in the `Tag Key` text box and enter the desired duration in the `Tag Value` text box. Click `Add Tag`. 6. Click `Save`. ### How to add Tags using the CLI Execute the following CLI command (substitute the GVC\_NAME, ORG\_NAME, COOKIE\_NAME, DURATION tokens) to add the requires Tags to a GVC: ```bash theme={null} cpln gvc tag GVC_NAME --tag cpln/sessionCookie=COOKIE_NAME --tag cpln/sessionDuration=DURATION --org ORG_NAME ``` ## Keda In order to use Keda to scale workloads, you must first enable it on the GVC. If the keda deployment requires network or cloud resources, you must attach a valid identity on the keda configuration here. ```yaml theme={null} spec: keda: enabled: true identityLink: //identity/keda-identity # optional secrets: # optional - //secret/keda-secret-1 - //secret/keda-secret-2 ``` ### Keda Configuration * **`enabled`**: Enable KEDA for this GVC. KEDA is a Kubernetes-based event-driven autoscaler that allows you to scale workloads based on external events. When enabled, a keda operator will be deployed in the GVC and workloads in the GVC can use KEDA to scale based on external metrics. * **`identityLink`**: A link to an Identity resource that will be used for KEDA. This will allow the keda operator to access cloud and network resources. * **`secrets`**: A list of secrets to be used as TriggerAuthentication objects. The TriggerAuthentication object will be named after the secret and can be used by triggers on workloads in this GVC. ## Export GVC Using the console UI, when a GVC is selected, an `Export GVC` link is available which will save (as a local multi-document YAML manifest file) the GVC and all associated resources ([Identities](/reference/identity), [Volume Sets](/reference/volumeset) and [Workloads](/concepts/workload)). Links to other resources are relative within the exported file. This allows the file to be easily used to backup and restore an entire GVC. It can also be used when promoting to other [Orgs](/reference/org). The export doesn't contain any referenced [Org](/reference/org) resources, such as, [Secrets](/reference/secret), [Cloud Accounts](/reference/cloudaccount), and [Agents](/reference/agent). These resources would need to be exported separately. ## Permissions The permissions below are used to define [policies](/reference/policy) together with one or more of the four [principal types](/concepts/access-control): | Permission | Description | Implies | | :--------- | :------------------- | :--------------------------------- | | create | Create new gvcs | | | delete | Delete existing gvcs | | | edit | Modify existing gvcs | view | | manage | Full access | create, delete, edit, manage, view | | view | Read-only access | | ## Access Report Displays the permissions granted to principals for the GVC. ## CLI To view the CLI documentation for GVCs, see the [GVC CLI reference](/cli-reference/commands/gvc). # Identity Source: https://docs.controlplane.com/reference/identity Workload identities for credential-free cloud provider access via Universal Cloud Identity. Covers AWS, GCP, Azure and network resources. ## Overview **Identity** (also known as a workload identity) is one of the four Control Plane [principal types](/concepts/access-control) (users, service accounts, groups, and identities). A [workload](/concepts/workload) needing to consume cloud resources from one or more cloud providers (e.g., AWS, Azure, and GCP) without storing credentials or needing to communicate to endpoints within a private network - must be assigned an **identity**. An **identity** is a named object that allows an authorized administrator to define: ### [Cloud Provider Access (Universal Cloud Identity)](/reference/identity#cloud-provider-access) * Least-privilege access rules allowed on cloud resources across cloud providers. Each identity can reference only one cloud account per provider. For example, you can have one account in Azure and one account in GCP, but not two different accounts in GCP within the same identity. You define access policies granted to the identity for each cloud provider you need to use (one, two, or all three clouds, depending on your workload requirements). ### [Private Network Access (Cloud Wormhole)](/reference/identity#network-resources-networkresources) * Network traversal rules from [workloads](/concepts/workload) into specific endpoints in private networks (e.g., a VPC). These rules connect an [agent](/reference/agent) in a private network to the Control Plane infrastructure, allowing [workloads](/concepts/workload) to selectively access TCP endpoints inside private networks where [agents](/reference/agent) are installed and running. ### [Native Cloud Network Access](/reference/identity#native-network-resources-nativenetworkresources) * Provides secure, private connectivity from a [workload](/concepts/workload) to cloud-hosted services across AWS (vpc endpoints) and GCP (private service connect) without traversing the public internet. It leverages cloud provider-specific private networking services to route traffic over internal infrastructure. This enables low-latency, secure communication to private resources while maintaining strict network isolation and identity-based access control. An **identity** is scoped to a [GVC](/reference/gvc) and can be assigned to multiple [workloads](/concepts/workload) within the same GVC that needs the same cloud resources and network access. Identities cannot be shared across GVCs. Different GVCs can have identities with the same name; they are independent resources. A [workload](/concepts/workload) can be assigned exactly one **identity**. An **identity** is only required when a [workload](/concepts/workload) needs to consume cloud resources without embedding credentials and/or when a [workload](/concepts/workload) needs to consume resources in a private network such as a VPC. If neither is required, a [workload](/concepts/workload) can operate **without** assigning it an **identity**. Once configured, an **identity** assigned to a [workload](/concepts/workload) enables it to: * Access specific resources of AWS, Azure, and/or GCP * Access private network resources from external cloud accounts, data centers or any other private network. (Tunneling network traffic from [workloads](/concepts/workload) to specific TCP hosts and ports is facilitated using [agents](/reference/agent). This capability is referred to as “wormholes”.) * Access the NATS NGS service using scoped credentials automatically supplied at runtime Identities are powerful Control Plane resources that can be granted any permission to your cloud environment. The ability to create identities should only be given to administrators by using a [policy](/reference/policy). ## Create an Identity Refer to the [Create an Identity](/guides/create-identity) guide for additional details. ## Identity Specification An identity can be configured with the following fields: ## Cloud Provider Access ### AWS Identity (`aws`) | Field | Type | Required | Description | | ------------------ | ------ | -------- | ------------------------------------- | | `cloudAccountLink` | string | Yes | Link to the AWS cloud account | | `roleName` | string | No\* | AWS role name to reuse (max 64 chars) | | `policyRefs` | array | No\* | Array of policy references | | `trustPolicy` | object | No | AWS trust policy document | \*Either `roleName` OR `policyRefs` is required, but not both. **Trust Policy Structure:** ```yaml theme={null} trustPolicy: Version: "2012-10-17" # Optional, defaults to "2012-10-17" Statement: - # Policy statement objects ``` ### GCP Identity (`gcp`) | Field | Type | Required | Description | | ------------------ | ------ | -------- | ------------------------------------------------------------------------------- | | `cloudAccountLink` | string | Yes | Link to the GCP cloud account | | `serviceAccount` | string | No\* | Existing service account email (must end with `.gserviceaccount.com`) | | `bindings` | array | No\* | Array of role bindings | | `scopes` | array | No | OAuth scopes (defaults to `["https://www.googleapis.com/auth/cloud-platform"]`) | \*Either `serviceAccount` OR `bindings` is required, but not both. **Binding Structure:** ```yaml theme={null} bindings: - resource: "projects/my-project" # Optional, defaults to project roles: - "roles/storage.objectViewer" - "roles/bigquery.dataViewer" ``` ### Azure Identity (`azure`) | Field | Type | Required | Description | | ------------------ | ------ | -------- | ------------------------------- | | `cloudAccountLink` | string | Yes | Link to the Azure cloud account | | `roleAssignments` | array | Yes | Array of role assignments | **Role Assignment Structure:** ```yaml theme={null} roleAssignments: - scope: "/subscriptions/123/resourceGroups/my-rg" # Optional, defaults to subscription roles: - "Reader" - "Storage Blob Data Reader" ``` ### NATS Identity (`ngs`) | Field | Type | Required | Description | | ------------------ | ------ | -------- | -------------------------------------------------------- | | `cloudAccountLink` | string | Yes | Link to the NATS cloud account | | `pub` | object | No | Publish permissions | | `sub` | object | No | Subscribe permissions | | `resp` | object | No | Response permissions | | `subs` | number | No | Max subscriptions per connection (default: -1, no limit) | | `data` | number | No | Max bytes a connection can send (default: -1, no limit) | | `payload` | number | No | Max message payload (default: -1, no limit) | **Permission Structure:** ```yaml theme={null} pub: allow: ["orders.*", "users.>"] deny: ["orders.sensitive.*"] sub: allow: ["orders.*", "users.>"] deny: ["orders.sensitive.*"] resp: max: 1 # Number of responses allowed (-1 = no limit) ttl: "30s" # Deadline format: #ms, #s, #m, #h ``` ## Private Network Access ### Network Resources (`networkResources`) | Field | Type | Required | Description | | ------------ | ------ | -------- | --------------------------------------- | | `name` | string | Yes | Resource name or domain | | `agentLink` | string | No | Link to the agent | | `IPs` | array | No\* | Array of IPv4 addresses (1-5 addresses) | | `FQDN` | string | No\* | Fully qualified domain name | | `resolverIP` | string | No | IPv4 resolver address | | `ports` | array | Yes | Array of ports (1-10 ports, 0-65535) | \*Either `IPs` OR `FQDN` is required. All agents referenced in an identity must be the same protocol version (v1 or v2). ### Native Network Resources (`nativeNetworkResources`) | Field | Type | Required | Description | | ------------------- | ------ | -------- | ------------------------------------ | | `name` | string | Yes | Resource name or domain | | `FQDN` | string | No | Fully qualified domain name | | `ports` | array | Yes | Array of ports (1-10 ports, 0-65535) | | `awsPrivateLink` | object | No\* | AWS PrivateLink configuration | | `gcpServiceConnect` | object | No\* | GCP Service Connect configuration | \*Either `awsPrivateLink` OR `gcpServiceConnect` is required. **AWS PrivateLink Structure:** ```yaml theme={null} awsPrivateLink: endpointServiceName: "com.amazonaws.vpce.us-east-1.vpce-svc-12345678" ``` **GCP Service Connect Structure:** ```yaml theme={null} gcpServiceConnect: targetService: "projects/my-project/regions/us-central1/serviceAttachments/my-service" ``` ## Status | Field | Type | Required | Description | | ------------------- | ------ | -------- | --------------------------- | | `status` | object | No | Provider status information | | `status.objectName` | string | No | Object name | | `status.aws` | object | No | AWS provider status | | `status.gcp` | object | No | GCP provider status | | `status.azure` | object | No | Azure provider status | **Provider Status Structure:** ```yaml theme={null} status: objectName: "my-identity" aws: lastError: "Error message if any" usable: true gcp: lastError: null usable: true azure: lastError: null usable: false ``` ## Additional Fields | Field | Type | Required | Description | | ----- | ------ | -------- | --------------------------------------------------- | | `gvc` | string | No | Parent GVC (used by local tools, ignored by server) | ## Example YAML Here's a comprehensive example showing all identity types and configurations: ```yaml theme={null} # Multi-cloud identity with all provider types name: "multi-cloud-workload-identity" description: "Identity for workload accessing AWS, GCP, Azure, and NATS resources" tags: environment: "production" team: "platform" # AWS Identity - using policy references aws: cloudAccountLink: "/org/my-org/cloudaccount/aws-prod" policyRefs: - "aws::arn:aws:iam::123456789012:policy/S3ReadOnly" - "aws::arn:aws:iam::123456789012:policy/DynamoDBReadOnly" trustPolicy: Version: "2012-10-17" Statement: - Effect: "Allow" Principal: Service: "ec2.amazonaws.com" Action: "sts:AssumeRole" # GCP Identity - using role bindings gcp: cloudAccountLink: "/org/my-org/cloudaccount/gcp-prod" bindings: - resource: "projects/my-gcp-project" roles: - "roles/storage.objectViewer" - "roles/bigquery.dataViewer" - "roles/pubsub.subscriber" scopes: - "https://www.googleapis.com/auth/cloud-platform" - "https://www.googleapis.com/auth/bigquery" # Azure Identity azure: cloudAccountLink: "/org/my-org/cloudaccount/azure-prod" roleAssignments: - scope: "/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/my-rg" roles: - "Reader" - "Storage Blob Data Reader" - scope: "/subscriptions/12345678-1234-1234-1234-123456789012" roles: - "Contributor" # NATS Identity ngs: cloudAccountLink: "/org/my-org/cloudaccount/nats-prod" pub: allow: ["orders.*", "users.>", "notifications.*"] deny: ["orders.sensitive.*", "users.admin.*"] sub: allow: ["orders.*", "users.>", "responses.>"] deny: ["orders.sensitive.*"] resp: max: 5 ttl: "30s" subs: 100 data: 1048576 # 1MB payload: 65536 # 64KB # Network Resources (Cloud Wormhole) networkResources: - name: "database-server" agentLink: "/org/my-org/agent/db-agent" IPs: ["10.0.1.100", "10.0.1.101"] ports: [5432, 3306] resolverIP: "10.0.1.1" - name: "api-gateway" FQDN: "api.internal.company.com" ports: [443, 8080] - name: "cache-cluster" agentLink: "/org/my-org/agent/cache-agent" IPs: ["10.0.2.50"] ports: [6379, 11211] # Native Network Resources nativeNetworkResources: - name: "aws-rds-proxy" FQDN: "rds-proxy.us-east-1.amazonaws.com" ports: [5432] awsPrivateLink: endpointServiceName: "com.amazonaws.vpce.us-east-1.vpce-svc-12345678abcdef" - name: "gcp-cloud-sql" FQDN: "my-project:us-central1:my-instance" ports: [5432] gcpServiceConnect: targetService: "projects/my-project/regions/us-central1/serviceAttachments/my-service" # Status (read-only, set by system) status: objectName: "multi-cloud-workload-identity" aws: lastError: null usable: true gcp: lastError: null usable: true azure: lastError: "Authentication failed" usable: false ``` ## Alternative AWS Identity Example (using existing role) ```yaml theme={null} # AWS Identity - reusing existing role aws: cloudAccountLink: "/org/my-org/cloudaccount/aws-prod" roleName: "MyExistingRole" ``` ## Alternative GCP Identity Example (using existing service account) ```yaml theme={null} # GCP Identity - using existing service account gcp: cloudAccountLink: "/org/my-org/cloudaccount/gcp-prod" serviceAccount: "my-service-account@my-project.iam.gserviceaccount.com" scopes: - "https://www.googleapis.com/auth/cloud-platform" ``` ## Permissions The permissions below are used to define [policies](/reference/policy) together with one or more of the four [principal types](/concepts/access-control): | Permission | Description | Implies | | :--------- | :--------------------------------------------------------- | :-------------------------------------- | | create | Create new identities | | | delete | Delete existing identities | | | edit | Modify existing identities | view | | manage | Full access | create, delete, edit, manage, use, view | | use | Refer to this identity from other entities (workload, etc) | view | | view | Read-only access | | ## Access Report Displays the permissions granted to principals for the identity. ## CLI To view the CLI documentation for Identities, see the [Identity CLI reference](/cli-reference/commands/identity). # Image Source: https://docs.controlplane.com/reference/image Container image management including private registry details, image reference formats, tagging, and support for internal and external image sources. ## Overview An image is a lightweight, standalone, executable package that includes everything needed to run an application: code, runtime, system tools, system libraries, and settings. Control Plane uses container images to deploy and run your applications as [workloads](/concepts/workload). Control Plane provides each organization with a private image registry at `your-org.registry.cpln.io`. You can also use external registries like Docker Hub, Google Container Registry, or Amazon ECR. The [CLI](/cli-reference/get-started/images) provides commands to build and push your application to your private image registry. ## Private Registry Each organization has a dedicated private registry hosted by Control Plane. **Registry URL format:** ```text theme={null} ORG_NAME.registry.cpln.io/IMAGE_NAME:TAG ``` **Benefits:** * No [pull secrets](/reference/secret) required when referencing images in workloads * Lower latency when pulling images (images are cached at each location where your workload is deployed) * Built-in access control through Control Plane policies * Automatic authentication when using the CLI ## Image Reference Formats When configuring a workload's container image, you can reference images from different sources: | Format | Description | Example | | -------------------------------- | --------------------------------------- | ----------------------------------------------------- | | `//image/IMAGE:TAG` | Image in your org's private registry | `//image/my-app:v1` | | `ORG.registry.cpln.io/IMAGE:TAG` | Explicit reference to an org's registry | `my-org.registry.cpln.io/my-app:v1` | | `IMAGE:TAG` | Public image from Docker Hub | `nginx:latest` | | `gcr.io/PROJECT/IMAGE:TAG` | Google Container Registry | `gcr.io/my-project/my-app:v1` | | `ECR_URL/IMAGE:TAG` | Amazon Elastic Container Registry | `123456789.dkr.ecr.us-east-1.amazonaws.com/my-app:v1` | Use `//image/IMAGE:TAG` for images in your org's private registry. This short format is resolved automatically and doesn't require the full registry URL. ## Internal vs External Images | Aspect | Internal (Private Registry) | External (Docker Hub, GCR, ECR, etc.) | | ----------------- | ---------------------------------------------- | ------------------------------------------ | | Authentication | Automatic via Control Plane | Requires [pull secrets](/reference/secret) | | Latency | Optimized (cached at each deployment location) | Depends on external registry location | | Access control | Control Plane policies | External registry permissions | | Build integration | `cpln image build --push` | Standard `docker push` | | Best for | Production workloads, proprietary code | Public images, shared base images | ## Building Images Control Plane supports two methods for building container images locally: ### With a Dockerfile The traditional approach using a Dockerfile to define your image. Use this when you need full control over the build process or have complex build requirements. ### With Buildpacks [Cloud Native Buildpacks](https://buildpacks.io) automatically detect your application language and create an optimized container image without a Dockerfile. Buildpacks are ideal for standard application frameworks and reduce the need to maintain Dockerfiles. For detailed language-specific requirements and conventions, see the [Buildpacks Guide](/guides/buildpacks). Both run through a local Docker daemon. ### Build Inputs A Dockerfile build accepts two kinds of input from the CLI. `--build-arg` sets a value the Dockerfile reads with `ARG`, and `--secret` mounts a credential for a single `RUN` step so it stays out of the image. A secret can come from a file on the build machine or from a secret in your org, referenced as `cpln://secret/`. See [Build Arguments](/cli-reference/get-started/images#build-arguments) and [Build Secrets](/cli-reference/get-started/images#build-secrets). ### Remote Builds `cpln image build --remote` uploads the build folder — or clones a GitHub or GitLab repository given with `--repo` — builds it remotely, and pushes the image to your org's private registry. See [Remote builds](/cli-reference/get-started/images#build-options) for details. ## Image Lifecycle 1. **Build**: Create the image with `cpln image build`, locally through Docker (Dockerfile or buildpacks) or remotely with `--remote` 2. **Push**: Upload to your org's private registry with the `--push` flag or `docker push`; a remote build pushes the image itself 3. **Reference**: Configure your workload to use the image via `//image/IMAGE:TAG` 4. **Deploy**: Control Plane pulls and runs the image across your configured locations ## Getting Started with the CLI For practical examples on building, pushing, and managing images, see [Images](/cli-reference/get-started/images) in the CLI Reference. A local `cpln image build` prefers `docker buildx build` (used by default since CLI `v3.7.2`) and falls back to legacy `docker build` when Buildx is unavailable (fallback added in CLI `v3.9.0`). Multi-platform builds (comma-separated `--platform` values) still require Buildx and will fail without it. To install the plugin, follow Docker's [Buildx installation guide](https://docs.docker.com/build/install-buildx/) or see the [Push an Image guide](/guides/push-image) (especially on CI runners). Neither Docker nor Buildx is needed with `--remote` (CLI `v3.15.0`). ## Push an Image Refer to the [Push an Image](/guides/push-image) guide for additional details. ## Pull an Image Refer to the [Pull an Image](/guides/pull-image) guide for additional details. ## Copy an Image Refer to the [Copy an Image](/guides/copy-image) guide for additional details. ## Update a Workload Image To update the image used by a workload's container: ```bash theme={null} cpln workload update WORKLOAD_NAME --set spec.containers.CONTAINER_NAME.image=//image/IMAGE:TAG --gvc GVC_NAME --org ORG_NAME ``` Use the convention `//image/IMAGE:TAG` to reference an image residing within the current org's private registry. ## Dynamic Tags Enable `supportDynamicTags` on a workload to automatically redeploy when an image tag's underlying digest changes. This is useful for: * Mutable tags like `latest` or `dev` * CI/CD pipelines that update the same tag * Automatic rollout of security patches to base images When enabled, Control Plane periodically checks if the image digest has changed and triggers a redeployment if it has. Using mutable tags in production is generally discouraged. Prefer immutable tags (e.g., `v1.2.3`, commit SHAs) for predictable deployments. ## Image Tags and Digests **Tags** are human-readable labels pointing to a specific image version: ```text theme={null} my-app:v1.0.0 my-app:latest my-app:abc123 # commit SHA ``` **Digests** are immutable SHA256 hashes that uniquely identify an image: ```text theme={null} my-app@sha256:3fe719... ``` For production deployments, use specific version tags or digests rather than `latest` to ensure reproducible deployments. ## Permissions The permissions below are used to define [policies](/reference/policy) together with one or more of the four [principal types](/concepts/access-control): | Permission | Description | Implies | | :--------- | :------------------------------------------------------- | :--------------------------------------- | | create | Create new image. You can push if you can create images. | pull | | delete | Delete | | | edit | Modify existing image (only tags can be changed) | view | | manage | Full access | create, delete, edit, manage, pull, view | | pull | Image can be pulled | view | | view | Read-only access | | ## Minimum Policy ### Push At a minimum, the `create` permission must be bound to the principal pushing an image to an org's private registry. Using the console UI, follow these steps to create a least privileged [policy](/reference/policy) which will allow a principal to push an image: 1. Click `Policies` in the left menu bar and click the `New` button at the top of the form. 2. Enter a policy name, select `image` from the Target Kind pulldown, and enable the `Target All Images` button. Click the `Next` button. 3. Click `Add Binding`. 4. Select the `create` permission. Select the principal type that will be pushing the image from the top menu bar and select the principal. Click `Add`. 5. Click `Create`. The policy is now active and the principal has the ability to push images to the org's private registry. ### Pull At a minimum, the `pull` permission must be bound to a principal pulling an image from an org's private registry. Unless the [policy](/reference/policy) targets all images, a query must be created with the image names (without the tag) that the principal is allowed to pull. That query uses the `property` parameter and can only be created/updated using [cpln apply](/guides/cpln-apply) or the CLI's [cpln policy](/cli-reference/commands/policy) command. Below is a sample JSON manifest used as input to `cpln apply`. **Notice that the `property` parameter is equal to `repository`.** Update the `POLICY_NAME`, `ORG_NAME`, `USER_EMAIL`, `SERVICE_ACCOUNT_NAME`, and `IMAGE_NAME` tokens. The `principalLinks` can refer to a user or service account. ```json JSON theme={null} { "kind": "policy", "name": "POLICY_NAME", "description": "", "tags": {}, "origin": "default", "bindings": [ { "permissions": ["pull"], "principalLinks": ["/org/ORG_NAME/user/USER_EMAIL", "/org/ORG_NAME/serviceaccount/SERVICE_ACCOUNT_NAME"] } ], "targetKind": "image", "targetLinks": [], "targetQuery": { "kind": "image", "fetch": "items", "spec": { "match": "all", "terms": [ { "op": "=", "property": "repository", "value": "IMAGE_NAME" } ] } } } ``` Once the policy is active, the principal will have the ability to pull images using any Docker compatible tool (e.g., `docker pull ...`). ## Access Report Displays the permissions granted to principals for the image. Available in the console UI or via the CLI with `cpln image access-report IMAGE:TAG`. ## Learn More Build and push images to your private registry Configure workloads to pull from private registries Language-specific conventions for building without Dockerfiles Full CLI command reference for images # IP Set Source: https://docs.controlplane.com/reference/ipset Reserve and manage public IP addresses per GVC location for workloads, including binding, permissions, and configuration options. ## Overview An IP Set reserves a static public IP address per [GVC](/reference/gvc) location, preventing the address from changing when a workload is redeployed or infrastructure is updated. To prevent ongoing charges, IP addresses must be explicitly released once they are no longer needed. ## Binding An IP Set can be linked to a [workload](/reference/workload) or a [GVC](/reference/gvc) through its `spec.link` property. Linking requires configuration on **both** the IP Set and the target resource — each must reference the other for the binding to take effect. The IP Set will only function if cross-linking is fully configured. The IP Set must reference the workload or GVC via `spec.link`, and the workload or GVC must reference the IP Set via its load balancer configuration. ### Workload To link an IP Set to a workload, the workload must have the [direct load balancer](/reference/workload/load-balancing#direct-load-balancer) enabled. **1. Configure the IP Set** — set `spec.link` to point to the target workload: ```yaml theme={null} kind: ipset name: example spec: link: /org/example-org/gvc/example-gvc/workload/example-workload locations: - name: //location/aws-us-west-2 retentionPolicy: keep ``` **2. Configure the Workload** — set `loadBalancer.direct.ipSet` to reference the IP Set: ```yaml theme={null} kind: workload name: example-workload spec: loadBalancer: direct: enabled: true ipSet: /org/example-org/ipset/example ports: - externalPort: 443 protocol: TCP containerPort: 8080 ``` **1. Configure the IP Set** — set `spec.link` to point to the target workload: ```json theme={null} { "kind": "ipset", "name": "example", "spec": { "link": "/org/example-org/gvc/example-gvc/workload/example-workload", "locations": [ { "name": "//location/aws-us-west-2", "retentionPolicy": "keep" } ] } } ``` **2. Configure the Workload** — set `loadBalancer.direct.ipSet` to reference the IP Set: ```json theme={null} { "kind": "workload", "name": "example-workload", "spec": { "loadBalancer": { "direct": { "enabled": true, "ipSet": "/org/example-org/ipset/example", "ports": [ { "externalPort": 443, "protocol": "TCP", "containerPort": 8080 } ] } } } } ``` ### GVC To link an IP Set to a GVC, the GVC must have the [dedicated load balancer](/reference/gvc#dedicated-load-balancer) enabled. **1. Configure the IP Set** — set `spec.link` to point to the target GVC: ```yaml theme={null} kind: ipset name: example spec: link: //gvc/example-gvc locations: - name: //location/aws-us-west-2 retentionPolicy: keep ``` **2. Configure the GVC** — set `loadBalancer.ipSet` to reference the IP Set: ```yaml theme={null} kind: gvc name: example-gvc spec: loadBalancer: dedicated: true ipSet: //ipset/example ``` **1. Configure the IP Set** — set `spec.link` to point to the target GVC: ```json theme={null} { "kind": "ipset", "name": "example", "spec": { "link": "//gvc/example-gvc", "locations": [ { "name": "//location/aws-us-west-2", "retentionPolicy": "keep" } ] } } ``` **2. Configure the GVC** — set `loadBalancer.ipSet` to reference the IP Set: ```json theme={null} { "kind": "gvc", "name": "example-gvc", "spec": { "loadBalancer": { "dedicated": true, "ipSet": "//ipset/example" } } } ``` ## Releasing To release an IP address from a location, set the `retentionPolicy` for that location to `"free"`. An IP address will not be released unless it is no longer in use (no workload linked, GVC location not active, etc.) ```yaml theme={null} kind: ipset name: example spec: link: /org/example-org/gvc/example-gvc/workload/example-workload locations: - name: //location/aws-us-west-2 retentionPolicy: free ``` ```json theme={null} { "kind": "ipset", "name": "example", "spec": { "link": "/org/example-org/gvc/example-gvc/workload/example-workload", "locations": [ { "name": "//location/aws-us-west-2", "retentionPolicy": "free" } ] } } ``` ## Status Once the IP Set is initialized, its status is updated with the reserved IP address(es) for each location. The following is an example API response: ```json theme={null} { "kind": "ipset", "name": "example", "spec": { "link": "/org/example-org/gvc/example-gvc/workload/example-workload", "locations": [ { "name": "//location/aws-us-west-2", "retentionPolicy": "keep" } ] }, "status": { "ipAddresses": [ { "name": "aws-us-west-2", "ip": "10.20.30.40", "id": "eipalloc-0a1b2c3d4e5f67890", "state": "bound", "created": "2024-07-30T14:18:36.327Z" } ] } } ``` # Location Source: https://docs.controlplane.com/reference/location Available geographical locations across AWS, GCP, and Azure that can be enabled or disabled for GVC workload deployment. ## Overview Locations show the available geographical locations for each of the following cloud providers: * Amazon Web Services (AWS) * Google Cloud Platform (GCP) * Microsoft Azure Locations are scoped to an [org](/reference/org) and the [org](/reference/org) administrator can turn off/on the locations that are permitted when configuring a [GVC](/reference/gvc). By default, all locations are enabled. If a location is disabled, any [workloads](/concepts/workload) within that location will be removed. Further, if a location is enabled, all [workloads](/concepts/workload) will be provisioned to that location. If a company is restricted for legal or compliance reasons to only use certain cloud providers, disabling those providers will ensure that workloads will not be deployed to those providers. ## Locations Control Plane can enable any cloud provider location. If a location is not shown, please email [support@controlplane.com](mailto:support@controlplane.com) with the provider and location and it will be provisioned. ## Custom Locations CPLN Platform (BYOK) enables you to add new locations by registering your own Kubernetes clusters as locations in the Control Plane platform. For a guide on how to create new BYOK locations and further details, [refer to the BYOK documentation](/byok/overview). ## Built-in Tags Each location has the following built-in [tags](/core/misc#tags): * `cpln/city=VALUE` * `cpln/country=VALUE` * `cpln/continent=VALUE` ## Public IPs The list of public IPs for each cloud provider and region can be obtained from the following: * Console UI: The `Locations` page. Select the location to display the IP ranges. * CLI: Using the command [cpln location get -o json](/cli-reference/commands/location) will list the IPs in the `ipRanges` property. The output can also be formatted in YAML by using the flag `-o yaml-slim`. * API: Using the `location` endpoint. View the [location API endpoints](/api-reference/location/get-all-locations). The `ipRanges` property will contain the list of SIPs. The list of IPs may be required by external services that restrict the IPs allowed to call them. The IPs may change when underlying infrastructure changes. Therefore, Control Plane recommends the automation of querying the location API for changes. In case of changes, the automation should re-configure external services' allowed IP address list. ## Permissions The permissions below are used to define [policies](/reference/policy) together with one or more of the four [principal types](/concepts/access-control): | Permission | Description | Implies | | :--------- | :--------------------------------------- | :------------------------------ | | create | Create new locations | | | edit | Modify existing locations | view | | manage | Full access | create, edit, manage, use, view | | use | Use this location for workload placement | view | | view | Read-only access | | ## Access Report Displays the permissions granted to principals for the location. ## CLI To view the CLI documentation for locations, see the [Location CLI reference](/cli-reference/commands/location). # Org (Organization) Source: https://docs.controlplane.com/reference/org Reference for orgs, the top-level isolated context encompassing all Control Plane resources including domains, images, workloads, and users. ## Overview An organization serves as a tightly isolated bounded context that encompasses all the resources managed by Control Plane. These resources comprise [domains](/reference/domain), [images](/reference/image), [workloads](/concepts/workload), [GVCs](/reference/gvc), [users](/reference/user), [groups](/reference/group), [service accounts](/reference/serviceaccount), and more. It's possible for a physical organization, to create multiple 'orgs,' although this is not mandatory. Creating multiple orgs can be beneficial in order to establish complete isolation between environments, for instance. ## Create an Org Refer to the [Create an Org](/guides/create-org) guide. ## Multiple Orgs A user can be a member of one or more Orgs. * To switch between Orgs from the console: * From the left menu, click on the `>` to the right of the current org. * Search or scroll to the desired org. * Click on the desired org. ## External Logs / Logging Control Plane offers the ability to ship all Org logs to an external provider. Please see the [External Logging overview](/external-logging/overview) for additional details and configuration instructions. ## Endpoint Prefix All orgs are setup with a random endpoint prefix. It can be found at `{org}.status.endpointPrefix`. These are used in the workload canonical endpoint and can be configured by the [gvc](/reference/gvc#endpoint-naming-format). ## Tracing OpenTelemetry traces are supported and can be configured with the native `Control Plane` tracing provider or sent to an OpenTelemetry collector endpoint by using the `OpenTelemetry` tracing provider. ### Control Plane Tracing Provider The Control Plane tracing provider is the default method for collecting OpenTelemetry traces. They will be accessible for exploration using Grafana by accessing the `Traces` link from any workload in the Console. To enable traces using the Console, navigate to your GVC, click on `Tracing`, and choose `Control Plane` as the metric provider. Then, configure the sampling percentage and, optionally, the Custom Tags. When using the Control Plane tracing provider, workloads can forward additional Open Telemetry traces to the following endpoints: GRPC: `tracing.controlplane:80` HTTP: `tracing.controlplane:4318` For each of these endpoints, mutual tls is added automatically for requests and any clients/libraries should be configured to send requests using plain text. Here is an example of an Org with tracing enabled for 10 percent of requests to be forwarded to the Control Plane tracing provider. Additionally, the tag `team`=`profile` will also be added to each trace. ```yaml YAML theme={null} kind: org name: online-boutique spec: ... tracing: provider: controlplane: {} customTags: { team: profile } sampling: 10 ``` ### OpenTelemetry Tracing Provider Similarly, traces can be sent to an OTEL collector endpoint using the `OpenTelemetry` tracing provider. For details, see the [Online Boutique](https://github.com/controlplane-com/examples/blob/main/examples/online-boutique/README.md) example. ## Observability The retention period for logs, metrics and traces defaults to 30 days and can be adjusted for each independently. Charges apply for combined storage of logs, metrics and traces over 100GB calculated by GB-Month. ## Threat Detection Control Plane provides real-time threat detection and alerting by inspecting syscalls of all running workloads using Falco. There is no action required to enable threat detection, metrics are collected for each detected threat and are used to send notifications. Threat alert notifications include the GVC, Workload, rule that triggered the alert and priority of the alert. If Syslog forwarding of threat details is required, it can be configured in the Org settings. The following list of threats or attempted activities will trigger an alert at the specified severity levels. ### Notice severity * Packet socket created * PTRACE anti-debug * SSH Activity * Connect to kubernetes api ### Info severity * Spawned processes with an interactive tty ### Warning severity * Directory traversal * Sensitive file read activity * Netcat remote code execution * Search for private keys or passwords * Log file clearing * Remove data from disk * Create symlinks\hardlinks to sensitive files * Kernel module injection * Debugfs activity * PTRACE attached * Credential search activity * Execution from /dev/shm * Malicious binaries or script execution ### Critical severity * Container escape attempts * Drop and execute new binary * Fileless execution using memfd\_create * Crypto currency mining activity ### Threat Detection Metrics Metrics used for threat alerts are stored in the `threat_detection_alerts` time series. Labels for this metric are as follows: * gvc * workload * rule (the falco rule that was triggered) * priority (the severity of the rule triggered) only threats with a severity >= `warning` will be alerted by default `threat_detection_forward_enable` is set to 1 when Syslog forwarding of alerts is enabled. `threat_detection_forward_total` is provides a total of all threat events sent to the Syslog target. ### Threat Detection Logs When alerts are triggered, detailed information is logged to the `_alerts` container for the workload. These logs can be viewed in the Console by navigating to the workload and clicking on `Logs`, and setting the container to `_alerts`. ## Session Timeout - Console UI The console UI will automatically sign out if inactive for 15 minutes. This timeout duration is the default setting (for [PCI compliance](/compliance#pci)) and can be modified. This timeout setting (in seconds) can be adjusted from the `Info` page when clicking on the `Org` link from the left menu. ## Permissions The permissions below are used to define [policies](/reference/policy) together with one or more of the four [principal types](/concepts/access-control): | Permission | Description | Implies | | :--------------- | :----------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------- | | edit | Modify org | view | | exec | Grantees can execute all commands on the org | exec.echo | | exec.echo | Grantees can execute the echo command | | | grafanaAdmin | Grantees are made Admin in Grafana, otherwise the role 'Viewer' is assigned | | | manage | Full access | edit, exec, exec.echo, grafanaAdmin, manage, readLogs, readMetrics, readUsage, view, viewAccessReport | | readLogs | Grantees can read logs from all workloads | view | | readMetrics | Grantees can access usage and performance metrics | | | readUsage | Grantees can access usage and billing metrics | | | view | Read-only view: every org member can view their org | | | viewAccessReport | Grantees can inspect the granted access report on all resources within the org | | ## Access Report Displays the permissions granted to principals for the Org. ## CLI To view the CLI documentation for an Org, see the [Org CLI reference](/cli-reference/commands/org). # Overview Source: https://docs.controlplane.com/reference/overview Reference section navigation and resource hierarchy. Covers org-level resources, GVC structure, principals, infrastructure, and asset relationships. The Reference section provides detailed documentation for all Control Plane resources. Each resource type has specific properties, permissions, and relationships that enable you to build secure, scalable applications across multiple cloud providers. *** ## Resource Hierarchy Control Plane resources are organized into a hierarchical structure where resources are scoped to provide isolation and governance: ```text theme={null} Org (Organization) ├── Principals: Users, Groups, Service Accounts ├── Governance: Policies, Quotas ├── Infrastructure: Cloud Accounts, Agents, Locations ├── Assets: Secrets, Images, Domains └── GVC (Global Virtual Cloud) ├── Workloads ├── Identities └── Volume Sets ``` *** ## Organization Top-level administrative boundary for all platform resources. Configure organization-wide governance, observability, security integrations, and operational settings. Isolated environment and top-level container for all resources. Configure external logging, threat detection, tracing, and organization-wide settings. *** ## Access Control Resources that control who can access what within your organization. Grant permissions to principals (users, groups, service accounts, identities) for resources in your organization. Collections of users and service accounts. Supports dynamic membership via tag-based queries. Organization members who access Control Plane via the Console, CLI, API, Terraform, or Pulumi. Programmatic access for CI/CD pipelines, automation, and API integrations. Resource limits for your organization including agents, domains, and workloads. *** ## Compute & Storage Core resources for running and managing your applications. Global Virtual Cloud - container for workloads deployed across multiple cloud locations with shared configuration. Deploy containerized applications as standard, stateful, cron, or serverless workloads with autoscaling and load balancing. Geographic regions across AWS, GCP, and Azure where your workloads can be deployed. Persistent storage for workloads with snapshots, autoscaling, and encryption. *** ## Networking & Domains Resources for routing traffic and connecting your services. Custom domain mapping with TLS certificates, CORS configuration, and path-based routing to workloads. Reserve static public IPs per location for DNS configuration and firewall rules. Secure tunneling to private networks, on-premises systems, and cloud VPCs without exposing them to the internet. *** ## Cloud Integration Resources that connect Control Plane to your cloud providers and private networks. Bridge to AWS, GCP, Azure, or NATS for identity-based access to cloud services. Credential-free access to cloud resources and private networks via workload identity federation with AWS, GCP, Azure, and NGS. *** ## Secrets, Images & Audit Resources for managing credentials, container images, and audit trails. Encrypted storage for credentials including AWS keys, Docker registries, TLS certificates, and more. Container images in Control Plane's private registry or references to external registries. Tamper-proof audit trails for Control Plane actions and custom external events from your workloads. *** ## Workload Configuration The Workload resource has extensive configuration options documented across multiple pages: Endpoints, environment variables, and debug mode Standard, stateful, cron, and serverless workloads Image references, ports, CPU, memory, and GPU Metric-based scaling, scale-to-zero, and KEDA event-driven scaling Capacity AI and resource optimization Direct load balancer and IP Sets Ingress and egress rules mTLS and external authorization Token-based authentication Mount secrets, cloud storage, and volume sets Prometheus-based metrics for advanced scaling Grace periods and shutdown behavior *** ## Key Concepts Control Plane implements hierarchical role-based access control (RBAC) where **Policies** bind permissions to **Principals** (users, groups, service accounts, identities) for target resources. Permission inheritance follows hierarchical implication rules; for example, `manage` implicitly grants `create`, `edit`, and `delete`. A single **GVC** can deploy workloads across multiple **Locations** spanning AWS, GCP, and Azure regions simultaneously. Platform networking automatically routes traffic to healthy workload instances based on availability and geographic proximity. **Identities** provide credential-free access to cloud resources and private networks. Instead of embedding secrets in your code, workloads assume cloud-native identities (AWS IAM roles, GCP service accounts, Azure managed identities) at runtime. **Agents** establish secure connectivity between Control Plane and private infrastructure without requiring public network exposure. Combined with **Identities**, workloads can securely access databases, internal APIs, and services within private cloud networks and VPC environments. # Policy Source: https://docs.controlplane.com/reference/policy Fine-grained permission policies for all resource types. Covers policy bindings, principal targeting, permission sets, and built-in system policies. ## Overview Refer to the [policy concepts](/concepts/access-control) page. ## Creating a Policy Refer to the [Create a Policy](/guides/policy) guide for additional details. ## Resource Permissions Each resource has a set of permissions that can be assigned to a policy. Expand the dropdown below and click on a resource to view their assignable permissions. After clicking on a link, expand the `Examples` dropdown to view the list. * [Agent](/cli-reference/commands/agent#agent-permissions) * [Audit Context](/cli-reference/commands/auditctx#auditctx-permissions) * [Cloud Account](/cli-reference/commands/cloudaccount#cloudaccount-permissions) * [Domain](/cli-reference/commands/domain#domain-permissions) * [Group](/cli-reference/commands/group#group-permissions) * [GVC (Global Virtual Cloud)](/cli-reference/commands/gvc#gvc-permissions) * [Identity](/cli-reference/commands/identity#identity-permissions) * [Image](/cli-reference/commands/image#image-permissions) * [Location](/cli-reference/commands/location#location-permissions) * [Org](/cli-reference/commands/org#org-permissions) * [Policy](/cli-reference/commands/policy#policy-permissions) * [Quota](/cli-reference/commands/quota#quota-permissions) * [Secret](/cli-reference/commands/secret#secret-permissions) * [Service Account](/cli-reference/commands/serviceaccount#serviceaccount-permissions) * [User](/cli-reference/commands/user#user-permissions) * [Volume Set](/cli-reference/commands/volumeset#volumeset-permissions) * [Workload](/cli-reference/commands/workload#workload-permissions) ## Built-in Policies Each [org](/reference/org) has the following built-in policies for each resource (agent, GVC, etc.): | Policy Name | Description | Target | Permission | Group | Service Account | | :------------------ | :------------------------------------------------------------- | :-------- | :--------- | :--------------------------------------------- | :------------------------------------------------------------------ | | superusers-RESOURCE | Built-in policy granting full access to the superusers group | All Items | manage | [superusers](/reference/group#built-in-groups) | [controlplane](/reference/serviceaccount#built-in-service-accounts) | | viewers-RESOURCE | Built-in policy granting read-only access to the viewers group | All Items | view | [viewers](/reference/group#built-in-groups) | none | ## Permissions The permissions below are used to define [policies](/reference/policy) together with one or more of the four [principal types](/concepts/access-control): | Permission | Description | Implies | | :--------- | :----------------------- | :--------------------------------- | | create | Create new policies | | | delete | Delete existing policies | | | edit | Modify existing policies | view | | manage | Full access | create, delete, edit, manage, view | | view | Read-only view | | ## Access Report Displays the permissions granted to principals for the policy. ## CLI To view the CLI documentation for policies, see the [Policy CLI reference](/cli-reference/commands/policy). # Quota Source: https://docs.controlplane.com/reference/quota View the current usage and allowed allocation of Control Plane resources such as agents, domains, GVCs, and workloads for your org. ## Overview The quotas page will display the currently used and allowed allocation of Control Plane resources for your [org](/reference/org). Current resources with a quota: | Resource | Description | | :----------------- | :----------------------------------------- | | agents | Max number of agents | | domains | Max number of domains | | identities-per-gvc | Max number of workload identities in a gvc | | workloads-per-gvc | Max number of workloads in a gvc | If your [org](/reference/org) requires additional resources, please email [support@controlplane.com](mailto:support@controlplane.com) ## Permissions The permissions below are used to define [policies](/reference/policy) together with one or more of the four [principal types](/concepts/access-control): | Permission | Description | Implies | | :--------- | :--------------------- | :------------------------- | | create | Create new quotas | | | edit | Modify existing quotas | view | | manage | Full access | create, edit, manage, view | | view | Read-only access | | ## CLI To view the CLI documentation for Quotas, see the [Quota CLI reference](/cli-reference/commands/quota). # Secret Source: https://docs.controlplane.com/reference/secret Encrypted secret storage with 12 types: AWS, Azure SDK, Azure Connector, Docker, ECR, GCP, Dictionary, Keypair, Opaque, NATS Account, TLS, and Username & Password. ## Overview Secrets are used to store strongly-encrypted information. All secrets can be injected into containers through the use of [environment variables](/reference/workload#environment-variables). In addition to optionally being injected into containers as [environment variables](/reference/workload#environment-variables), secrets are also used for: 1. Image registry ([Docker](#docker), [ECR](#ecr), and [GCP](#google-cloud-platform-gcp)) pull secrets, enabling workloads in a [GVC](/reference/gvc) to pull images from private repositories. 2. Enabling Control Plane to manage policies in Azure, using the [Azure SDK](#azure-sdk) and [Azure Connector](#azure-connector) secret type. A [workload identity](/reference/identity) **MUST** be granted the [reveal permission](#reveal-permission) on a secret to have its value injected at runtime. If a workload has no identity assigned, it cannot access any secrets at runtime. An identity must be created, assigned to the workload, and granted the `reveal` permission on the target secret via a [policy](/reference/policy). ## Create a Secret For step-by-step instructions on creating secrets, see the [Create a Secret](/guides/create-secret/overview) guide. ## Secret Types * [Amazon Web Services (AWS)](#amazon-web-services-aws) * [Azure SDK](#azure-sdk) * [Azure Connector](#azure-connector) * [Docker](#docker) * [Dictionary](#dictionary) * [ECR](#ecr) * [Google Cloud Platform (GCP)](#google-cloud-platform-gcp) * [Keypair](#keypair) * [Opaque](#opaque) * [NATS Account](#nats-account) * [TLS](#tls) * [Username & Password](#username-and-password) ### Amazon Web Services (AWS) The AWS secret type enables a [workload](/concepts/workload) to consume services from AWS. It is used in cases where an [identity](/reference/identity) isn't used. For example, when an Org does not wish to create a [cloud account](/reference/cloudaccount), or to create cloud access policy as part of an [identity](/reference/identity) definition. The recommended approach to consuming services from AWS is not to use a AWS secret, but rather to: 1. Create a [cloud account](/reference/cloudaccount). 2. Create an [identity](/reference/identity). 3. Defining a cloud access policy for the AWS account as part of the identity. In cases where an Org wishes to not leverage the [identity](/reference/identity) feature, in order to use an AWS secret, simply read the secret from the environment variable and convey its set of values to AWS using any AWS API you wish. The AWS secret schema consists of: * Secret Key * Access Key * Role ARN (Optional) * External ID (Optional) For an existing AWS user, these values are obtained from the [AWS IAM Dashboard](https://console.aws.amazon.com/iam/home). Refer to [these instruction](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html) to create a new AWS IAM user in your AWS account. ### Azure SDK * Used to store [Azure SDK](/reference/cloudaccount#azure-sdk) credentials. * When creating a [Cloud Account using the Azure SDK](/reference/cloudaccount#azure-sdk), a JSON file is generated containing the credentials. This file is uploaded and automatically stored as an Azure-SDK secret. * Sample Azure-SDK secret content: ```json JSON theme={null} { "subscriptionId": "2cd2674e-4f89-4a1f-b420-7a1361b46ef7", "tenantId": "292f5674-78b0-488b-9ff8-6d30d77f38d9", "clientId": "649746ce-d862-49d5-a5eb-7d5aad90f54e", "clientSecret": "CONFIDENTIAL" } ``` * If the client secret has been compromised or needs to be updated, the content of the secret can be updated using the console or CLI and will be used by the associated Cloud Accounts. ### Azure Connector * Used to store [Azure Function App](/reference/cloudaccount#azure-connector) credentials. * When creating a [Cloud Account using the Azure Connector](/reference/cloudaccount#azure-connector), the `deployment URL` and `code` will be saved. * These values can be updated using the console or the [CLI](/cli-reference/commands/secret#secret-update). ### Docker The Docker secret type is used when a [container](/reference/workload/containers) pulls an [image](/reference/workload#images) from a private Docker compatible repository, such as a Org's private repository at Control Plane, [Docker Hub](https://hub.docker.com/), [Azure Container Registry](https://docs.microsoft.com/en-us/azure/container-registry/) or [Google Artifact Registry](https://cloud.google.com/artifact-registry/docs). An authorized user then assigns a secret of this type to a [GVC Pull Secret](/reference/gvc#pull-secrets). [Workloads](/concepts/workload) in the [GVC](/reference/gvc) can then pull their image using the secret. The contents of a Docker secret follows this syntax: ```json JSON theme={null} { "auths": { "REPOSITORY_URL": { "username": "USERNAME", "password": "PASSWORD" } } } ``` #### Control Plane To create a Docker secret targeting a private repository hosted at Control Plane, a [Service Account](/reference/serviceaccount) at the source Org, bound with the [pull permission](/reference/image#pull) is required. Example: ```json JSON theme={null} { "auths": { "ORG_NAME.registry.cpln.io": { "username": "", "password": "SERVICE_ACCOUNT_KEY" } } } ``` The `username` is the literal string ``. #### Docker Hub To create a Docker secret targeting Docker Hub, a Docker username and password (which has permissions to pull the images) are required. There are two ways to create the secret content: 1. `Username & Password` Keys Example: ```json JSON theme={null} { "auths": { "https://index.docker.io/v1/": { "username": "USERNAME", "password": "PASSWORD" } } } ``` 2. `auth` Key * When using a password, the value of the `auth` key is the base64 encoded result of the string `USERNAME:PASSWORD`. Example: ```json JSON theme={null} { "auths": { "https://index.docker.io/v1/": { "auth": "BASE_64_ENCODING(USERNAME:PASSWORD)" } } } ``` * When using a [Docker Access Token](https://docs.docker.com/docker-hub/access-tokens/), the value of the `auth` key is the base64 encoded result of the string `USERNAME:DOCKER_ACCESS_TOKEN`. Example: ```json JSON theme={null} { "auths": { "https://index.docker.io/v1/": { "auth": "BASE_64_ENCODING(USERNAME:DOCKER_ACCESS_TOKEN)" } } } ``` When performing the base64 encoding, remember to include the colon ( : ) when concatenating the username and password/token. The `base64` utility can be used to easily encode the string. Example: `echo -n USERNAME:PASSWORD | base64` #### Azure Container Registry To create a Docker secret targeting the [Azure Container Registry](https://docs.microsoft.com/en-us/azure/container-registry/), the following properties are required: * Repository URL * Service Principal ID * Password An `Azure Service Principal` is recommended as the identity used to pull images. Refer to this [article](https://docs.microsoft.com/en-us/azure/container-registry/container-registry-auth-service-principal) to create an Azure Service Principal that only has the pull permission. * The format of the repository URI is: `REGISTRY_NAME.azurecr.io` and can be obtained from the `Azure Container Registry` section of the [Azure Portal](https://portal.azure.com/). * The USERNAME is the ID GUID of the Service Principal (e.g., ed0a30b4-5cd0-44af-864a-cf825b0ceeb6). * The PASSWORD is generated when creating the Service Principal. **By default, the password expires after 1 year.** To increase the expiration length of the password, use the `--years` flag when running the `az ad sp create-for-rbac` command during the creation of a Service Principal. Example: ```json JSON theme={null} { "auths": { "REGISTRY_NAME.azurecr.io": { "username": "APP_ID", "password": "PASSWORD" } } } ``` #### Google Artifact Registry To create a Docker secret targeting [Google Artifact Registry](https://cloud.google.com/artifact-registry/docs), the following property is required: * Base64-encoded service account JSON key A Docker secret using a base64-encoded service account JSON key is recommended, alternatively a GCP secret containing the service account JSON is still supported but deprecated and should be avoided. * The format of the repository URI is: `LOCATION-docker.pkg.dev` where `LOCATION` is the repository’s region chosen at creation. * The **USERNAME** must be `_json_key_base64`. * The **PASSWORD** is the base64-encoded service account JSON key. Example: ```json JSON theme={null} { "auths": { "LOCATION-docker.pkg.dev": { "username": "_json_key_base64", "password": "BASE64_ENCODED_SERVICE_ACCOUNT_JSON" } } } ``` #### GitHub Container Registry To create a Docker secret targeting GitHub, a GitHub [personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) (which has the `read:packages` scope) is required. Example: ```json JSON theme={null} { "auths": { "ghcr.io": { "username": "controlplane", "password": "PERSONAL_ACCESS_TOKEN" } } } ``` ### Dictionary * Used to securely store key/value pairs that can be used by a [workload's environment variables](/reference/workload#environment-variables). * Multiple key/value pairs can be added to a single dictionary secret. ### ECR The Elastic Container Registry (ECR) secret type is used when a [workload](/concepts/workload) pulls a container image from a private AWS ECR repository. This secret type is used when configuring a [GVC's Pull Secret](/reference/gvc#pull-secrets). [Workloads](/concepts/workload) that are connected to that [GVC](/reference/gvc) will have access to the secret when pulling the image. The ECR secret consists of: * AWS Secret Key * AWS Access Key * AWS Role ARN (Optional; This role will be assumed after authentication with the IAM user’s programmatic access) * ECR Repository URIs (e.g., AWS\_ACCOUNT\_ID.dkr.ecr.REGION.amazonaws.com/REPO\_NAME) * External ID (Optional) Multiple repositories can be added to the same ECR secret. To obtain the keys required to create an ECR secret from the [AWS IAM Dashboard](https://console.aws.amazon.com/iam/home): 1. Add a new IAM User with programmatic access. 2. Create an Access Token for the new user and take note/download the secret and access key. 3. Create a new IAM Policy for the `Elastic Container Registry` service with the `Read` action on the specific repositories. 4. Attach the new IAM Policy to the new IAM User. 5. Optionally, impersonate the IAM user with its credentials and try to pull an image locally as described [here.](https://docs.aws.amazon.com/AmazonECR/latest/userguide/registry_auth.html) 6. Create the ECR secret using either the [UI Console](https://console.cpln.io) or [CLI](/cli-reference/commands/secret#secret-create-ecr). #### Amazon ECR Helper Links * [ECR Dashboard](https://us-west-2.console.aws.amazon.com/ecr/repositories) * [Private registry concepts](https://docs.aws.amazon.com/AmazonECR/latest/userguide/Registries.html) ### Google Cloud Platform (GCP) * Similar to a [Docker](#docker) and [ECR](#ecr) secret, a GCP secret is used when a [workload](/concepts/workload) pulls a container image from a private `Google Container Registry` (deprecated). For new deployments, use [`Google Artifact Registry`](#google-artifact-registry). * This secret type is used when configuring a [GVC Pull Secret](/reference/gvc#pull-secrets). [Workloads](/concepts/workload) that are connected to that [GVC](/reference/gvc) will have access to the secret when pulling the image. * Using the UI, the secret JSON text can be in a file that is uploaded or pasted into the textbox. * Below is a sample of a GCP secret. The values can be obtained from your GCP account. ```json JSON theme={null} { "type": "service_account", "project_id": "PROJECT_ID", "private_key_id": "PRIVATE_KEY_ID", "private_key": "PRIVATE_KEY_CERTIFICATE", "client_email": "CLIENT_EMAIL", "client_id": "CLIENT_ID", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token", "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", "client_x509_cert_url": "X509_CERTIFICATE_URL" } ``` ### Keypair * Used to securely store a keypair that can be used by a [workload's environment variables](/reference/workload#environment-variables). * A keypair secret consists of: * Secret key * Public key * Passphrase (required if the secret key is encrypted) * Using the UI, the secret and public key text can be in a file that is uploaded or pasted into the textbox. * The keys will be validated and the secret will only be created or updated if they are valid PEM-encoded certificates. **Sample secret key:** ```text theme={null} -----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: DES-EDE3-CBC,9A26BB15304B18E7 ZdBgMExsvIJEsIFDMQ02xh4nDnhXEGUNu7LiWIZjn9WS6QB2jApyOFOBWmp0lK6L dIJ+Mb8wMeHtkiKS6ZbYeea8M29kwEejZRnKl1Wq0EFycdwbONtbcbjzF+tQGEBT gQQgkY7wjDWl8HwjFEA+NUuitzi6uI2xWlQpFdUrmqJAZCbxNFa0aM8nW6jnitvP 616ps3HjLnWCjoyqS4hWxiWmt+VE3KruPnUVVV7bWlzc6jnoZcSaeqeaoQrNKguH te2iBIMdY/uldb7Ik2Kxr2+kBRmV4YNkp1EelNi/m39VcoUHJLk1jLldzuINhbi2 IRqYZe4EEMSYdb3TkSosXa64Sz7jMBz5AxlA0n78FKlB9G5FAxaXcVYNQIlvzCbw uXPbQd/UYKUuEI1Yn8OmGBN5xcOdgWz8hfyxA2Hq1tmo1XN6snavGe7TKbZd70N+ 1yFbclB2T1z8fPcLwUZUxOl4g2DoMMHIzCSPaIe/otT8389k4H6hEulLis4lW0p3 qopL5kdpxmSGgXsX6q6CUFb/0cw9HskNT3zbzKLx2MzjFCo93IB07UxPwkCD2kb1 sLKMcpTC8a0vLaTVNYgDX7wW/YjBrCokaqk0z1whuN6iSReOtvmu5ybrq1Ksg8UQ yvCSScM/+muKi+gbEOskQs4Ph3ZLHqAX3/XYoyBcFnPNxVHTIa5Dcju6h5gl1/uY 6tkRsHDr0Lzy8pd6jjf/ApPf9ypCuxKUO1q8PzPg2E4bmEFxc8zOB2NLvfPgFrUR 0Sbkapv/6x6nNRw75cu69c5we/atip6wst8J1MSU0fTqb6bZ3TF2pDyNEOkdkvoZ YZ0r3hUytdT0pImoDLKoyy17mtHLLApzHyIgmR3cqtSt07ncmC5lyEBcZBrQXMa8 aZeOr8iUWQE/q+4BvoxeKsOD6ttKuFnrgl0rmMnYQsSyLJOPizrU4L1d1HMIKswm iW+Rg7xlWmQg95m8XEWTjAb3tuNz/tGXC7Qa88HvC7YfyG69yM61oPsT83YnxcBT C/X67lSFTYguFa3HgDZpjGq7Hc/Q7nhaoqNMEs01O6jbcmrue8IIa2FH1tTwPN0W D7JefjCQjEghue2mjc0fovOGe9A9jvWf+gJHF3vRtFa67uQiQxge9zUzpHyVNpOj Ve0y0HvibNTd6TSCArctJpIcwpjO3MTT5LBJ1p/8v4b4+knEKD2c69jumNbKGbWr Wjq39M/MGNUO5SbZMO3gFCt6fgtXkOktH9pJ9iOQpYKgl7QTe2qQygfWkIm0EZRN 6EaQdNNKgENWicpKyKQ4BxoY1LYAHFHJ95VisLf3KmmOF5MwajADZQT/yth3gvht xx21b9iudcgq/CRccSvfIPIWZKi6oaqNIXK+E3DQd40TUopLsBWzacTZn9maSZtW RyAY1TkRn1qDR2soyhBcihrX5PZ83jnOlM3XTdfF1784g8zB9ooDnK7mUKueH1W3 hWFADMUF7uaBbo5EZ9sE+dFPzWPJLhu2j67a1iHmByqEvFY64lzq7VwwU/GE8JdA 85oEkhg1ZEPJp3OYTQfPI/CC/2fc93Exf6wmaXuss8AHehuGcKQniOZmFOKOBprv -----END RSA PRIVATE KEY----- ``` **Sample public key:** ```text theme={null} -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwrVyExI0uvRmwCAKFHiv baAcPMcKJDa6f6TtaVo2p8jyfEhVwDTmR3FUrDDZAjh0Q8G/Up8Ob3+IJafNymCO BhUKou+8ie7guqsbU9JrT0Zos1k/pd0aVfnAR0EpW3es/7fdkWUszU0uweeEj22m XMlLplnqqoYOGAhuNMqGsZwBr36Bxq9EeB2O79QsAFDNkPVg7xIaYKn32j69o0Zr ryYI8xqOYYy5Dw6CX+++YYLYiR/PkLYJTVAsxXeqyltCfb3Iv7vN5HrfoYBhndr3 NxBPkcIJZeh3Z+QzfJ5U+bB5fP/aOsEk5bPbtLzylj2KnOOM/ZxXJtOcu0xtJLd3 XwIDAQAB -----END PUBLIC KEY----- ``` **Passphrase:** ```text theme={null} cpln ``` ### Opaque Opaque secrets are used to securely store any text. When creating or updating an opaque secret, an option is available to perform a base64 decode at runtime. To access the stored text, an [environment variables'](/reference/workload#environment-variables) value must be set to one of the following: 1. `cpln://secret/SECRET_NAME.payload` * If the option to perform a base64 decode at runtime is enabled, the decoded value will be returned. 2. `cpln://secret/SECRET_NAME` (without the .payload) * If the option to perform a base64 decode at runtime is enabled, the `encoding` property will be set to `base64`. It is the responsibility of the user to perform the base64 decode. Example values returned: `base64: "{\"payload\":\"VGhpcyBpcyBhbiBvcGFxdWUgaW4gYmFzZTY0\",\"encoding\":\"base64\"}"` `plain text: "{\"payload\":\"This is an opaque secret in plain text\",\"encoding\":\"plain\"}"` ### NATS Account * Used to store NATS Account credentials. * A NATS Account secret consists of: * Account ID * Private Key * These properties will be validated and the secret will only be created or updated if they are valid. ### TLS * Used to securely store a Transport Layer Security (TLS) secret that can be used by a [workload's environment variables](/reference/workload#environment-variables). * A TLS secret consists of a: * Key * Certificate * Chain Certificate (optional if the certificate is self-signed or not part of the installed root certificates) * Using the UI, the secret can be in a file that is uploaded or pasted into the textbox. * The certificates will be validated and the secret will only be created or updated if they are valid PEM-encoded certificates **Sample key:** ```text theme={null} -----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDBzN2jRf9ouoF4 XG0eUxcc4f1sP8vhW1fQXjun3cl0RsN4jRdOyTKWcls1yAxlOkwFod8d6HND9OvN rsl7U4iJIEcJL6vTqHY7jTGXQkd9yPONMpMXYE8Dsiqtk0deoOab7fafYcvq1iWn pvg157mJ/u9qdyU+1h8DncES30FkPsG8TsIsjx94JkTJeMmEJxtws4dfuoCk88IN bBHLjxBQgwTu0vgMxN34b5z+esHraetDN2fqxSoTOeIlyFzeS+kwG3GK4I1hUQBi L2TeDrnEY6qP/ZoGuyyVnsT/6pHY/BTAcH3Rgeqose7mqBT+7zlxDfHYHceuNB/l jq0e1j69AgMBAAECggEAPGhrPZV4A2D/MlE9AhLMRYh7wd4w4tHiEWUOG0kank/g Zhc0iK5WQmbq31y34GXHhInsThpCs5AIYFh3HSXwjS2udsKRQKxmDjH4nzldp2uX 3w9Aoiy29GP4wZoCyRBGUZxfH1cQhOazXgrBm6vbPZRldD4nMer0R+BIamWEsIYD YjDj1pT0noLUSeqoLmGxSQ4DNIBQVZB/T8ziMcEzl6bhprT0QrapJSyD2CtA8tH1 Z8cyhmyE0CUvSkV4K2ecvVukWBJvrAYc6euPAnkS5LJrQotI5+3jJO2QawOlL6Uw rFWBpgBrCgbzquMRpDCQ/J9/GDYaZjim4YdonboBgQKBgQD7jx3CVnG4LDz198am spmPwKCW1ke6PhlG7zf3YR00xg9vPBYiy4obb1Jg6em1wr+iZ0dEt8fimeZXewBf LzlrR8T1Or0eLzfbn+GlLIKGKhn2pKB/i1iolkfIonchqXRk9WNx+PzjgUqiYWRC /1tH2BsODlVrzKL2lnbWKNIFdQKBgQDFOLedpMeYemLhrsU1TXGt1xTxAbWvOCyt vig/huyz4SQENXyu3ImPzxIxpTHxKhUaXo/qFXn0jhqnf0LfWI4nbQUbkivb5BPr KY9aj7XwwsY4MXW5C12Qi0lIwHOWCmfzvyS7TCMqnQb7sT4Mjmm4ydEbiI1TjlFJ D/RFxzcDKQKBgQCehPcJyZNrrWTU0sh5rz4ZWhdYNbuJXyxqiMBJwQa4hL6hJ8oD LyPeWe4daAmAIjLEUjSU1wK8hqKiKb54PLgAJH+20MbvyG14lm2Iul2d0dX+mIsT FGpQAjNF+Sr9KV1RaVi7L12ct5KidKDLn0KUKVgTKXEmtxNSNEq6dYqzKQKBgDI8 zljzvnwSwNloIYgAYDK+FPGHU/Z8QrVHOQ1lmyn+8aO41DfeqZPeVW4b/GrII3QC HnqsWdJ32EZOXoRyFFPqq2BojY+Hu6MthPy2msvncYKi5q/qOz00nchQbaEMqYon aH3lWRfjxAGdFocwR7HwhrmSwR1FpWMNE1Yq9tJxAoGBANc0nZSy5ZlTiMWdRrTt gFc9N/jz8OL6qLrJtX2Axyv7Vv8H/gbDg4olLR+Io38M0S1WwEHsaIJLIvJ6msjl /LlseAW6oiO6jzhWEr0VQSLkuJn45hG/uy7t19SDuNR7W5NuEr0YbWd6fZEpR7RR S1hFKnRRcrVqA+HjWnZ//BGi -----END PRIVATE KEY----- ``` **Sample certificate:** ```text theme={null} -----BEGIN CERTIFICATE----- MIID+zCCAuOgAwIBAgIUEwBv3WQkP7dIiEIxyj+Wi1STz7QwDQYJKoZIhvcNAQEL BQAwgYwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRQwEgYDVQQH DAtMb3MgQW5nZWxlczENMAsGA1UECgwEQ1BMTjERMA8GA1UECwwIQ1BMTi1PUkcx EDAOBgNVBAMMB2NwbG4uaW8xHjAcBgkqhkiG9w0BCQEWD3N1cHBvcnRAY3Bsbi5p bzAeFw0yMDEwMTQxNzI4MDhaFw0zMDEwMTIxNzI4MDhaMIGMMQswCQYDVQQGEwJV UzETMBEGA1UECAwKQ2FsaWZvcm5pYTEUMBIGA1UEBwwLTG9zIEFuZ2VsZXMxDTAL BgNVBAoMBENQTE4xETAPBgNVBAsMCENQTE4tT1JHMRAwDgYDVQQDDAdjcGxuLmlv MR4wHAYJKoZIhvcNAQkBFg9zdXBwb3J0QGNwbG4uaW8wggEiMA0GCSqGSIb3DQEB AQUAA4IBDwAwggEKAoIBAQDBzN2jRf9ouoF4XG0eUxcc4f1sP8vhW1fQXjun3cl0 RsN4jRdOyTKWcls1yAxlOkwFod8d6HND9OvNrsl7U4iJIEcJL6vTqHY7jTGXQkd9 yPONMpMXYE8Dsiqtk0deoOab7fafYcvq1iWnpvg157mJ/u9qdyU+1h8DncES30Fk PsG8TsIsjx94JkTJeMmEJxtws4dfuoCk88INbBHLjxBQgwTu0vgMxN34b5z+esHr aetDN2fqxSoTOeIlyFzeS+kwG3GK4I1hUQBiL2TeDrnEY6qP/ZoGuyyVnsT/6pHY /BTAcH3Rgeqose7mqBT+7zlxDfHYHceuNB/ljq0e1j69AgMBAAGjUzBRMB0GA1Ud DgQWBBRxncC/8RRio/S9Ly8tKFS7WnTcNTAfBgNVHSMEGDAWgBRxncC/8RRio/S9 Ly8tKFS7WnTcNTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAr sDZQj4K47fW6JkJbxlzZ1hd7IX6cQhI/DRIdTGR1u0kM1RtZoS0UtV5qsYV/g/S4 ChuB/aIARyTWvHKDhcT3bRGHLnoZJ8pLlQh4nEfO07SRhyeNiO4qmWM9az0nP5qD wAXpLpmYIairzAgY7QXbk5wXbTrXli3mz14VaNoqN4s7iyLtHn5TGAXc12aMwo7M 5yn/RGxoWQoJqSQKc9nf909cR81AVCdG1dFcp7u8Ud1pTtlmiU9ZJ/YOXDCT/1hZ YxoeotDBBOIao3Ym/3351somMoQ7Lz6hRWvG0WhDIsCXvth4XSxRkZFXgjWNuhdD u2ZCis/EwXsqRJPkIPnL -----END CERTIFICATE----- ``` **Sample chain certificate:** ```text theme={null} None. The above key and certificate were self-signed. ``` ### Username & Password * Used to securely store a username and password secret that can be used by a [workload's environment variables](/reference/workload#environment-variables). * The secret consists of: * Username * Password * Encoding (Base64 / Plain Text) * The secret will only be created or updated if the username and password are valid strings. ## Permissions The permissions below are used to define [policies](/reference/policy) together with one or more of the four [principal types](/concepts/access-control): | Permission | Description | Implies | | :--------- | :------------------------------------------------------------------ | :---------------------------------------------- | | create | Create new secrets | | | delete | Delete secrets | | | edit | Modify existing secrets | view, reveal | | manage | Full access | create, delete, edit, manage, reveal, use, view | | reveal | Reveal the plaintext of the secret | view | | use | Refer to this secret from other entities (gvcs, cloudaccounts, etc) | view | | view | Read-only access excluding plaintext | | ## Reveal Permission The plaintext of a secret can be viewed only if you or an [identity](/reference/identity) has the `reveal` permission. For example, when using a secret as the value of an [environment variable](/reference/workload#environment-variables) for a container, the [identity](/reference/identity) assigned to the [workload](/concepts/workload) must have the `reveal` permission set on the assigned secret using a [policy](/reference/policy). When configuring the [environment variable](/reference/workload#environment-variables), the value of the variable will be in the following format: `cpln://secret/SECRET_NAME`. The image running within the container will be able to access the plaintext of the secret by referring to the name of the configured [environment variable](/reference/workload#environment-variables). ## Use Permission A secret is used with a: * GVC's [pull secret](/reference/gvc#pull-secrets) * Container's [environment variables](/reference/workload#environment-variables) * Policy's [binding](/concepts/access-control#bindings) To allow a non-administrator user the ability to use a secret with one of the above objects, the user must be granted the `use` permission. For example, if the image assigned to a container belongs to a private Docker registry, a pull secret for that registry must be added to the associated GVC. The user that is configuring this application must have the `use` permission set on the secret (using a [policy](/reference/policy)) to be able to add it as a pull secret. ## Updating Secrets If a secret value is updated, any workload referencing that secret will need to be redeployed. ## Access Report Displays the permissions granted to principals for the secret. ## CLI To view the CLI documentation for secrets, see the [Secret CLI reference](/cli-reference/commands/secret). # Service Account Source: https://docs.controlplane.com/reference/serviceaccount Service accounts are headless principals for API and CLI access. Covers token generation, key management, profiles, and authentication for automation. ## Overview A service account is one of the [principal types](/concepts/access-control) of an [org](/reference/org) that can be granted specific access permissions to perform headless operations against the [API](/api-reference/api). A service account can be [mapped](#map-a-service-account-with-a-profile) to a [cpln profile](/cli-reference/commands/profile) and execute CLI commands on behalf of that account. ## Create a Service Account Refer to the [Create a Service Account](/guides/create-service-account) guide for additional details. ## Built-in Service Accounts The following service accounts are built-in and cannot be modified or deleted: | Service Account Name | Description | | :------------------- | :----------------------------------------------------------------------------------- | | controlplane | Built-in service account used by Control Plane to interact with this org's resources | ## Map a Service Account with a profile Using the following [CLI](/cli-reference/overview) command, a [profile](/cli-reference/commands/profile) will be created and mapped to a service account: ```bash theme={null} cpln profile create PROFILE_NAME --token GENERATED_KEY ``` This profile can be set as the default profile by using the command `cpln profile set-default PROFILE_NAME` or by using the CLI option `--profile PROFILE_NAME` to override the default when executing a command. ## Service Account Keys A service account can be associated with one or more **keys** that are generated by the platform. They are used to authenticate and authorize the service account to the API. When a new key is created, it will only be displayed and available for download **one time**. It cannot be retrieved again. If a key is lost, misplaced, or forgotten, it must be removed and regenerated. After a new key has been generated and saved, the UI will display the name of the key (which matches the first sixteen characters of the key) and the description entered. Individual keys can be removed if they are no longer used or have been compromised. ## Permissions The permissions below are used to define [policies](/reference/policy) together with one or more of the four [principal types](/concepts/access-control): | Permission | Description | Implies | | :--------- | :------------------------------------- | :----------------------------------------- | | addKey | Add key to an existing service account | | | create | Create new service accounts | | | delete | Delete service accounts | | | edit | Modify existing resources | view | | manage | Full access | addKey, create, delete, edit, manage, view | | view | Read-only access | | ## Access Report Displays the permissions granted to principals for the service account. ## CLI To view the CLI documentation for Service Accounts, see the [Service Account CLI reference](/cli-reference/commands/serviceaccount). # User Source: https://docs.controlplane.com/reference/user User management within organizations. Covers invitations, multi-org membership, built-in tags, and access control for org members. ## Overview A user is a member of one or more [orgs](/reference/org). Users are granted specific access permissions to the various resources within the platform. These grants can be applied to a user by being a member of a [group](/reference/group) or [policy](/reference/policy). ## Invite Users Refer to the [Invite Users](/guides/invite-users) guide for additional details on how to invite new users to your [org](/reference/org). ## Multiple Orgs A user can be a member of one or more [orgs](/reference/org). * To switch between [orgs](/reference/org) from the console: * Click on the profile dropdown in the upper right corner. * Click on the Org dropdown menu. * Click on the desired organization. ## Built-in Tags Each user has the following built-in [tags](/core/misc#tags): * `firebase/sign_in_provider` ## Permissions The permissions below are used to define [policies](/reference/policy) together with one or more of the four [principal types](/concepts/access-control): | Permission | Description | Implies | | :---------- | :--------------------------- | :---------------------------------------------- | | delete | Delete existing users | | | edit | Modify existing users | view | | impersonate | Lets you impersonate a user | | | invite | Can invite users to this org | | | manage | Full access | delete, edit, impersonate, invite, manage, view | | view | Read-only access | | ## Access Report Displays the permissions granted to principals for the user. ## CLI To view the CLI documentation for users, see the [User CLI reference](/cli-reference/commands/user). # Volume Set Source: https://docs.controlplane.com/reference/volumeset Persistent storage volumes for stateful workloads. Covers filesystem types (ext4, xfs, shared), autoscaling, snapshots, and capacity management. ## Overview A volume set is a collection of storage volumes, which may be linked to one or more workloads running on Control Plane. The behavior varies greatly depending on your choice of filesystem. ### Traditional File Systems (ext4, xfs) * Control Plane provisions a unique volume for each replica in the linked workload. * Each volume set can be used by at most one [stateful workload](/reference/workload/types#stateful). * Each volume in the set is bound to a single workload replica * Data is not replicated between volumes. If you require data sharing or replication, this must be accomplished at the application level (e.g. by using WAL streaming between two Postgresql instances) ### Shared File System * Control Plane provisions a single volume per location * The volume set can be attached to any number of workloads * All attached workload replicas in a given location use the same volume #### Caveats The shared filesystem does not support: * snapshots * certain commands: * `createVolumeSnapshot` * `deleteVolumeSnapshot` * `restoreVolume` * `deleteVolume` * `shrinkVolume` ### Capacity and Billing * When a volume is created, it will have an initial capacity defined by the spec of your volume set. * Volume capacity can be increased by sending an `expandVolume` command. * Volume capacity can be decreased using the `shrinkVolume` command, but this causes data loss (see [Volume Shrinkage](#volume-shrinkage) below). * The bill for a volume set is calculated by summing the reserved GB of all volumes. ## Autoscaling ### One Volume Per Replica This only applies to volume sets using traditional file systems (e.g. ext4, xfs) * Like workloads, volume sets scale horizontally. Every replica in the linked workload is automatically assigned a volume. * When the linked workload scales down, the volumes are preserved. Volumes are only deleted when you send a `deleteVolume` command, or when the volume set itself is deleted. ### Automatic Expansion (Reactive) The `spec.autoscaling` object allows you to specify rules for automatically expanding volumes in the set. When a volume's free space drops below a threshold, Control Plane expands it. The available options are: * `maxCapacity`: The largest allowable size (in GB) for any volume in the set. * `minFreePercentage`: This must be a number between 1 and 100. When the free percentage on any volume drops below this threshold, Control Plane will issue an `expandVolume` command automatically. * `scalingFactor`: This must be a number 1. Applied as a multiplier when calculating the new capacity, providing headroom beyond the minimum needed to restore `minFreePercentage`. When a volume needs expansion, the new capacity is calculated as: ```text theme={null} newCapacity = usedGB / (1 - minFreePercentage/100) × scalingFactor ``` The result is capped at `maxCapacity`. **Example calculation:** Suppose a 10 GB volume has 8 GB used (2 GB free, 20%), and the configuration is `minFreePercentage: 20`, `scalingFactor: 2`. | Step | Calculation | Result | | :---------------------------- | :------------------- | :----------------------- | | Current state | 8 GB used, 2 GB free | 20% free — threshold met | | Minimum capacity for 20% free | 8 / (1 - 0.20) | 10 GB | | Apply `scalingFactor` | 10 2 | 20 GB | The volume expands to 20 GB (assuming this is `maxCapacity`). ```json Volume Set with Reactive Autoscaling theme={null} { ... "spec": { ... "autoscaling": { "maxCapacity": 100, "minFreePercentage": 20, "scalingFactor": 2 } } } ``` ### Predictive Scaling Reactive scaling responds to low free space after it happens. In most cases this is sufficient, but workloads that experience rapid surges in storage consumption can outpace reactive scaling — the volume fills up faster than the expansion can complete. Predictive scaling addresses this by projecting future usage and expanding volumes *before* they run low. It is a **supplement** to reactive scaling, not a replacement. On each evaluation cycle, Control Plane computes both a reactive target and a predictive target, then uses **whichever is larger** (capped at `maxCapacity`). This means reactive scaling always acts as a safety net. #### How It Works When predictive scaling is enabled, Control Plane: 1. Queries volume usage over a configurable lookback window. 2. Fits a linear regression to compute the average growth rate. 3. Projects usage forward over a configurable projection window. 4. Calculates a predictive target capacity using the same formula as reactive scaling, but substituting **projected** usage for current usage: ```text theme={null} predictiveCapacity = projectedUsedGB / (1 - minFreePercentage/100) × predictiveScalingFactor ``` 5. Compares the predictive and reactive targets, and uses the larger of the two. Predictive scaling is skipped (falling back to reactive only) when any of the following are true: * The volume's growth rate is below `minGrowthRateGBPerHour`. * Fewer than `minDataPoints` data points are available in the lookback window, meaning the projection is not yet considered reliable. #### Configuration Add a `predictive` object inside `autoscaling` to enable predictive scaling: | Field | Type | Default | Description | | :----------------------- | :------ | :---------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `enabled` | boolean | `false` | Enable predictive scaling based on historical growth rates. | | `lookbackHours` | number | `24` | Hours of historical data to analyze for the growth rate calculation. Min: 1, Max: 168 (1 week). | | `projectionHours` | number | `6` | Hours into the future to project storage needs. Min: 1, Max: 72. | | `minDataPoints` | number | `10` | Minimum number of historical data points required before a projection is considered reliable. Prevents premature scaling decisions when insufficient data is available. Min: 2, Max: 100. | | `minGrowthRateGBPerHour` | number | `0.01` | Minimum growth rate (GB/hour) to trigger predictive expansion. Volumes growing slower than this rate will not trigger predictive scaling. | | `scalingFactor` | number | (inherited) | Scaling factor for predictive expansion. If not set, uses the parent `autoscaling.scalingFactor`. A lower value (e.g., 1.2) is recommended for gentler proactive scaling. Min: 1.1. | When `predictive.enabled` is `true`, the parent `autoscaling.minFreePercentage` and `autoscaling.scalingFactor` fields are required. #### Example ```json Volume Set with Predictive Scaling theme={null} { ... "spec": { ... "autoscaling": { "maxCapacity": 100, "minFreePercentage": 20, "scalingFactor": 2, "predictive": { "enabled": true, "lookbackHours": 48, "projectionHours": 12, "minDataPoints": 10, "minGrowthRateGBPerHour": 0.1, "scalingFactor": 1.2 } } } } ``` **Example calculation:** Suppose a 50 GB volume currently has 40 GB used (10 GB free), and its growth rate over the past 48 hours is 0.5 GB/hour. *Reactive target:* | Step | Calculation | Result | | :---------------------------- | :--------------------- | :----------------------- | | Current state | 40 GB used, 10 GB free | 20% free — threshold met | | Minimum capacity for 20% free | 40 / (1 - 0.20) | 50 GB | | Apply `scalingFactor` (2.0) | 50 2.0 | **100 GB** | *Predictive target:* | Step | Calculation | Result | | :------------------------------------- | :-------------- | :--------------- | | Project usage 12 hours ahead | 40 + (0.5 12) | 46 GB | | Minimum capacity for 20% free | 46 / (1 - 0.20) | 57.5 GB | | Apply predictive `scalingFactor` (1.2) | 57.5 1.2 | **⌈69⌉ = 69 GB** | Control Plane compares the two targets: reactive (100 GB) vs. predictive (69 GB). Since reactive is larger, the volume expands to **100 GB** (which also equals `maxCapacity` in this configuration). In a different scenario — say the volume still has plenty of free space but is growing rapidly — the predictive target could exceed the reactive target. The key principle is: **whichever target is larger gets used**, ensuring the volume is sized for both current needs and anticipated growth. ## Snapshots Snapshots can be taken at any time and (optionally) on a regular schedule. To set up automatic snapshotting, you may use the `spec.snapshots` object. Options include: * `retentionDuration`: The length of time to retain a newly created snapshot. This should be a floating point number followed by either d, h, or m (for day, hour or minute) * `schedule`: A cron expression describing the snapshot frequency. Snapshots cannot be taken more frequently than once per hour. ## File System Type Currently supported file systems are: * ext4 * xfs * shared ## Mount Resources For the shared file system only, you will be charged CPU and memory per mount point. Shared volumes must be mounted once per node. There will be **at most** one mount point per replica. You can control the minimum and maximum resource allocations per mount point using the `mountOptions` property. e.g. ```json theme={null} { "kind": "volumeset", "name": "my-shared-volumeset", "description": "", "tags": {}, "spec": { "fileSystemType": "shared", "initialCapacity": 10, "mountOptions": { "resources": { "maxCpu": "200m", "maxMemory": "128Mi", "minCpu": "100m", "minMemory": "128Mi" } }, "performanceClass": "shared" } } ``` ### Resource Constraints * `minCpu` and `maxCpu` can be at most 4000m apart * The ratio between `minCpu` and `maxCpu` must be at least 1:4 * `minMemory` and `maxMemory` can be at most 4096Mi apart * The ratio between `minMemory` and `maxMemory` must be at least 1:4 ## Performance Classes Each volume set has a single, immutable, performance class. The performance class determines: * How many Megabytes per second can be transferred to and from the volume (MB/second) * How many I/O operations can be processed per second. (IOPS) * Read/write latency Because these drives are served over the network, IOPS/throughput is limited per VM. The performance of individual drives will vary. Volume performance varies widely by cloud service provider. ### General-Purpose SSD **Name**: `general-purpose-ssd` **Minimum Capacity**: 10Gb **Maximum Capacity**: 65536Gb | Service Provider | Max Throughput | Max IOPS | | :--------------- | :------------- | :------- | | AWS | 125 MB/s | 3000 | | GCP | 1200 MB/s | 80000 | | Azure | 125 MB/s | 3000 | ### High-Throughput SSD In general, IOPS/throughput capacity varies linearly with storage capacity. The values shown below are the maximum possible values, and may only be achievable with large volume sizes. **Name**: `high-throughput-ssd` **Minimum Capacity**: 200Gb **Maximum Capacity**: 65536Gb | Service Provider | Max Throughput | Max IOPS | | :--------------- | :------------- | :------- | | AWS | 400 MB/s | 4600 | | GCP | 1200 MB/s | 100000 | | Azure | 1200 MB/s | 15500 | ## Custom Encryption (AWS) Control Plane encrypts all volumes by default. The custom encryption feature allows you to specify your own AWS KMS keys for encrypting volumes in a volume set, giving you complete control over the encryption keys used for your data. ### When to Use Custom Encryption Use custom encryption when you need to: * Meet specific compliance or regulatory requirements that mandate customer-managed keys * Maintain control over key lifecycle management (rotation, revocation, etc.) * Use separate encryption keys for different environments or applications * Integrate with existing key management workflows Custom encryption is currently only available for AWS. Support for other cloud providers is not yet available. ### Prerequisites Before configuring custom encryption, you need: 1. **An AWS KMS key** in each region where you want to use custom encryption 2. **Appropriate KMS key policies** configured for the KMS key to allow volume encryption #### Required Key Policies Your KMS key policy must permit to Control Plane to use the key for volume encryption. The policies below grant the necessary permissions. You must add these policies to any KMS key used with Control Plan volume sets. ```json theme={null} [ { "Sid": "GrantAccessToControlPlane", "Effect": "Allow", "Principal": { "AWS": [ "arn:aws:iam::957753459089:root" ] }, "Action": [ "kms:Decrypt", "kms:DescribeKey", "kms:Encrypt", "kms:GenerateDataKey", "kms:GenerateDataKeyWithoutPlaintext", "kms:ReEncrypt*", "kms:CreateGrant", "kms:RetireGrant", "kms:RevokeGrant" ], "Resource": "*" }, { "Sid": "GrantAccessToControlPlaneEC2", "Effect": "Allow", "Principal": { "Service": "ec2.amazonaws.com" }, "Action": [ "kms:CreateGrant", "kms:Decrypt", "kms:DescribeKey", "kms:GenerateDataKey" ], "Resource": "*", "Condition": { "StringEquals": { "kms:ViaService": "ec2.{REGION}.amazonaws.com", "kms:CallerAccount": "957753459089" } } } ] ``` ### Configuration Schema Custom encryption is configured in the volume set `spec` using the `customEncryption` object: ```json theme={null} { "spec": { "customEncryption": { "regions": { "{cloud-provider}-{region}": { "keyId": "arn:aws:kms:region:account:key/key-id" } } } } } ``` **Field Descriptions:** * `customEncryption.regions`: An object mapping region names to encryption configurations * `{cloud-provider}-{region}`: The region identifier in Control Plane format (e.g., `aws-us-east-1`, `aws-eu-west-1`) * `keyId`: The full ARN of the AWS KMS key to use for volumes in that region ### Region Naming Format Region names must follow the format: `aws-{aws-region-name}` Examples: * `aws-us-east-1` for US East (N. Virginia) * `aws-us-west-2` for US West (Oregon) * `aws-eu-west-1` for EU (Ireland) * `aws-ap-southeast-1` for Asia Pacific (Singapore) ### Complete Example Here's a full example of a volume set with custom encryption configured for two AWS regions: ```json Volume Set with Custom Encryption theme={null} { "kind": "volumeset", "name": "my-encrypted-volumeset", "description": "Production database volumes with custom encryption", "tags": { "app": "database", "env": "production" }, "spec": { "fileSystemType": "ext4", "initialCapacity": 100, "performanceClass": "high-throughput-ssd", "autoscaling": { "maxCapacity": 500, "minFreePercentage": 20, "scalingFactor": 1.5 }, "snapshots": { "retentionDuration": "7d", "schedule": "0 2 * * *" }, "customEncryption": { "regions": { "aws-us-east-1": { "keyId": "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012" }, "aws-eu-west-1": { "keyId": "arn:aws:kms:eu-west-1:123456789012:key/abcdefab-abcd-abcd-abcd-abcdefabcdef" } } } } } ``` ### Important Constraints Be aware of the following constraints when using custom encryption: **Key Immutability** * Once a volume is created with a specific KMS key, the encryption key cannot be changed * To use a different key, you must create new volumes **Regional Configuration** * Encryption keys are configured per region, not globally * Each region can have its own KMS key * If a region is not specified in `customEncryption.regions`, volumes in that region will use AWS default encryption **File System Support** * Custom encryption only works with traditional file systems: `ext4` and `xfs` * The `shared` file system does not support custom encryption **BYOK Clusters** * This feature is for Control Plane-managed clusters only * For BYOK clusters you have full control over your storage classes, and therefore the encryption method as well. ## Commands Volume sets support imperative operations on individual volumes and snapshots. To issue a command, send a `POST` to the volume set's `-command` endpoint. e.g. `POST https://api.cpln.io/org/my-org/gvc/my-gvc/volumeset/my-volume-set/-command`. These commands can also be created using the Control Plane console at [https://console.cpln.io](https://console.cpln.io) ## Volume Expansion Volumes can be expanded on-demand by issuing an `expandVolume` command. If the volume set is in-use by a workload, the corresponding workload replica will be restarted. You can only expand a volume 4 times in a rolling 24-hour period. Please plan accordingly. Volumes cannot be "expanded" to a smaller size. ### expandVolume Spec: * location * volumeIndex * newStorageCapacity For example: ```json expandVolume Command theme={null} { "type": "expandVolume", "spec": { "location": "aws-sa-east-1", "volumeIndex": 0, "newStorageCapacity": 11 } } ``` ## Volume Shrinkage Volumes can be shrunk on-demand by issuing a `shrinkVolume` command. If the volume set is in-use by a workload, the corresponding workload replica will be restarted. **CRITICAL: Shrinking a volume causes PERMANENT DATA LOSS.** The `shrinkVolume` command provisions a new volume at the smaller size. Existing data is not migrated - the old volume and all its data are permanently deleted. **Only use `shrinkVolume` for applications with built-in data redundancy:** * Apache Kafka (with proper replication factor) * Distributed databases with replication (Cassandra, CockroachDB, etc.) * Applications where data can be rebuilt from other replicas **Do not use `shrinkVolume` for:** * Single-replica stateful workloads * Databases without replication * Any application where data loss is unacceptable The `shrinkVolume` command is only available for `ext4` and `xfs` filesystems. It is not supported for `shared` filesystems. ### shrinkVolume Spec: * location * volumeIndex * newStorageCapacity For example: ```json shrinkVolume Command theme={null} { "type": "shrinkVolume", "spec": { "location": "aws-sa-east-1", "volumeIndex": 0, "newStorageCapacity": 5 } } ``` ## Volume Deletion To delete a volume, issue a `deleteVolume` command. ### deleteVolume This command deletes the specified volume's storage device. Note: the metadata for the volume at the specified index will not be removed from the volume set. Only your data will be deleted. ### Deleting an in-use volume If the volume set is in-use by a workload, a new storage device may be immediately created. e.g. if the volume set is in-use by a workload with one replica, and you delete the volume at index 0, Control Plane will: 1. Create an empty volume to service the workload 2. Delete the old volume as requested 3. Restart the workload replica, binding it to the volume created in step 1. For example: ```json deleteVolume Command theme={null} { "type": "deleteVolume", "spec": { "location": "aws-sa-east-1", "volumeIndex": 0 } } ``` ## Snapshots Each volume in a set has its own list of snapshots. You manipulate snapshots by issuing commands to the volume set. ### createVolumeSnapshot Take a snapshot for a given volume (specified by location and volume index). `snapshotName` must be unique for the target volume. Spec: * location * volumeIndex * snapshotName * snapshotExpirationDate * tags * Specify any key/value pair here. For example: ```json createVolumeSnapshot Command theme={null} { "type": "createVolumeSnapshot", "spec": { "location": "aws-sa-east-1", "volumeIndex": 0, "snapshotName": "snap-0", "snapshotExpirationDate": "2025-01-01T00:00:00Z", "tags": { "my-tag-key": "my-tag-value" } } } ``` ### deleteVolumeSnapshot Delete the specified snapshot. Spec: * location * volumeIndex * snapshotName For example: ```json deleteVolumeSnapshot Command theme={null} { "type": "deleteVolumeSnapshot", "spec": { "location": "aws-sa-east-1", "volumeIndex": 0, "snapshotName": "snap-0" } } ``` ### restoreVolume Restore the specified volume to one of its snapshots. If this volume set is in-use by a workload, the corresponding workload replica will restart. This operation creates an entirely new volume using the given snapshot. All unsaved data on the original volume will be lost. Spec: * location * volumeIndex * snapshotName For example: ```json restoreVolume Command theme={null} { "type": "restoreVolume", "spec": { "location": "aws-sa-east-1", "volumeIndex": 0, "snapshotName": "snap-0" } } ``` ## BYOK Support Volume sets are supported in BYOK locations as long as the following prerequisites are met: 1. The cluster must have a CSI-compatible storage driver installed. 2. You must create storage classes which use the CSI-compatible provisioner, with the following names: * `general-purpose-ssd-ext4` * `general-purpose-ssd-xfs` * `premium-low-latency-ssd-ext4` * `premium-low-latency-ssd-xfs` * `general-purpose-ssd-ext4-command` * `general-purpose-ssd-xfs-command` * `premium-low-latency-ssd-ext4-command` * `premium-low-latency-ssd-xfs-command` ## Planned Features * Automatic volume expansion. ## Permissions The permissions below are used to define [policies](/reference/policy) together with one or more of the four [principal types](/concepts/access-control): | Permission | Description | Implies | | :------------------------ | :---------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | create | Create new volumesets | | | delete | Delete existing volumesets | | | edit | Modify existing volumesets | view | | exec | Execute commands | exec.restoreVolume, exec.createVolumeSnapshot, exec.expandVolume, exec.deleteVolume, exec.deleteVolumeSnapshot, exec.shrinkVolume | | exec.createVolumeSnapshot | Create a snapshot of a volume | | | exec.deleteVolume | Delete a volume | | | exec.deleteVolumeSnapshot | Delete a volume snapshot | | | exec.expandVolume | Increase the storage capacity of a volume | | | exec.restoreVolume | Restore a volume to a snapshot | | | exec.shrinkVolume | Shrink a volume (causes data loss) | | | manage | Full access | create, delete, edit, exec, exec.createVolumeSnapshot, exec.deleteVolume, exec.deleteVolumeSnapshot, exec.expandVolume, exec.restoreVolume, exec.shrinkVolume, manage, view | | view | Read-only access | | ## Access Report Displays the permissions granted to principals for the volume set. ## CLI To view the CLI documentation for Volume Sets, see the [Volume Set CLI reference](/cli-reference/commands/volumeset). # Autoscaling Source: https://docs.controlplane.com/reference/workload/autoscaling Autoscaling strategies: disabled, concurrency-based, requests-per-second, CPU utilization, latency percentile, and memory-based with target values. ## Autoscaling Overview Workload auto-scaling is configured by setting a **strategy**, a **target** value, and in some cases as **metric percentile**. Together these values determine when the workload will scale up & down. As the system scales up, traffic will not be sent to the new replicas until they pass the readiness probe, if configured. If there is no probe configured or if it is a basic TCP port check, the requests will hit the new replicas before they are ready to respond. This could cause a delay or errors for end-user traffic. You can configure autoscaling in the default options for a workload (`defaultOptions`) and in any of the location-specific options. ## Scaling Strategies The scaling strategy is set using `autoscaling.metric`. * Disabled (`disabled`) * Scaling will be disabled. * Concurrent Requests Quantity (`concurrency`) * The average number of requests executing at a given point in time across all the replicas. `(requests * requestDuration)/(timePeriod * replicas)`. * Example: A workload with 5 replicas received 1000 requests with an average response time of 50ms (05 seconds) over a 1 second period. The concurrent requests metric for that period is `(1000 * .05)/(1 * 5) = 10`. * Requests Per Second (`rps`) * The raw number of requests received by a workload each second divided by the number of replicas. Requests are counted even if they haven't been completed. * Percentage of CPU Utilization (`cpu`) * The percentage of CPU consumed by system and user processes in the container(s) as specified in the container cpu field. * Request Latency (`latency`) * The request response time (at a configurable percentile) in milliseconds, averaged across all replicas. * Memory Utilization (`memory`) * The percentage of memory consumed by system and user processes in the container(s) as specified in the container memory field. Caveats when choosing a workload type and a scaling strategy: * Serverless workloads cannot use the `latency` scaling strategy or any of the Multi Metric scaling strategies. * Standard workloads cannot use the `concurrency` scaling strategy The scale to zero functionality is available for Serverless workloads when using the `rps` or `concurrency` scaling strategies, and for Standard and Stateful workloads when using the `keda` metric. Cron workloads cannot scale to zero. ## Autoscaling Target The target is the value that the system will try to keep the chosen scaling metric (such as `concurrency`, `rps`, `cpu`, `latency`, or `memory`) near but below. ## Autoscaling Standard Workloads For standard workloads, Control Plane runs two asynchronous control loops: 1. The Scaling Decision Loop 2. The Metric Calculation Loop Because of this asynchronous structure, autoscaling decisions may be made based on a metric value that is as old as the metric's collection rate (usually 20 seconds). ### The Scaling Decision Loop A workload's scale is evaluated every 15 seconds, using the value most recently calculated by the [Metric Calculation Loop](#the-metric-calculation-loop). Each time an evaluation is made the chosen metric is averaged across all available replicas and compared against the scale target. When scaling up, Control Plane does not enforce a stabilization window; the number of pods will increase as soon as the [scaling algorithm](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#algorithm-details) dictates. When scaling down, a stabilization window of 5 minutes is used; the highest number of pods recommended by the scaling algorithm within the past 5 minutes will be applied to the running workload. ### The Metric Calculation Loop #### Requests per Second Every 20 seconds, Control Plane calculates the average number of requests per second over the past 60 seconds. #### Latency Every 20 seconds, Control Plane calculates latency, using the response time of the workload once requests are received, using an average over the past 60 seconds at the specified percentile (p50, p75, p99). #### CPU Every 15 seconds, Control Plane calculates the average CPU usage over the past 15 seconds. #### Memory Every 15 seconds, Control Plane calculates the average memory usage over the past 15 seconds. ## Autoscaling Serverless Workloads The current capacity is evaluated every 2 seconds and compared against the scale target. It averages requests completed over the previous 60 seconds to avoid rapid changes. If ever a scaling decision is made which results in a scale increase above 200% then it suspends scale down decisions and averages over 6 seconds for 60 seconds. This is to allow for rapid scaling when a burst of traffic is detected. **Special considerations for the `latency` scaling strategy** Because request latency is represented as a distribution, when using the `latency` scaling strategy, you must choose a metric percentile by setting the `autoscaling.metricPercentile` property to one of the following values: * `p50` * `p75` * `p99` ## Autoscaling Options * Minimum Scale (`autoscaling.minScale`) * The minimum allowed number of replicas. * Control Plane can scale the workload down to 0 when there is no traffic and scale up immediately to fulfill new requests. * Must be between 0 and `Maximum Scale` inclusive. * Maximum Scale (`autoscaling.maxScale`) * The maximum allowed number of replicas. * Scale to Zero Delay (`autoscaling.scaleToZeroDelay`) * The amount of time (in seconds) with no requests received before a workload is scaled down to 0. * Must be between 30 and 3600 inclusive. * Maximum Concurrency (`autoscaling.maxConcurrency`) * A hard maximum for the number of concurrent requests allowed to a replica. * If no replicas are available to fulfill the request, it will be queued until a replica with capacity is available and delivered as soon as one is available again. * Capacity can be available from requests completing or when a new replica is available from scale out. * A value of 0 allows all requests. * Must be between 0 and 30000 inclusive. * Metric (`autoscaling.metric`) * Controls the metric which will be used for scaling decisions. The goal is to maintain the target across all replicas of a deployment. Options include: * `concurrency`: Uses the number of concurrent requests for the target. * `cpu`: Uses % processor time for the target. * `memory`: Uses memory in Mi for the target. * `rps`: Uses requests per second for the target. * `latency`: Uses the average request response time for the target. Not available for Serverless workloads. * Multi Metric (`autoscaling.multi`) * Allows specifying multiple metrics for autoscaling decisions. * Each metric must be unique and is defined with a target value. * Not available for Serverless workloads. * Metric Percentile (`autoscaling.metricPercentile`) * The latency metric is represented as a distribution, so a percentile within the distribution must be chosen to be used with the target. * The default value is `p50`. * Control Plane supports p50, p75, and p99 metric percentiles. * For example, if the percentile is p50 and the target is 100, when the 50th percentile of latency is greater than 100ms for the workload, a scale-up decision will be made. [Capacity AI](/reference/workload/capacity) is not available if CPU Utilization is selected because dynamic allocation of CPU resources cannot be accomplished while scaling replicas based on the usage of its CPU. Additionally, Capacity AI may not be enabled when multi metric is set. See [Capacity AI Restrictions](/reference/workload/capacity#caveats) for the full list. ## Autoscaling Examples ### Concurrency ```yaml theme={null} spec: defaultOptions: autoscaling: metric: concurrency maxConcurrency: 0 maxScale: 1 minScale: 5 scaleToZeroDelay: 300 target: 100 ``` ### CPU ```yaml theme={null} spec: defaultOptions: autoscaling: metric: cpu target: 80 minScale: 1 maxScale: 5 ``` ### Memory ```yaml theme={null} spec: defaultOptions: autoscaling: metric: memory target: 80 minScale: 1 maxScale: 5 ``` ### RPS ```yaml theme={null} spec: defaultOptions: autoscaling: metric: rps target: 100 minScale: 1 maxScale: 5 ``` ### Latency ```yaml theme={null} spec: defaultOptions: autoscaling: metric: latency target: 100 minScale: 1 maxScale: 5 metricPercentile: p50 ``` ### Multi Metric ```yaml theme={null} spec: defaultOptions: autoscaling: minScale: 1 maxScale: 5 multi: - metric: cpu target: 80 - metric: memory target: 80 ``` ## Keda In cases where the autoscaling strategies provided by Control Plane do not meet your needs, you can use [Keda](https://keda.sh/) to scale workloads based on custom metrics. Keda is a Kubernetes-based event-driven autoscaler that allows you to define custom scaling rules and metrics. A workload can use keda to scale based on custom metrics, like redis queue lengths or kafka topic lag. It is available for both standard and stateful workloads. In order to use keda, you must first [enable it on the gvc](/reference/gvc#keda) If Keda requires network or cloud resources, you must attach a valid identity on the keda configuration in the gvc ### Keda Configuration ```yaml theme={null} spec: defaultOptions: autoscaling: metric: keda keda: triggers: - type: redis metadata: address: my-redis.my-gvc.cpln.local:6379 queueLength: '5' passwordFromEnv: REDIS_PASSWORD usernameFromEnv: REDIS_USERNAME ``` ### Internal access If keda requires access to a Control Plane workload, that workload must be configured to allow keda access. ```yaml theme={null} spec: firewallConfig: internal: inboundAllowWorkload: - cpln://internal/keda ``` ### Advanced Options For a more advanced setup, perhaps utilizing multiple triggers or additional configurations, you can define the keda advanced section in the workload spec: ```yaml theme={null} spec: defaultOptions: autoscaling: metric: keda keda: triggers: ... advanced: scalingModifiers: activationTarget: '0' formula: >- (latency > 3000) ? (existing_pods) : min(max((queue_length / 15) + 1, 1), existing_pods * 1.15 + 1, existing_pods + 7) metricType: AverageValue target: '1' ``` ### TriggerAuthentication If your keda trigger requires authentication, you can reference the TriggerAuthentication object in the config. The name will be the name of the secret added to the [gvc keda configuration](/reference/gvc#keda) ```yaml theme={null} spec: defaultOptions: autoscaling: metric: keda keda: triggers: - type: kafka metadata: bootstrapServers: kafka.my-gvc.cpln.local:9092 consumerGroup: my-group topic: test-topic authenticationRef: name: ``` ### Prometheus metrics You can configure keda to use prometheus metrics for autoscaling. First, follow [these steps](/guides/export-metrics#steps) to create a service account with access to `readMetrics`. Then, configure the keda trigger with the service account token: ```yaml theme={null} spec: defaultOptions: autoscaling: metric: keda keda: triggers: - type: prometheus metadata: query: > (histogram_quantile(90/100 , sum(rate(request_duration_ms_bucket{workload="workload-name", gvc="gvc-name"}[1m])) by(gvc,workload, le))) threshold: '1' customHeaders: >- Authorization=Bearer serverAddress: https://metrics.cpln.io:443/metrics/org/ activationThreshold: '0' name: workload-latency ``` #### Query Examples You can customize the PromQL query to use different metrics and calculations: **P99 Latency:** ```bash theme={null} histogram_quantile(0.99, sum(rate(request_duration_ms_bucket{ gvc="my-gvc", workload="target-workload" }[5m])) by (le) ) ``` **Average Latency:** ```bash theme={null} sum(rate(request_duration_ms_sum{ gvc="my-gvc", workload="target-workload" }[5m])) / sum(rate(request_duration_ms_count{ gvc="my-gvc", workload="target-workload" }[5m])) ``` **Container Restarts (scale when restarts exceed threshold):** ```bash theme={null} sum(rate(container_restarts{ gvc="my-gvc", workload="problematic-service" }[5m])) ``` **Ready Replicas (scale when ready replicas fall below expected):** ```bash theme={null} sum(workload_ready_replicas{ gvc="my-gvc", workload="backend-service" }) ``` **Requests Per Second (across all replicas):** ```bash theme={null} sum(requests_per_second{ gvc="my-gvc", workload="api-gateway" }) ``` **Custom Metrics:** If your workload exposes custom Prometheus metrics (see [Custom Metrics documentation](/reference/workload/custom-metrics)), you can use them for scaling: ```bash theme={null} # Example: Scale based on queue depth sum(my_app_queue_depth{ gvc="my-gvc", workload="queue-processor" }) ``` ```bash theme={null} # Example: Scale based on cache hit ratio sum(rate(my_app_cache_misses_total{ gvc="my-gvc", workload="cache-service" }[5m])) / sum(rate(my_app_cache_requests_total{ gvc="my-gvc", workload="cache-service" }[5m])) ``` #### How It Works 1. **Metrics Collection**: Control Plane automatically collects default metrics for all workloads (latency, requests, replicas, etc.) and can be configured to collect [Custom Metrics](/reference/workload/custom-metrics) you expose from your application 2. **KEDA Polling**: KEDA polls the Prometheus endpoint at `metrics.cpln.io` using the service account token for authentication 3. **Query Evaluation**: The PromQL query is evaluated against the metrics to calculate the current value 4. **Scaling Decision**: When the metric value exceeds the threshold, KEDA triggers a scale-up event; when it falls below, KEDA scales down #### Additional Resources * [Default Metrics](/guides/default-metrics) - List of available metrics * [KEDA Prometheus Scaler Documentation](https://keda.sh/docs/latest/scalers/prometheus/) # Capacity AI Source: https://docs.controlplane.com/reference/workload/capacity Automatically optimize container CPU and memory allocation using historical usage analysis to reduce costs while maintaining performance. ## Overview Workloads can leverage intelligent allocation of their container's resources (CPU and Memory) by using Capacity AI. Capacity AI uses an analysis of historical usage to adjust the resources up/down between the configured minimum and maximum values of each container. This can significantly reduce cost but may, in rare cases, cause temporary performance issues with sudden spikes in usage depending on the autoscaling settings of the workload. If capacity AI is disabled, the amount of resources configured is set using the provided cpu and memory settings for each container. ## Availability by workload type Capacity AI is supported on every workload type: standard, serverless, cron, stateful, and VM. Standard, serverless, and cron workloads enable it by default. ## Resource Minimums `minCpu` and `minMemory` are typically used only when Capacity AI is enabled. However, [stateful workloads](/reference/workload/types#stateful) always respect these fields. On a stateful workload these fields stay bounded whether Capacity AI is enabled or disabled. See [Cost Optimization](/reference/workload/types#cost-optimization) for the limits. ## Limit Capacity AI update frequency You can set the highest frequency that Capacity AI is allowed to update the resources of a workload by setting the `capacityAIUpdateMinutes` field in the workload options. By defualt (when not set), Capacity AI can update the resources as frequently as necessary. ```yaml YAML theme={null} kind: workload spec: defaultOptions: capacityAIUpdateMinutes: 30 # Capacity AI can update resources at most once every 30 minutes ``` ## In-place Resource Sizing When Capacity AI adjusts the CPU or memory allocations for containers, the corresponding Kubernetes pods will have their resources resized **in place**, without requiring a pod restart or recreation. This applies to standard and stateful workloads where the underlying cluster supports pod resizing. Where it does not, the new reservation is applied with a rolling update, as described in [Termination](/reference/workload/termination#when-termination-occurs). Serverless workloads roll a new revision instead. Cron workloads apply the new reservation on their next scheduled execution, because every run starts a new pod. A run already in progress is never resized. Some programs may not take advantage of the updated resources automatically. E.g. if a program reserves resources on startup and does not scale dynamically. ## Caveats | Restriction | Reason | | :--------------------------------------------- | :------------------------------------------------------ | | Not available with CPU Utilization autoscaling | Dynamic CPU allocation conflicts with CPU-based scaling | | Not available with multi-metric autoscaling | Multi-metric requires stable resource baselines | | Not available with GPU containers | GPU allocation is fixed, not dynamic | See [Autoscaling](/reference/workload/autoscaling) for scaling strategy details and [Workload Types](/reference/workload/types) for type-specific capabilities. Capacity AI will prevent the ratio of memory and cpu from diverging by a large percentage. Changes made to a workload will reset its historical usage and will restart the analysis process. ## Minimum Capacity AI When resources are not being used, Capacity AI will downscale CPU usage to a minimum of 25 millicores. The minimum will increase depending on the memory size being recommended by Capacity AI using a 1:3 ratio of CPU millicores to memory MiB. # Containers Source: https://docs.controlplane.com/reference/workload/containers Container configuration reference covering image sources, resource limits (CPU/memory), ports, environment variables, liveness and readiness probes, and volumes. ## Overview For general information about containers, refer to the [Kubernetes containers](https://kubernetes.io/docs/concepts/containers/) concepts page. Workloads must have at least one container configured with the following: * [Image](#images) * [Resources](#resources) (CPU and Memory) * [Port(s)](#ports) that the container exposes * optional [Environment Variables](#environment-variables) * optional [Container Overrides](#container-overrides) * optional [Custom Metrics](/reference/workload/custom-metrics) * optional [Readiness / Liveness Probes](#probes) * optional [Volumes](/reference/workload/volumes) * optional [Lifecycle](#lifecycle) If a workload has more than one container, only one can serve traffic on the default global endpoint, however additional containers can receive traffic using a custom domain. The following rules apply to the name of a container: * Cannot be: 'istio-proxy', 'queue-proxy', 'istio-validation'. * Cannot start with: `cpln_`. ## Images Each workload must be configured with at least one container, associated with an image. Images can be pulled from: * A public registry * If the image does not require authentication, only the image name and optional tag are required. * If authentication is required, a [pull secret](/reference/gvc#pull-secrets) must be configured on the [GVC](/concepts/gvc) containing the [workload](/concepts/workload). * Org's private registry * If the image resides in your org's private registry, no [pull secret](/reference/gvc#pull-secrets) is required and you may use one of the following for the image name: * Full Name: `/org/ORG_NAME/image/IMAGE_NAME:TAG` * Short Name: `//image/IMAGE_NAME:TAG` * An external private registry * Create a [pull secret](/reference/gvc#pull-secrets) with the registry credentials and configure it for the GVC. Images must be formatted for linux/amd64 to run on the Control Plane Cloud Platform managed locations. When using a BYOK location additional runtime platforms are available. Control Plane supports the use of dynamic tags. If an image is pushed and the tag is not updated (e.g., `latest`), the platform will redeploy the new image within 5 minutes. To enable this feature: * Using the console * Toggle the `Support Dynamic Tag` switch on the `Info` page of the selected workload. * Using [cpln apply](/guides/cpln-apply) * Add the element `supportDynamicTags: true` within the selected workload. The UserID 1337 is restricted. If used then inbound and outbound communication to the workload will be disabled. One known case where this UserID is configured by default is with [Laravel Sail](https://github.com/laravel/sail). If you need to use Laravel Sail on Control Plane then you must change the default UserID. Contact support for assistance. ## Ports Workloads can expose ports to be accessed by other workloads internally, externally from the provided default endpoints and from Domains. Serverless workloads must expose one port. Cron workloads may not expose ports. Standard workloads can expose any number of ports (including zero). The default endpoints will route traffic to the first port exposed by the first container of a workload. ```yaml YAML theme={null} ports: - number: 8080 protocol: http ``` ### Number The port to expose externally or internally to other Control Plane workloads. Access is controlled using the workload [firewall](#firewall). Ports which are not exposed or are only accessed from another container in the same workload do NOT need to be defined. The ports listed below are blocked and are not allowed to be used. Containers which attempt to use these ports will not be able to bind. 8012, 8022, 9090, 9091, 15000, 15001, 15006, 15020, 15021, 15090, 41000 ### Protocol * grpc * http * http2 * tcp If the protocol is not specified when using the `ports` then `http` is configured. If the deprecated `port` parameter is used then `http2` is configured. When the protocol is `tcp`... 1. External access to TCP ports of workloads is not available when using default external endpoints or standard custom domains. 2. The inbound allow CIDR list in firewall is always restricted to TCP ports when using default external endpoints or standard custom domains. To enable external access through TCP and inbound firewall functionality you must: 1. Enable [Dedicated Load Balancer](/reference/gvc#dedicated-load-balancer) on the GVC. 2. Configure access using a [Domain](/reference/domain) and a [custom port](/reference/domain#custom-ports) configured with TCP. ## Probes Probes are used to check the health of an application running inside a container. ### Readiness Probe The readiness probe is used to determine if the workload replica is ready to receive traffic. For example, if the application is performing some actions during start-up and needs it to complete before serving requests, the readiness probe should fail until the actions have been completed. This check is used in two ways. 1. Determines if replicas from a new version of the workload are ready, when the check passes the rollout continues, when the check fails the rollout is paused. 2. Determines if the workload replica should receive new requests from end users. When the readiness probe is failing the replica is removed the pool of available replicas for this workload and all endpoints. It is recommended to use an HTTP or Command probes that perform an adequate check that the workload is healthy and able to respond to requests. ### Liveness Probe The liveness probe defines when the container should be restarted. For example, if the application code hits a deadlock condition, the liveness probe can catch that the container is not healthy, and Control Plane will restart the failing workload replica. This will ensure that the application is available as much as possible until the defect causing the deadlock is fixed. ### Options Health Check Type: * Run a Custom Command. * HTTP * Scheme (either HTTP or HTTPS, default is HTTP and should almost always be HTTP). * Path. * Port (must be between 80 and 65535 inclusive). * Optional HTTP headers. * TCP * Socket port (must be between 80 and 65535 inclusive. Ports 8012, 8022, 9090, 9091, 15000, 15001, 15006, 15020, 15021, 15090, 41000 are invalid). * gRPC * gRPC port. Configurable Limits: * Initial Delay Seconds * The delay to wait after the container is started before performing the first probe (must be between 0 and 120 inclusive, default is 0). * Period Seconds * How often to perform the probe (must be between 1 and 60 inclusive, default is 10). * Timeout Seconds * Number of seconds after which the probe times out (must be between 1 and 60 inclusive, default is 1). * Success Threshold * Minimum consecutive successes for the probe to be considered successful after having failed (must be between 1 and 20 inclusive, default is 1). * Failure Threshold * When a probe fails, Kubernetes will try this amount of times before giving up. For a liveness probe, the container will be restarted. For a readiness probe, the workload will be marked `Unready`. (must be between 1 and 20 inclusive, default is 3). Refer to the [Kubernetes probe documentation](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) for additional details. ## Resources Control Plane allows you to specify CPU and memory resources for each container. Stateful workloads treat `minMemory` and `minCpu` differently than other workload types. For more information, please consult the [stateful workload reference](/reference/workload/types#stateful) ### CPU CPU is specified in **millicores (m)** and **cores**. * **Millicore**: Since a whole core might be too much for many applications, the platform allows you to specify CPU resources in millicores. One core is equivalent to 1,000 millicores. Specifying `500` (in the console) or `500m` (in the YAML manifest) would allocate half a core. * **Core**: A core is a CPU unit. Specifying `1` (in both the console and YAML manifest) as the CPU resource means allocating the equivalent of one full CPU core. #### minCpu When CapacityAI is enabled, `minCpu` defines the lowest possible CPU reservation for a container. Otherwise, the `minCpu` property is ignored. ### Memory Memory is specified in **Mebibytes (Mi)** and **Gibibytes (Gi)**. * **Mebibyte**: A unit of digital data that is equal to 1,048,576 bytes. Specifying `1` and selecting Mi (in the console) or `1Mi` (in the YAML manifest) would allocate 1 Mebibyte. * **Gibibyte**: A unit of digital data that is equal to 1,073,741,824 bytes. Specifying `1` and selecting Gi (in the console) or `1Gi` (in the YAML manifest) would allocate 1 Gibibyte. #### minMemory When CapacityAI is enabled, `minMemory` defines the lowest possible memory reservation for a container. Otherwise, the `minMemory` property is ignored. ### GPU Workloads can be attached to the following GPUs at runtime: | GPU | # per replica | | :---------- | :-----------: | | Nvidia T4 | 1-4 | | Nvidia A10g | 1 | The drivers for the card will be installed and available for use automatically. Driver versions are maintained by Control Plane and kept current. When a GPU is selected, minimum values for CPU and Memory must be met and CapacityAI is not allowed to be enabled. There are no additional charges for GPUs. The standard CPU, Memory and Egress charges are used. Example workload container with 4 Nvidia T4 GPUs: ```yaml YAML theme={null} gpu: nvidia: model: t4 quantity: 4 ``` ## Environment Variables Custom environment variables can be made available to the image running within a container. The value of the variable can be in plain text or a [secret value](#secret-variables). The length of an environment variable value cannot be greater than 4096 characters. ### Secret Variables Sensitive values can be used as an environment variable by using a [secret](/reference/secret). The [identity](#identity) of the workload must be member of a [policy](/reference/policy) that has the `reveal` permissions on the [secret](/reference/secret). When adding an environment variable using the UI, a list of available secrets can be accessed by pressing Control-S within the value textbox. If you do not have any secrets defined, the prefix `cpln://secret/` will be inserted. ### Reference Variables Environment variables can take on values from the pod manifest. To reference a field, prefix the field with `cpln://reference/` The supported fields are: `metadata.name`, `metadata.namespace`, `spec.nodeName`, `status.hostIP`, `status.podIP`, and `status.podIPs` ### PORT Variable The `PORT` environment variable is provided at runtime and available to a container. It can be assigned as a custom environment variable in all cases except when the container is exposed and the value doesn't match that of the exposed port. For example: * If the container is exposed with a port of `3000`: * the system will **accept** a PORT environment variable with the value `3000`. * the system will **deny** a PORT environment variable with any value other than `3000`. * If the container is not exposed then any value is accepted for the PORT environment variable. ### Environment Variable Inheritance Environment variables may be set at the [GVC](/reference/gvc#environment-variables) level. These variables are available to any container running in the GVC on an opt-in basis. To opt in, set the container's `inheritEnv` property to `true`. You can override the value of an inherited variable by adding a local variable with the same key. ### Use by Other Variables Environment variables can contain the values of other environment variables by following the pattern `$(ENV_NAME)`. For example, the runtime value of the built-in variable `CPLN_GLOBAL_ENDPOINT` can be used in another environment variable by having the value `$(CPLN_GLOBAL_ENDPOINT)` added to the new variable. ### Disallowed Variables The following variable names are not allowed to be used as a custom environment variable: * `K_SERVICE` * `K_CONFIGURATION` * `K_REVISION` ### Import Variables A .env file can be uploaded using the console to import multiple environment variables. [Secret](#secret-variables) values are supported. ```text Sample .env file theme={null} URL=http://test.example.com USERNAME=user001 PASSWORD=cpln://secret/username_secret.password DATA=cpln://secret/opaque_secret.payload ``` ## Container overrides A custom `command` (entrypoint), `args` (arguments) or `workingDir` can be provided for the container which will override the startup configuration of the container. ### Command The container entrypoint can be overridden by entering a custom command value. ### Arguments The arguments will be appended to the image `ENTRYPOINT` if no command is specified. The argument list is ordered and will be passed to the container in the same order it is defined in the api. ### Working Directory The `workingDir` setting changes the directory that commands are executed in when no path or relative paths are specified. ## Lifecycle Each Workload container can be configured to execute a subset of the [Kubernetes lifecycle hooks](https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/). The supported hooks are: ### PostStart This hook is executed immediately after a container is created. However, there is no guarantee that the hook will execute before the container ENTRYPOINT. In the event of a failure, the relevant error message will be recorded in the corresponding deployment. ### PreStop This hook is executed immediately before a container is stopped. In the event of a failure, the relevant error message will be recorded in the workload's event log ### Setting lifecycle hooks These hooks can be configured using the console or [cpln apply](/guides/cpln-apply). * Using the console * From the workload container, select the `Lifecycle` link from the top menu bar. * Enter the command and optional arguments. * Click `Save`. * Using [cpln apply](/guides/cpln-apply) * Only the `exec` type is supported. * Example: **Add the `lifecycle` section to an existing workload container.** ```yaml Workload Spec theme={null} spec: containers: - name: advanced-options-example args: [] cpu: 50m env: [] image: '//image/IMAGE:TAG' memory: 128Mi port: 8080 lifecycle: postStart: exec: command: - sh - '-c' - sleep 10 preStop: exec: command: - sh - '-c' - sleep 10 ``` # Custom Metrics Source: https://docs.controlplane.com/reference/workload/custom-metrics Collect Prometheus-formatted custom metrics from workload containers and use them for autoscaling and monitoring. ## Overview Control Plane can collect custom metrics from your workload by having your application emit a [Prometheus formatted list of metrics](https://prometheus.io/docs/practices/naming/) at a path and port of your choosing. The port can be different than the one serving traffic. Each container in a workload can be configured with metrics. The convention is to use the path `/metrics`, but any path can be used. Sample output from the metrics endpoint: ```text theme={null} MY_COUNTER 788 MY_COUNTER_2 123 NUM_USERS 2 NUM_ORDERS 91 ``` The platform will scrape all the replicas in the workload every 30 seconds with a 5 second timeout. Metric names with the prefix `cpln_` will be ignored by the scrapping process. The collected metrics can be viewed by clicking the `Metrics` link on the workload page within the console. Clear any existing query and enter the name of the metric. Click `Run Query` to execute. The time-series displayed will include these labels: * `org` * `gvc` * `location` * `provider` * `region` * `cluster_id` * `replica` ## Configuring Custom Metrics Scraping 1. In the UI, navigate to `Workloads` in the selected [GVC](/reference/gvc) and select the workload for which you want to scrape metrics. 2. Select the appropriate container name under `Containers`, then choose `Metrics`. 3. Configure the `Path` and `Port` where the container serves metrics, then save the settings. 4. The workload will automatically redeploy to apply the changes. Metrics will be collected once the rollout is complete. Workload with the Actions dropdown open showing the Export sub-menu with JSON, YAML, JSON Slim, YAML Slim, and Terraform options All objects in the UI can be exported to YAML, JSON, or Terraform formats by clicking `Actions` and selecting `Export`. For the configuration schema to use with the API, please refer to the `API Reference` in the documentation, under the `Workload` section.\ For Terraform documentation, see the [Workload metrics Terraform resource documentation](https://registry.terraform.io/providers/controlplane-com/cpln/latest/docs/resources/workload#nestedblock--container--metrics). To create dashboards for collected custom metrics, please refer to the [Grafana documentation](https://grafana.com/docs/grafana/latest/dashboards/build-dashboards/). ## Filter metrics If you wish to filter which metrics are collected from your workload, you can specify regex patterns in the `dropMetrics` field. ```yaml YAML theme={null} kind: workload spec: containers: - name: my-container metrics: path: /metrics port: 9090 dropMetrics: - '^go_.*' # Drop all go runtime metrics - '^process_.*' # Drop all process metrics - 'MY_UNWANTED_METRIC' # Drop a specific metric ``` # Firewall Source: https://docs.controlplane.com/reference/workload/firewall External and internal firewall rules for inbound and outbound traffic. Covers CIDR allowlists, hostname filtering, and HTTP header-based access control. Inbound network access is only available for workloads of types `serverless`, `standard`, and `stateful`. For other workload types, only outbound firewall settings are relevant. ## External The external firewall is used to control Internet traffic to/from a workload. ```yaml YAML theme={null} spec: firewallConfig: external: inboundAllowCIDR: - 1.2.3.0/24 outboundAllowCIDR: - 1.2.3.0/24 outboundAllowHostname: - example.com outboundAllowPort: - protocol: http number: 80 inboundBlockedCIDR: - 1.2.3.4/32 outboundBlockedCIDR: - 1.2.3.4/32 http: inboundHeaderFilter: - key: foo allowedValues: - bar.* ``` ### Inbound Requests * By default, all inbound requests are disabled. * Access is granted by explicitly adding one or more IPv4 / IPv6 / [CIDR](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing) addresses or allowing all addresses. * Using the UI: * Multiple addresses can be entered within the textbox by delimiting each address with either a comma or space. * An import file can be uploaded containing each address on its own line or delimited with either a comma or space. The CIDR address `0.0.0.0/0` allows full inbound access from the public Internet. ### Outbound Requests * By default, all outbound requests are disabled. * By default, or by specifying NO outbound ports, all are ALLOWED. * When outbound ports are specified, only traffic to those ports will be allowed. * Access is granted by explicitly adding one or more IPv4 / IPv6 / [CIDR](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing) addresses or public hostnames or allowing all addresses / hostnames. * **When using a hostname, only ports 80, 443, and 445 will be reachable. To allow all ports, enable all outbound requests.** * When using an IP or CIDR, all ports will be reachable. * The IP/CIDR addresses takes precedence over hostnames. * Using the UI: * Multiple addresses can be entered within the textbox by delimiting each address with either a comma or space. * An import file can be uploaded containing each address on its own line or delimited with either a comma or space. The CIDR address `0.0.0.0/0` allows full outbound access to the public Internet. ### Blocked Addresses All addresses specified will be blocked from outbound/inbound requests to/from this workload ```yaml YAML theme={null} spec: firewallConfig: external: inboundBlockedCIDR: - 1.2.3.4/32 outboundBlockedCIDR: - 1.2.3.4/32 ``` * Blocking an address has the same effect as not including it in the allow CIDR list ### Inbound HTTP Header Filter Using the `inboundHeaderFilter` field, you can filter out requests that do not meet certain http header criteria. You can permit permit headers that contain a specific header/value pair or deny them. This can be used in conjunction with geo-headers, to allow/deny requests originating from specific locations. If multiple allow or deny filters are specified, the resulting operation will be triggered if ANY value is matched. It does not have to match all of them. The values are regular expressions (RE2) #### Examples Only allows requests with the foo header, with any value: ```yaml YAML theme={null} spec: firewallConfig: http: inboundHeaderFilter: - key: foo allowedValues: - .* ``` Allows all requests except those with the foo=bar header ```yaml YAML theme={null} spec: firewallConfig: http: inboundHeaderFilter: - key: foo blockedValues: - ^bar$ ``` Since the values are regular expressions, ^ must be put at the start and \$ at the end to match a whole string (and not also match strings like `"barbell"`) #### Geo Filtering The inbound header filter can be configured to be used in conjunction with geo headers. Here is an example where we only allow requests coming from the United States ```yaml YAML theme={null} spec: loadBalancer: geoLocation: enabled: true headers: country: x-country firewallConfig: http: inboundHeaderFilter: - key: x-country allowedValues: - United States ``` ## Internal The internal firewall is used to control access between other workloads within an [org](/reference/org). Only the ports listed in the workload containers array will be made accessible to other workloads. This example allows workloads running in the same [GVC](/reference/gvc) to access this workload. ```yaml YAML theme={null} spec: firewallConfig: internal: inboundAllowType: same-gvc ``` This example allows a few specific workloads across the org to access this workload. ```yaml YAML theme={null} spec: firewallConfig: internal: inboundAllowType: workload-list inboundAllowWorkload: - //gvc/amazingapp/workload/users - //gvc/bestapp/workload/orders ``` This example allows a few specific workloads across the org and all workloads in the same GVC to access this workload. ```yaml YAML theme={null} spec: firewallConfig: internal: inboundAllowType: same-gvc inboundAllowWorkload: - //gvc/amazingapp/workload/users - //gvc/bestapp/workload/orders ``` **Available Options:** * `none`: No access is allowed between workloads. * `same-gvc`: Workloads running in the same [GVC](/reference/gvc) are accessible. * `same-org`: Workloads running in the same [org](/reference/org) are accessible. * `workload-list`: Specific workloads are allowed to access this workload. * These workloads can be from the same or different GVCs. * The user configuring this setting must have the `view` permission, set within a [policy](/reference/policy#permissions), on the workload being specified. * Use `inboundAllowWorkload` to list out the allowed workloads using their links. # General Source: https://docs.controlplane.com/reference/workload/general Workload reference overview covering access reports, identity assignment, the connect feature for debugging, and replica management across locations. ## Workload Overview Refer to the [Workload concepts](/concepts/workload) page. ## Create a Workload Refer to the [Create a Workload](/guides/create-workload) guide for additional details. ## Access Report Displays the permissions granted to principals for the workload. ## Identity Refer to the [identities](/reference/identity) page for additional details. ## Connect A specific replica of a workload can be connected to (similar to `exec`) from either the console or the CLI. This can be used for troubleshooting any issues with the replica. To connect using the console, click the `Connect` link from a workload. Select the location, container, replica, and command. Click `Connect` to execute the command. By default, the `bash` shell will be executed. To connect using the CLI, review the workload [connect](/cli-reference/commands/workload#workload-connect) subcommand. ## Debug In order to see detailed routing for the global georouted endpoint of a workload, debug values can be included within the response headers of a workload's endpoint request. The values will only be returned when: 1. `debug` is active and the header `x-cpln-debug: true` is in the request. 2. The global or canonical endpoint is being requested. Using the console, debug can be activated by: * Clicking `Options`. * Clicking the `Debug` switch to `on`. * Clicking `Save`. After the workload redeploys, the response from the workload's endpoint will contain the following headers if the header `x-cpln-debug: true` is in the request: * `x-cpln-location`: Location of the responding replica. * `x-cpln-replica`: Name of the responding replica. ```text Sample Request Headers theme={null} GET https://doc-test-v39red0.cpln.app/ HTTP/1.1 Host: doc-test-v39red0.cpln.app Connection: keep-alive x-cpln-debug: true ``` ```text Sample Response Headers theme={null} HTTP/1.1 200 OK content-length: 2993 content-type: text/plain date: Fri, 10 Sep 2021 21:34:27 GMT x-envoy-upstream-service-time: 2 x-cpln-location: aws-us-west-2 x-cpln-replica: doc-test-00083-deployment-75584b7d66-f8wtb ``` ## Endpoints ### Canonical Endpoint (global) This URL is globally load-balanced and TLS terminated. This can be used for testing if there is an issue with the custom domain that is associated with the [GVC](/reference/gvc). The endpoint can be configured to use the [org endpoint prefix](/reference/gvc#endpoint-naming-format). This adds the prefix as a subdomain to `cpln.app`. #### Canonical Endpoint Formatting The format depends on whether the GVC is configured to use an org-prefixed name. * default name: `$workloadName-$gvcAlias.cpln.app` * org-prefixed name: `$workloadName-$gvcAlias.$orgEndpointPrefix.cpln.app` ### Location-Specific Endpoint One endpoint will be created for each location in the [GVC](/reference/gvc). Format: `$workloadName-$gvcAlias.$locationName.controlplane.us` ### Replica-Direct Endpoints For stateful workloads, Control Plane optionally provides endpoints which point directly to each workload replica. Format: `$workloadName-$gvcAlias-$replicaIndex.$locationName.controlplane.us` Example: stateful-workload-name-cry3tqvce07s4-0.aws-us-west-2.controlplane.us These are built-in `controlplane.us` endpoints. For per-replica access under your own custom domain, see [Replica Direct Routing](/reference/domain#replica-direct-routing) in the Domain reference. ### Custom Domain Endpoints Additional globally load-balanced endpoints will show in the workload status for each domain route that is configured to use this workload. ### Internal Mutual-TLS Endpoint Each workload can be allowed to receive requests from other workloads in the same org internally using the provided internal endpoint. Access to this endpoint is controlled by the internal firewall settings. #### Internal Endpoint Formatting Typically, the workload endpoint is used. For stateful workloads with `replicaDirect` enabled, you may also use the replica-direct endpoint * workload: `$workloadName.$gvcName.cpln.local:$port` * replica-direct: `replica-$replicaIndex.$workloadName.$locationName.$gvcName.cpln.local:$port` ### Finding Replica Hostnames When `replicaDirect` is enabled on a stateful workload, each replica is reachable at its own stable hostname, letting you target a specific replica instead of load-balancing across all of them: * Internal (workload-to-workload): use the [replica-direct internal endpoint](#internal-endpoint-formatting), `replica-$replicaIndex.$workloadName.$locationName.$gvcName.cpln.local:$port`. * External: use the [replica-direct endpoint](#replica-direct-endpoints), `$workloadName-$gvcAlias-$replicaIndex.$locationName.controlplane.us`. Replica indexes start at `0` and run to one less than the replica count, so you can enumerate every replica's hostname from the workload name, location, GVC, and index. These names are served by Control Plane's internal DNS, so you construct them directly — there's no need to connect into the container to discover them. ## Publish Not-Ready Addresses By default, Control Plane only routes internal traffic to workload replicas that have passed their readiness probe. For clustered applications that need to discover peers during startup (before becoming ready), you can enable publishing not-ready addresses. To enable this feature, add the following tag to your workload: | Tag | Value | | :------------------------------ | :----- | | `cpln/publishNotReadyAddresses` | `true` | When enabled, the internal service endpoint will include all pod IP addresses, even those that haven't passed their readiness probe yet. This is useful for: * Distributed databases that need peer discovery during initialization * Clustered applications where nodes must communicate before becoming ready * Any application using StatefulSet-style stable network identities for bootstrapping Enabling this feature means traffic may be routed to pods that are not yet ready to handle requests. Only use this for applications specifically designed to handle connections during startup. ## Inter-Container Communication Containers deployed within the same workload can communicate with each other using their names and assigned ports. This setup facilitates direct networking between containers. * **Container Identification** - Each container is uniquely identified by its name. * **Port Allocation** - Containers are assigned specific ports for network communication. ### Example Scenario * Imagine two containers within the same workload: `foo` and `bar`. * Container `foo` is running on port **4020**. * Container `bar` is running on port **4030**. `foo` can access `bar` using the URL **[http://bar:4030](http://bar:4030)**. This URL combines the name of the destination container `bar` and its assigned port **4030**, enabling direct communication between the two containers. This method ensures efficient and organized networking within a multi-container workload environment. ## Built-in Variables Each workload has the following built-in environment variables: | Variable Name | Description | Format | | :---------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------- | | CPLN\_GLOBAL\_ENDPOINT | The canonical Host header that the container will receive requests on | `${\workloadName}-${gvcAlias}.cpln.app` | | CPLN\_GVC | The Global Virtual Cloud (/reference/gvc) the container is running under | string | | CPLN\_GVC\_ALIAS | The Global Virtual Cloud Alias | 13 digit alphanumeric value | | CPLN\_LOCATION | The [location](/reference/location) the container is serving the request from | aws-us-west-2, azure-eastus2, gcp-us-east1, etc. | | CPLN\_NAMESPACE | The namespace of the container | Generated random string (e.g., aenhg2ec6pywt) | | CPLN\_PROVIDER | The cloud provider the container is serving the request from | aws, azure, gcp, etc. | | CPLN\_ORG | The org the container is running under | string | | CPLN\_WORKLOAD | The workload the container is running under | string | | CPLN\_WORKLOAD\_VERSION | The Control Plane version of the Workload, only updated when needed to apply changes. For example, changing scaling settings will not cause this to change. | numeric | | CPLN\_TOKEN | A token used to authenticate to the Control Plane CLI / API | Random authorization token | | CPLN\_IMAGE | The image as defined for this container in the Control Plane api | string | Since a workload identity can be the target of a policy, a running workload can be authorized to exercise the Control Plane [CLI](/cli-reference/overview) or [API](/api-reference/api) without any additional authentication. Examples: * Direct call to the Control Plane API: * `curl ${CPLN_ENDPOINT}/org/${CPLN_ORG} -H "Authorization: ${CPLN_TOKEN}"` * If the Control Plane CLI installed: * `cpln org get ${CPLN_ORG}` \*\* The value of CPLN\_TOKEN is valid only if the request originates from the workload it is injected in. If it is used from another workload or externally, a `403 Forbidden` response will be returned. \*\* If a workload is not assigned an identity, it can still `GET` its parent org. ## Logs Workload logs are consolidated from all the deployed locations and can be viewed using the UI or CLI. Using the UI, the logs page will be prefilled with the [LogQL](https://grafana.com/docs/loki/latest/logql/) query for the workload and [GVC](/reference/gvc) name. ```log Example LogQL Query theme={null} {gvc="test-gvc", workload="test-workload"} ``` Logs can be further filtered by: * `Date` * `Location` * `Container` Grafana can be used to view the logs by clicking the `Explore on Grafana` link within the console. Refer to the [logs](/core/logs) page for additional details. ## Rollout Options Settings to control the rollout process between versions. ```yaml YAML theme={null} spec: rolloutOptions: minReadySeconds: 0 maxUnavailableReplicas: 1 maxSurgeReplicas: 100% scalingPolicy: OrderedReady terminationGracePeriodSeconds: 30 ``` ### minReadySeconds The minimum number of seconds that a workload replica must be running before the rollout progresses. ### maxUnavailableReplicas The maximum number or percentage of replicas that can be unavailable during a rollout or during regular rescheduling of workloads. ### maxSurgeReplicas The maximum number or percentage of new replicas that can be added during a rollout for each batch. Example: If there are 4 running replicas and maxSurgeReplicas is set to 50%, then during each rollout 2 replicas will be added at the new version. Once they are healthy as determined by the [ReadinessProbe](#readiness-probe), the rollout will continue, -2 old replicas, +2 new replicas, -2 old replicas. In cases where a short rollout cutover is needed, a maxSurgeReplicas setting of `100%` is recommended. ### scalingPolicy The strategies used to update applications and services deployed. Valid values: `OrderedReady` (Updates workloads in a rolling fashion, taking down old ones and bringing up new ones incrementally, ensuring that the service remains available during the update.), `Parallel` (Causes all pods affected by a scaling operation to be created or destroyed simultaneously. This does not affect update operations.). Default: `OrderedReady`. ## Automatic Retries Settings to control automatic http/grpc request retries ```yaml YAML theme={null} spec: requestRetryPolicy: attempts: 2 retryOn: - connect-failure - '501' ``` ### attempts The amount of retries before sending the response to the client. By default, attempts will be set to `2`. ### retryOn Indicates under which circumstances a retry should be attempted. Can include [HTTP](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/router_filter#x-envoy-retry-on) or [GRPC](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/router_filter#x-envoy-retry-grpc-on) policies. By default, retryOn will be set to `[connect-failure,refused-stream,unavailable,cancelled,resource-exhausted,retriable-status-codes]` ## Default Ephemeral Storage Each workload replica receives at least 1GB of local ephemeral solid state drive (SSD) storage. Workloads that request more than 1 core of CPU receive 1GB of storage for each core. For example, a workload that requests 1500 millicore of CPU can consume up to 1.5GB of ephemeral storage. If the replica uses more than its allotted ephemeral storage, it will be replaced with a new replica. ## Suspend Each workload can be suspended which immediately stops the workload from serving traffic. This is the same as setting the min/max scale to 0. When the workload is unsuspended, it will resume serving traffic. To temporarily deactivate a workload choose `Stop` from the Actions menu. ```yaml YAML theme={null} spec: defaultOptions: suspend: true ``` The workload will stop running and will not serve any traffic. To reactivate the workload, choose `Start` from the actions menu. ```yaml YAML theme={null} spec: defaultOptions: suspend: false ``` ## Timeout Seconds `spec.defaultOptions.timeoutSeconds` The maximum request duration in seconds before Control Plane will timeout. This timeout amount can be reached when Control Plane is waiting for the workload to respond or when waiting for a new workload to become available when using Autoscaling. The minimum value is **1 second** and the maximum value is **600 seconds**. ### Metering and Billing The CPU, memory and egress used for mounted object stores are billed to the workload. To review the costs of mounting an object store, query the container named `cpln-mounter` for the workload within the metrics page. ## Workload Health * Possible values: * `Loading` * `Healthy` * `Unhealthy` * `Deleting` * `Unknown` ## Multi-Zone Multi-zone deployment distributes workload replicas across multiple availability zones within a location for higher availability. When enabled, Control Plane will attempt to spread replicas evenly across available zones. ```yaml YAML theme={null} spec: defaultOptions: multiZone: enabled: true ``` ### Configuration | Property | Type | Description | | :-------- | :------ | :------------------------------------------------------- | | `enabled` | boolean | Enable multi-zone replica distribution. Default: `false` | Not all locations support multi-zone deployments. Confirm that your target location supports multiple availability zones before enabling this option. When using multi-zone with a dedicated load balancer, enable `multiZone` on the GVC's load balancer settings as well to reduce cross-zone traffic costs. ## Special Tags Control Plane supports special tags prefixed with `cpln/` that modify workload behavior. These tags provide access to advanced or niche configuration options that have not yet been implemented as first-class API options. Some tags listed below are deprecated in favor of first-class spec options. Where indicated, prefer using the spec option as tags may be removed in future releases. ### Traffic and Networking | Tag | Value | Description | Status | | :------------------------------------ | :----------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------- | | `cpln/publishNotReadyAddresses` | `true` | Routes internal traffic to replicas that haven't passed their readiness probe. See [Publish Not-Ready Addresses](#publish-not-ready-addresses). | | | `cpln/protocol` | `http`, `http2`, `grpc`, `tcp` | Override the protocol detection for the workload. | Deprecated. Use [`ports[].protocol`](/reference/workload/containers#protocol). | | `cpln/bypassProxyOutbound` | `true` | Bypass the service mesh proxy for outbound traffic. | | | `cpln/disableServiceMeshInboundPort` | port number | Exclude specific inbound port from service mesh. | | | `cpln/disableServiceMeshOutboundPort` | port number | Exclude specific outbound port from service mesh. | | | `cpln/multiZone` | `true` | Enable multi-zone deployment for the workload. | Deprecated. Use [`spec.defaultOptions.multiZone`](#multi-zone). | | `cpln/skipLocationEndpointHeaders` | `true` | Skip adding location endpoint headers in redirect responses. | | | `cpln/discoverCrossGvcReplicas` | `true` | Enable discovery of replicas across different GVCs via mTLS. | | ### Observability | Tag | Value | Description | | :--------------------- | :----- | :--------------------------------------------- | | `cpln/tracingDisabled` | `true` | Disable distributed tracing for this workload. | ### Resources | Tag | Value | Description | Status | | :---------------------------- | :----------------- | :------------------------------------------------------- | :---------------------------------------------------------------------------- | | `cpln/largeDisk` | `true` | Enable large ephemeral disk allocation for the workload. | | | `cpln/timeoutSecondsOverride` | seconds (max 3600) | Override the timeout seconds beyond the standard limit. | Use [`spec.defaultOptions.timeoutSeconds`](#timeout-seconds) for values ≤600. | ### BYOK and Direct Load Balancer Tags These tags are only available for workloads running on [BYOK](/reference/location#byok-locations) clusters or workloads with [Direct Load Balancer](/reference/workload/load-balancing#direct-load-balancer) enabled. | Tag | Value | Description | | :------------------------------------ | :--------- | :-------------------------------------------------------------------------- | | `cpln/disableServiceMesh` | `true` | Disable the service mesh entirely. | | `cpln/disableServiceMeshOutboundCIDR` | CIDR range | Exclude specific CIDR range from service mesh outbound traffic. | | `cpln/k8sClusterRole` | role name | Bind a Kubernetes ClusterRole to the workload's service account. BYOK only. | ### External Authorization Configure an external authorization service for the workload. All requests will be checked against this service before being processed. | Tag | Value | Default | Description | | :---------------------------------------- | :-------------- | :--------- | :------------------------------------------ | | `cpln/externalAuthAddress` | hostname/IP | (required) | Address of the external auth service. | | `cpln/externalAuthPort` | port | `443` | Port of the external auth service. | | `cpln/externalAuthProtocol` | `grpc`, `http` | `grpc` | Protocol to use for auth requests. | | `cpln/externalAuthScheme` | `https`, `http` | `https` | URL scheme for the auth service. | | `cpln/externalAuthTimeout` | duration | `1s` | Timeout for auth requests. | | `cpln/externalAuthFailureAllow` | `true`, `false` | `true` | Allow requests if auth service fails. | | `cpln/externalAuthPackAsBytes` | `true`, `false` | `false` | Pack request body as bytes. | | `cpln/externalAuthRemoveRequestBody` | `true`, `false` | `false` | Remove request body before forwarding. | | `cpln/externalAuthIncludePeerCert` | `true`, `false` | `false` | Include client certificate in auth request. | | `cpln/externalAuthPathPrefix` | path | - | Path prefix for auth requests. | | `cpln/externalAuthUpstreamHeaderPrefixes` | prefixes | - | Header prefixes to forward upstream. | | `cpln/externalAuthListenPort` | port | - | Specific listen port to apply auth to. | ### External Rate Limiting Configure an external rate limiting service for the workload. | Tag | Value | Default | Description | | :-------------------------- | :-------------- | :---------- | :------------------------------------- | | `cpln/rateLimitAddress` | hostname/IP | (required) | Address of the rate limit service. | | `cpln/rateLimitPort` | port | `443` | Port of the rate limit service. | | `cpln/rateLimitDomain` | string | `cpln` | Rate limit domain identifier. | | `cpln/rateLimitScheme` | `https`, `http` | `https` | URL scheme for the rate limit service. | | `cpln/rateLimitDescriptors` | string | `authority` | Descriptors to use for rate limiting. | ### Internal Tags The following tag is used internally and should generally not be set manually: | Tag | Value | Description | | :------------ | :----- | :---------------------------------------------------------------------------- | | `cpln/ignore` | `true` | Causes the scheduler to ignore this workload. Used for internal testing only. | ## CLI To view the CLI documentation for Workloads, see the [Workload CLI reference](/cli-reference/commands/workload). # JWT Authentication Source: https://docs.controlplane.com/reference/workload/jwt-auth Configure JWT providers, claim-to-header mappings, and rules to validate and authenticate JSON Web Tokens in HTTP requests to workloads. JWT (JSON Web Token) Authentication is a security feature that allows you to validate and authenticate JSON tokens in HTTP requests. Multiple JWT providers can be configured for use. [Claims](/reference/workload/jwt-auth#claim-to-headers) inside the JWT can be assigned to headers that will be included in the request received by the Workloads. [Rules](/reference/workload/jwt-auth#rules) are used to define which requests must provide valid tokens and from which Provider. ## Configuration JWT Authentication is configured as part of the Workload or GVC `sidecar.envoy` settings, specifically within the `http` filters array. The configuration includes a name, a typed config, and providers for JWT authentication. When configured on the GVC layer, the settings are applied to all Workloads in the GVC. | Parameter | Type | Description | | :----------------------- | :----- | :--------------------------------------------------------------------------------------------------------------- | | `name` | string | Must be set to `envoy.filters.http.jwt_authn`. | | `priority` | number | Used for ordering multiple filters defined in the GVC and Workload. | | `typed_config."@type"` | string | Must be set to `type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication`. | | `typed_config.providers` | object | A map of JWT provider configurations. Keys starting with `cpln_` are restricted and will be displayed in the UI. | | `typed_config.rules` | object | A set of rules to control which paths and headers require a valid JWT and from which provider. | ## JWT Provider Configuration Each JWT Provider is configured using a dictionary key of the provider name and the following parameters: | Parameter | Type | Description | | :----------------- | :-------- | :-------------------------------------------------------------------------------------------- | | `issuer` | string | The URL of the domain that issued the JWT. | | `audiences` | string\[] | The audiences that are accepted for the JWT. | | `claim_to_headers` | object\[] | Specifies which claims should be added to headers. | | `remote_jwks` | object | Configuration for [remote JWKS](/reference/workload/jwt-auth#remote-jwks) (JSON Web Key Set). | ### Claim to Headers Each object represents a mapping between claims in the JWT and the header. It will be mapped to when the request is forwarded to the workload. | Parameter | Type | Description | | :------------ | :----- | :--------------------------------------------- | | `header_name` | string | The name of the header to add. | | `claim_name` | string | The name of the claim to extract from the JWT. | ### Remote JWKS Configuration for JWT public key resolution and cache behavior. | Parameter | Type | Description | | :--------------- | :----- | :------------------------------------------------------------------------------------------------------- | | `http_uri` | object | The [HTTP URI](/reference/workload/jwt-auth#http-uri) configuration for the JWKS public key lookup. | | `cache_duration` | string | Duration to cache the JWKS. Must be in the format "Ns" where N is the number of seconds. Example `300s`. | #### HTTP URI The JWKS public key lookup for this Provider. | Parameter | Type | Description | | :-------- | :----- | :-------------------------------------------------------------------------------------------------------------------------------- | | `uri` | string | The endpoint use to lookup the JWKS public key. | | `cluster` | string | The cluster name used for the JWKS public key. must match the [Cluster](/reference/workload/jwt-auth#clusters) for this Provider. | | `timeout` | string | Timeout for the JWKS request. Must be in the format "Ns" where N is the number of seconds. Example `10s`. | ## Rules Rules are evaluated in order using details from the request. The first matching rule will be used. | Parameter | Type | Description | | :----------------------- | :-------- | :---------------------------------------------------------------------------------------------------------------- | | `match` | string | The issuer of the JWT. | | `match.headers` | string\[] | An optional list of headers that must exist in the request for this match | | `match.prefix` | string | A required URI prefix for this match. | | `requires.provider_name` | string | The optional JWT Provider to use for JWT Verification of this match. All requests are allowed when not specified. | ## Clusters A Cluster for each provider is required to detail out how the request will be made to the JWT Provider. Since most providers must use `https` the cluster configuration will be similar to the following. Replace `${providerName}` with the name of the provider. Replace `${providerEndpoint}` with the endpoint of the provider, ex `mydomain.auth.us-east-1.amazoncognito.com`. ```yaml theme={null} clusters: - name: cpln_${providerName} type: STRICT_DNS load_assignment: cluster_name: cpln_${providerName} endpoints: - lb_endpoints: - endpoint: address: socket_address: address: ${providerEndpoint} port_value: 443 transport_socket: name: envoy.transport_sockets.tls ``` ## Notes * Provider names starting with "cpln\_" are configured by the UI and will have more restricted configurations. * The `cache_duration` and `http_uri.timeout` must be equal when configured using the UI. * All settings are available from envoyproxy [JWT Authentication](https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/jwt_authn/v3/config.proto) when configured manually, contact support for details. ## Example Configuration ```yaml theme={null} sidecar: envoy: clusters: - name: cpln_foo type: STRICT_DNS load_assignment: cluster_name: cpln_foo endpoints: - lb_endpoints: - endpoint: address: socket_address: address: foo.com port_value: 443 transport_socket: name: envoy.transport_sockets.tls http: - name: envoy.filters.http.jwt_authn priority: 50 typed_config: '@type': >- type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication providers: cpln_foo: audiences: - myaudience claim_to_headers: - claim_name: user.special header_name: X_SPECIAL_USER issuer: https://foo.com/auth remote_jwks: cache_duration: 5s http_uri: cluster: cpln_foo timeout: 5s uri: https://foo.com/auth rules: - match: headers: [] prefix: /metric - match: headers: [] prefix: / requires: provider_name: cpln_foo ``` # Load Balancing Source: https://docs.controlplane.com/reference/workload/load-balancing Configure direct load balancers to expose workload ports through cloud load balancers with geo DNS routing in each deployment location. ## Direct Load Balancer The Direct Load Balancer allows you to expose your [Workload's Ports](/reference/workload/containers#ports) directly through a cloud load balancer in each location where the workload is running. ## Overview * A direct load balancer is created in each location where the workload is running * Each location obtains a public address configured as a Geo DNS target, using standard latency-based routing for traffic distribution across locations * Domains do not need to be registered — traffic is routed directly to the workload through the load balancer * The workload's DNS record can be configured as a CNAME target for any source domains you wish to accept traffic for * Users are responsible for configuring TLS certificates on the workload if required ## Configuration The Direct Load Balancer is enabled on a workload using `loadBalancer.direct.enabled`, with the following properties: ### `direct` The `direct` object contains the configuration for the Direct Load Balancer. | Property | Type | Description | | :-------- | :---------------- | :--------------------------------------------------------------------------------------- | | `enabled` | boolean | When set to `false`, this load balancer will be stopped and no charges will accrue | | `ports` | array | A list of ports that will be exposed by this load balancer | | `ipSet` | string (optional) | A link to an [IP Set](/reference/ipset), used to reserve a static public IP per location | ### `ports` Each port in the `ports` array is added to the cloud load balancer to control the routing behavior: | Property | Type | Description | | :-------------- | :---------------- | :---------------------------------------------------------------------------------------------------------------------------------------------- | | `externalPort` | number | The port that is available publicly. Must be between 22 and 32768 | | `protocol` | string | The protocol that is exposed publicly. Can be either 'TCP' or 'UDP' | | `scheme` | string (optional) | Overrides the default `https` URL scheme that will be used for links in the UI and status. Valid values are 'http', 'tcp', 'https', 'ws', 'wss' | | `containerPort` | object | The port on the container that will receive this traffic | ### Static IP Addresses By default, the public IP assigned to each location is not reserved and may change. To assign a static public IP per location, link an [IP Set](/reference/ipset) to the direct load balancer via `loadBalancer.direct.ipSet`. See [IP Set — Workload Binding](/reference/ipset#workload) for configuration steps. ## Example Configuration ```yaml theme={null} kind: workload name: example-workload spec: loadBalancer: direct: enabled: true ports: - externalPort: 80 protocol: TCP scheme: http containerPort: port: 8080 - externalPort: 443 protocol: TCP scheme: https containerPort: port: 8443 - externalPort: 9000 protocol: UDP containerPort: port: 9000 ``` ```json theme={null} { "kind": "workload", "name": "example-workload", "spec": { "loadBalancer": { "direct": { "enabled": true, "ports": [ { "externalPort": 80, "protocol": "TCP", "scheme": "http", "containerPort": { "port": 8080 } }, { "externalPort": 443, "protocol": "TCP", "scheme": "https", "containerPort": { "port": 8443 } }, { "externalPort": 9000, "protocol": "UDP", "containerPort": { "port": 9000 } } ] } } } } ``` ## Geo Location Headers The Geo Location Headers feature allows you to include geographic information in inbound HTTP requests. When enabled, this feature adds headers containing geographic information to incoming HTTP requests. These headers can provide valuable data about the origin of the request, including the Autonomous System Number (ASN), city, country, and region. This product includes GeoLite2 data created by MaxMind, available from [https://www.maxmind.com](https://www.maxmind.com). ### Configuration | Parameter | Type | Description | | :---------------- | :------ | :----------------------------------------------------------------------------------------------------------------------------------------- | | `enabled` | boolean | When set to `true`, geo location headers will be included on inbound HTTP requests. Existing headers will be replaced. Default is `false`. | | `headers.asn` | string | The header name for the Autonomous System Number information. | | `headers.city` | string | The header name for the city information. | | `headers.country` | string | The header name for the country information. | | `headers.region` | string | The header name for the region information. | ### Usage Guidelines 1. **Enabling the Feature**: Set `geoLocation.enabled` to `true` to activate the Geo Location Headers feature. 2. **Header Configuration**: When enabled, you must specify at least one header (ASN, city, country, or region). 3. **Unique Headers**: The values for ASN, city, country, and region headers must be unique. You cannot use the same header name for different types of information. 4. **Existing Headers**: If enabled, this feature will replace any existing headers with the same names in incoming requests. ### Notes * Enabling this feature may have performance implications, as it requires additional processing for each incoming request. * Enabling this feature has no effect if the workload does not expose an http port. * Ensure that you comply with all relevant data protection and privacy regulations when using geographic information. * The accuracy of geographic information may vary and should not be relied upon for critical decision-making without verification. * Workloads receive the latest ip to geo database on startup. ### Example Configuration This configuration will add the following headers to incoming HTTP requests: * `X-GeoIP-ASN`: Containing the Autonomous System Number * `X-GeoIP-City`: Containing the city name * `X-GeoIP-Country`: Containing the country name * `X-GeoIP-Region`: Containing the region name ```yaml theme={null} loadBalancer: geoLocation: enabled: true headers: asn: X-GeoIP-ASN city: X-GeoIP-City country: X-GeoIP-Country region: X-GeoIP-Region ``` ### Geo Location Filtering Geo location headers can be use in conjunction with header filters, to allow/deny http requests from specified places. Checkout [Geo Filtering](/reference/workload/firewall#geo-filtering) # Security Source: https://docs.controlplane.com/reference/workload/security Workload security permissions, access control policies, and configuration options for managing workload resources and operations. ## Permissions The permissions below are used to define [policies](/reference/policy) together with one or more of the four [principal types](/concepts/access-control): | Permission | Description | Implies | | :------------------- | :--------------------------------------------- | :---------------------------------------------------------------------- | | connect | Connect to replica (open an interactive shell) | | | create | Create new workloads | | | delete | Delete existing workloads | | | edit | Modify existing workloads | view | | exec | Execute commands | exec.runCronWorkload | | exec.runCronWorkload | Force a cron-workload to run | | | manage | Full access | connect, create, delete, edit, exec, exec.runCronWorkload, manage, view | | view | Read-only access | | ## Security Options Settings to control the security of the container at runtime. ### filesystemGroupId Any mounted [Volumes](#volumes) for this container will be owned by the group id provided. When not specified `0` (root) is used. ```yaml YAML theme={null} spec: securityOptions: filesystemGroupId: 777 ``` ### runAsUser Any processes running inside the container will run as the user id provided. When not specified, the default user id for the container image is used. ```yaml YAML theme={null} spec: securityOptions: runAsUser: 1000 ``` # Termination Sequence Source: https://docs.controlplane.com/reference/workload/termination Understand the graceful termination process for workload replicas, including preStop hooks, connection draining, and `SIGTERM` handling. The termination sequence provides a controlled and graceful process for removing workload replicas from the load balancer pool and handling container termination. This process uses preStop hooks (either default or custom) to manage termination timing and ensure proper connection handling based on your workload's specific requirements. ## When Termination Occurs Workload termination typically happens in these scenarios: * **Scaling down**: When reducing the number of workload replicas. * **Version updates**: When replacing an old version with a new version after a successful deployment. * **Capacity AI**: Regular rollouts similar to version updates when [Capacity AI](/reference/workload/capacity) is enabled. * **Maintenance**: Rare cases where maintenance activities require workload replicas to be rescheduled. ## Termination Process Overview The following steps are performed for each workload replica. The shutdown process occurs simultaneously across all containers in a workload, with default configurations giving in-flight requests time to complete and load balancers time to update before containers receive shutdown signals. **Termination Grace Period** By default, `spec.rolloutOptions.terminationGracePeriodSeconds` is set to `90` seconds. This controls the total time available for the workload replica to shut down gracefully before all containers receive a `SIGKILL` signal. ### 1. Load Balancer Update At the start of workload termination, load balancers receive a command to remove the workload replica from the pool. This update process typically takes a few seconds but can take up to 10 seconds. Once updated, new incoming requests are routed to the remaining healthy replicas. ### 2. Workload Sidecar and Container Termination The workload sidecar (managed by Control Plane) and all other workload containers receive commands to begin their termination process. This occurs nearly simultaneously with the load balancer update. ## Sidecar Termination Process The Control Plane-managed sidecar shutdown process consists of three sequential phases: **Hold** → **Monitoring** → **Drain**. ### Phase 1: Sidecar Hold The sidecar continues running normally for 80 seconds by default. This is calculated as 10 seconds less than the termination grace period, which defaults to 90 seconds. The termination grace period can be adjusted in the workload rollout options. As an example, if the termination grace period is reduced to 10 seconds or less, the sidecar hold period would be 0 seconds. ### Phase 2: Sidecar Monitoring The Control Plane-managed sidecar continues monitoring inbound and outbound network activity from workload containers. The sidecar remains running and waits to drain until no active requests exist. This phase can continue until the termination grace period expires or until no more connections are found. ### Phase 3: Sidecar Drain In the drain phase, the sidecar stops accepting new connections and once again verifies that all existing connections are complete. Once all connections are completed, the sidecar shuts down. ## Workload Container Termination Process ### Default PreStop Hook If no custom preStop hook is defined for workload containers, Control Plane applies a default preStop hook that pauses the shutdown process by sleeping. The sleep duration is half of `terminationGracePeriodSeconds`; with the default 90-second grace period, the process sleeps for 45 seconds. After the sleep completes, the container receives a `SIGTERM` signal and has the remaining half of the termination grace period to shut down gracefully before receiving a `SIGKILL` signal. ### Custom PreStop Hook (Optional) Consider implementing a custom preStop hook only if your workload requires specific termination logic, such as: * Ensuring connections are gracefully terminated. * Implementing custom request handling during shutdown. **Important**: If you implement a custom preStop hook, ensure it includes a delay or checks for ongoing requests before exiting. This allows external load balancers sufficient time to update. After the preStop hook completes, the container receives a `SIGTERM` signal to terminate gracefully, with the full `terminationGracePeriodSeconds` (default: 90 seconds) allocated from the start of the shutdown process. **PreStop hook errors**: If a custom preStop hook for any container throws an error, then ALL containers will immediately receive a `SIGKILL` signal. ## Summary Implementing a custom preStop hook is only recommended when additional logic is necessary for your specific workload termination requirements. The default preStop hook provides adequate termination handling for most use cases. # Types Source: https://docs.controlplane.com/reference/workload/types Comparison of workload types: Standard (default), Stateful (persistent storage), Cron (scheduled jobs), Serverless (scale-to-zero), and VM (virtual machines). Capabilities matrix. | | Standard | Stateful | Cron | Serverless | | :--------------------------------------------------------------- | :------: | :------: | :--: | :--------: | | Allow multiple containers | ✔ | ✔ | ✔ | ✔ | | Scale to Zero | KEDA | KEDA | | ✔ | | Must expose one HTTP port | | | | ✔ | | Allow no exposed ports | ✔ | ✔ | ✔ | | | Allow multiple exposed ports | ✔ | ✔ | | | | Unable to expose any ports | | | ✔ | | | Custom Domain requests have the HOST header of the custom domain | ✔ | ✔ | | | | Fast switching update between versions | | | | ✔ | | Rolling update between versions | ✔ | ✔ | | | | [Capacity AI](/reference/workload/capacity) | ✔ | ✔ | ✔ | ✔ | | Autoscale by CPU | ✔ | ✔ | | ✔ | | Autoscale by requests per second | ✔ | ✔ | | ✔ | | Autoscale by concurrent requests | | | | ✔ | | Autoscale by request latency | ✔ | ✔ | | | | Runs on a schedule and is expected to complete | | | ✔ | | | Readiness/liveness probes default to TCP check of container port | | | | ✔ | | Readiness/liveness probes default to disabled | ✔ | ✔ | ✔ | | ## Standard Standard workloads have greater flexibility in network exposure. They can scale to zero only via KEDA. Standard workloads **may**: * Expose no network endpoint. * Serve traffic on multiple ports. * Scale to zero when using KEDA as the autoscaling metric. Standard workloads **may not**: * Scale to zero with `cpu`, `memory`, `rps`, `latency`, or multi-metric strategies. ## Stateful Stateful workloads provide stable replica identities and persistent storage, making them ideal for databases, message queues, and other applications that require durable state. Each replica maintains a consistent identity across restarts and can be addressed individually. Stateful workloads **may**: * Expose no network endpoint. * Serve traffic on multiple ports. * Mount [volume sets](/reference/volumeset) for persistent storage. * Scale to zero when using KEDA as the autoscaling metric. Stateful workloads **may not**: * Scale to zero with `cpu`, `memory`, `rps`, `latency`, or multi-metric strategies. ### Overview Stateful workloads are similar to [standard](#standard) and [serverless](#serverless) workloads in many ways. Much of the base functionality, including Universal Cloud Identity, metrics, logs, audit trail, etc. remains the same. ### Replica-Direct Endpoints By default, stateful workloads (like other types) receive load-balanced endpoints. To enable one endpoint per replica, set `spec.loadBalancer.replicaDirect` to `true`. Enable this when clients need to target a specific replica directly—for example, connecting to a particular database node or routing requests to a designated leader in a clustered application. See the [Replica-Direct Endpoints documentation](/reference/workload/general#replica-direct-endpoints) for the endpoint format. ### Capacity AI [Capacity AI](/reference/workload/capacity) is supported on stateful workloads. Whether it is enabled or not, specify `minCpu` and `minMemory` to optimize costs: they set the reserved resources you are billed for. ### Autoscaling Stateful workloads **do not** support concurrency-based horizontal autoscaling. ### Cost Optimization To reduce costs when using a stateful workload, set `minCpu` and `minMemory` to a low value. These values are used to reserve resources on Control Plane. In Kubernetes parlance, these values are the resource requests. Control Plane bills based on reserved resources, so lower minimum values directly reduce your costs when actual usage is below the maximum. #### `minCpu` * `minCpu` and `cpu` can be at most 4000m apart * The ratio between `minCpu` and `cpu` must be at least 1:4 #### `minMemory` * `minMemory` and `memory` can be at most 4096Mi apart * The ratio between `minMemory` and `memory` must be at least 1:4 ### Key Features #### Stable Replica Identities Each replica has a permanent identity. If a replica is rescheduled, restarted, or recreated for any reason, its identity will remain constant. Identities are of the form - e.g. for a workload called my-workload, the replica identities would be: * my-workload-0 * my-workload-1 * etc. #### Stable Local Hostnames Each replica has a hostname corresponding to its identity. Hostnames are of the form `{replicaIdentity}.{workloadName}` e.g. for a workload called my-workload, the replica hostnames would be: * my-workload-0.my-workload * my-workload-1.my-workload * etc. These hostnames are only accessible within the same location. They cannot be exposed publicly or accessed from other locations. To enable cross-location and public access to individual replicas, set `spec.loadBalancer.replicaDirect` to `true` (see [Replica-Direct Endpoints](#replica-direct-endpoints)). To make an HTTP request to a specific replica within the same location: `curl http://my-workload-1.my-workload:8080` from another workload running on Control Plane #### Persistent Storage Stateful workloads can mount a [volume set](/reference/volumeset) as a volume in one or more of its containers. To do this, simply add a volume to a container's list of volumes of the form: ```yaml YAML theme={null} uri: cpln://volumeset/my-volume-set path: /some/mount/directory ``` #### Considerations When Using Persistent Storage * Volume sets are [GVC](/reference/gvc) scoped. * A workload can only use volume sets in the same GVC. * A volume set can be used by at most one workload, unless you use the [shared file system type](/reference/volumeset#shared-file-system), which provisions a single volume per location that multiple workloads can access simultaneously. * A workload may use any number of volume sets. * The volume set uri must begin with `cpln://volumeset/`. ### Examples #### PostgreSQL [This example](https://github.com/controlplane-com/examples/tree/main/examples/postgres) demonstrates how to run a simple postgresql instance on Control Plane. #### NATS [This example](https://github.com/controlplane-com/examples/tree/main/examples/nats) demonstrates how to run [NATS](https://nats.io/) on Control Plane. ### Minimal Example ```yaml YAML theme={null} kind: workload name: my-database spec: type: stateful containers: - name: db image: postgres:16 cpu: "1" minCpu: "250m" memory: 1Gi minMemory: 256Mi ports: - number: 5432 protocol: tcp volumes: - uri: cpln://volumeset/my-data path: /var/lib/postgresql/data defaultOptions: autoscaling: minScale: 1 maxScale: 3 loadBalancer: replicaDirect: true ``` ## Cron Cron workloads should be used when you need to perform a background task on a regular schedule. Cron workloads **may not**: * Serve traffic. * Scale to zero. * Include a container that runs indefinitely. Cron workloads **must**: * Exit upon completion of the task at hand. Control Plane will start a new replica of your workload at the next scheduled execution time. A cron workload can be suspended by setting `job.suspend` to `true`. While suspended, no new executions start. Use [Run Now](#run-now) for on-demand execution even while suspended. ### Cron Configuration Cron workloads are always deployed to all locations within their GVC. Unlike workloads of other types, there is no way to provide location-specific configuration overrides. * `job.schedule` * A string, which determines how often the cron workload should execute. This field uses the [Kubernetes cron schedule syntax](https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/#cron-schedule-syntax). * `job.concurrencyPolicy` * This determines what Control Plane will do when a prior execution of your workload is still running when the next scheduled execution time arrives. * `Forbid`: subsequent executions will be forgone until the running execution completes. * `Replace`: the running execution will be stopped so that a new execution can begin. * `Allow`: subsequent executions will be allowed to proceed as normal. * `job.historyLimit` * An integer between 1 and 10 representing the number of prior executions to be retained for reference. * `job.restartPolicy` * Either `Never` or `OnFailure`. This determines whether your workload will be restarted when it fails on execution. * `job.activeDeadlineSeconds` * **Optional**: By default there is no deadline. Job executions are allowed to run indefinitely. * If this property is set, this is the maximum number of a seconds a job execution can run. If the job does not exit in the allotted time, Control Plane will remove it. ### Run Now Cron workloads can be run on-demand even when they are suspended. When running a cron workload on-demand the job.concurrencyPolicy field is ignored. Cron workloads can be run on-demand by submitting a `runCronWorkload` command via `POST https://api.cpln.io/org/my-org/gvc/my-gvc/workload/my-cron-workload/-command` ```yaml runCronWorkload Command theme={null} type: runCronWorkload spec: location: aws-us-west-2 containerOverrides: - name: my-container command: '/bin/sh' cpu: '25m' memory: '32Mi' image: 'ubuntu:latest' args: - '-c' - sleep 10 env: - name: MY_ENV_VAR value: some-new-value ``` #### runCronWorkload Spec * `location` * The name of the location as specified in your [GVC](/reference/gvc) configuration. * `containerOverrides` * **Optional**: A list of objects that override specific parts of a container's configuration. * `containerOverrides[].name` * The name of the container to override in the workload specification. * `containerOverrides[].command` * A new command for the container during this execution only. This field corresponds to `workload.containers[].command` in the workload specification. * `containerOverrides[].cpu` * A new CPU configuration for the container during this execution. This field corresponds to `workload.containers[].cpu` in the workload specification. * `containerOverrides[].memory` * A new memory allocation for the container during this execution. This field corresponds to `workload.containers[].memory` in the workload specification. * `containerOverrides[].image` * A new image for the container during this execution. This field corresponds to `workload.containers[].image` in the workload specification. * `containerOverrides[].args` * A new list of arguments for the container during this execution only. This field corresponds to `workload.containers[].args` in the workload specification. * `containerOverrides[].env` * A new list of environment variables for the container during this execution only. This field corresponds to `workload.containers[].env` in the workload specification. #### Run via the CLI Run a cron workload using the `cpln` CLI via `cpln workload cron start`. e.g. If you do not specify a location using --location, the CLI will run the workload everywhere it has been deployed. The --file parameter should contain a list of containerOverrides. ```shell CLI Example theme={null} echo '[{"name":"my-container","command":"/bin/bash","args":["-c","sleep 10"],"env":[{"name":"MY_ENV_VAR","value":"some-new-value"}]}]' | cpln workload cron start cron --gvc my-gvc --org my-org --file - ``` ### Job History A cron workload retains up to `job.historyLimit` job executions in its history. Each job execution will be in one of the following statuses: * Invalid * Active * Success * Failure * Removed #### The Removed Status The Removed status indicates that a job execution was deleted before it could finish execution. There are several reasons this can happen, but the most common are: * The `job.concurrencyPolicy` is `Replace` and while the job was still executing, `job.Schedule` dictated that the job should begin again. * The `job.activeDeadlineSeconds` limit was exceeded. ## Serverless Serverless workloads should be used for web applications that serve traffic on a single port, but may not need to run 100% of the time. Serverless workloads **may**: * Scale to zero. Serverless workloads **may not**: * Serve traffic on multiple ports. Serverless workloads **must**: * Expose a network endpoint. When a custom domain routes to a serverless workload, the `Host` header is set to the canonical endpoint, not the custom domain. The original domain is available in the `X-Forwarded-Host` header. See [Hostname Behavior](/reference/domain#hostname-behavior) for details. ## VM VM workloads run a full virtual machine — its own guest OS and kernel — as a workload, while participating in the same service mesh, identity, networking, firewall, and observability as container workloads. Use them for Windows guests, custom kernels, appliance images, or lifting an existing VM image (VMDK/qcow2/VHD) onto Control Plane. VM workloads **may**: * Boot from an OCI containerDisk or an HTTP(S) disk image. * Persist the boot disk and attach additional data disks via [volume sets](/reference/volumeset). * Expose no endpoint, or serve traffic on multiple ports. * Run Linux or Windows guests. VM workloads **may not**: * Run more than one container entry (the single entry describes the VM). * Use a container `image`, `command`, or `args` — the guest comes from `spec.vm.bootDisk`. * Live‑migrate between nodes — a VM is **cold‑restarted** when its node is scaled down or replaced, so durable state must live on a volume set. See the dedicated [Virtual Machines](/reference/workload/vm) reference for the full configuration, lifecycle, networking, cloud-init, and persistence model, and [Publishing & converting VM images](/guides/vm-images) for building boot images. # Virtual Machines Source: https://docs.controlplane.com/reference/workload/vm Run full virtual machines as a workload type on Control Plane — inside the same service mesh, identity, networking, and observability as containers. Boot disks, persistence, cloud-init, and SSH. ## Overview A **VM workload** (`spec.type: vm`) runs a full virtual machine — its own kernel, init system, and guest operating system — as a first‑class Control Plane workload. A VM is scheduled, networked, secured, and observed exactly like a [standard](/reference/workload/types#standard) container workload: it joins the [service mesh](/reference/workload/security), receives a [Universal Cloud Identity](/reference/identity), is governed by the same [firewall](/reference/workload/firewall) rules, and streams metrics and logs into the same observability stack. Use a VM workload when you need something a container cannot give you: * A "lift and shift" of an existing virtual machine image (VMDK, qcow2, VHD) without re‑platforming it into a container. * A specific guest OS or kernel (Windows, a custom Linux distribution, an appliance image). * Software that must run against real hardware abstractions (custom kernel modules, nested virtualization‑style tooling, legacy applications). VM workloads are backed by [KubeVirt](https://kubevirt.io/). You do not interact with KubeVirt directly — Control Plane translates your workload spec into the underlying VM definition, disk imports, networking, and mesh configuration. ### A VM is just another workload Everything you already know about workloads applies to VMs: | Capability | Behavior for VMs | | :-------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Service mesh / mTLS** | VM traffic flows through the Istio sidecar; workload‑to‑workload traffic is mutually authenticated with the workload's identity. | | **Service discovery** | Other workloads reach the VM at `..cpln.local`. The VM resolves in‑cluster names through a platform DNS forwarder (see [Cloud-init & platform injection](#cloud-init--platform-injection)). | | **Identity & cloud access** | The VM runs as the workload [identity](/reference/identity); cloud‑provider credentials are available to the guest via the same metadata endpoint used by containers. | | **Firewall** | The VM's inbound/outbound access is governed by [`firewallConfig`](/reference/workload/firewall), identical to other workloads. | | **Domains** | A [domain](/reference/domain) can route public traffic to a VM that exposes ports. | | **Metrics & logs** | Prometheus scraping, the serial console log, and the audit trail all work without extra configuration. | | **Autoscaling** | Manual replica counts via `minScale`/`maxScale`. (See [Scaling & lifecycle](#scaling--lifecycle).) | Because of this, most of the rest of the workload documentation — [firewall](/reference/workload/firewall), [identity](/reference/identity), [domains](/reference/domain), [custom metrics](/reference/workload/custom-metrics) — applies to VMs unchanged. This page covers what is **specific** to the VM type. ## Lifecycle & disruption **VMs can currently be disrupted.** A VM replica runs similar to a container workload on our platform. When we need to scale down, the VM can on occasion be **stopped and restarted elsewhere**. Control Plane does **not** offer live migration yet — there is no seamless, in‑memory hand‑off between nodes. A restart is a cold boot: in‑memory state is lost and the guest boots again from its disk. Data can be lost if the root disk is not configured with a volumeset. Design your VM workloads to tolerate restarts: * **Persist anything you need to keep** on a [volume set](#persistence-with-volume-sets) — both the boot disk and any data disks. State written only to an ephemeral root disk does not survive rescheduling. * **Expect cold reboots** during node scale‑down, node upgrades, and cluster maintenance. When running with your own [Mk8s](/mk8s/overview) location you can control this in your nodepool settings. * **Make boot idempotent.** Your [cloud-init](#cloud-init--platform-injection) and guest services should converge to a working state on every boot, not just the first. This is the same operational model as a [stateful](/reference/workload/types#stateful) container workload: durable storage is explicit, and identity/storage are stable across restarts, but the running process itself is not pinned to a node. ## Minimal example A single Ubuntu VM that installs and serves nginx on port 80, with a persisted boot disk: ```yaml YAML theme={null} kind: workload name: my-vm spec: type: vm containers: - name: vm cpu: 1000m memory: 2Gi ports: - number: 80 protocol: http vm: bootDisk: source: oci: image: quay.io/containerdisks/ubuntu:22.04 persist: volumeSet: cpln://volumeset/my-vm-root runStrategy: Always cloudInit: userData: | #cloud-config packages: - nginx runcmd: - systemctl enable nginx - systemctl start nginx firewallConfig: external: inboundAllowCIDR: ['0.0.0.0/0'] outboundAllowCIDR: ['0.0.0.0/0'] internal: inboundAllowType: same-gvc defaultOptions: autoscaling: minScale: 1 maxScale: 1 ``` A VM workload has exactly **one** container entry. It describes the VM's resources (`cpu`, `memory`), exposed `ports`, `metrics`, and attached `volumes`. The guest image comes from `spec.vm.bootDisk`, **not** from `containers[0].image` — setting a container image on a VM workload is rejected. ## The container entry A VM workload reuses the [container](/reference/workload/containers) object for the VM's resource and networking surface, with VM‑specific rules: * **Exactly one container.** Multiple containers are rejected. * **`cpu` must be a whole number of cores** — a multiple of `1000m`, and at least `1000m`. The guest is presented this many vCPU cores. (`500m`, `1500m`, etc. are rejected.) * **`memory`** is the RAM presented to the guest (e.g. `2Gi`). * **`ports`** advertise the services the guest listens on, for service discovery and mesh routing. Use the `ports` array (`{ number, protocol }`); the singular `port` field is not valid for VMs. * **`metrics`** enables Prometheus scraping of a guest endpoint (see [Custom metrics](/reference/workload/custom-metrics)). * **`readinessProbe` / `livenessProbe`** support `tcpSocket` and `httpGet` only (`exec` and `grpc` are rejected). * **`volumes`** attach data disks to the VM (see [Persistence](#persistence-with-volume-sets)). * **`env`** is injected into the guest OS (see [Environment variables](#environment-variables)). * **Not valid for VMs:** `image`, `command`, `args`, `workingDir`, `lifecycle`, and the singular `port`. The guest OS owns process lifecycle. CPU topology (how those cores are presented) can optionally be shaped with `spec.vm.cpu.sockets` (1–32) and `spec.vm.cpu.threads` (1–8). By default the core count is derived from `containers[0].cpu`. ## Boot disk `spec.vm.bootDisk` defines where the VM boots from. Exactly one **source** is required. ### OCI containerDisk source The simplest path: package a disk image as an OCI image (a "containerDisk") and reference it. This is the recommended, most reproducible option. ```yaml YAML theme={null} bootDisk: source: oci: image: quay.io/containerdisks/ubuntu:22.04 ``` * The image may be a public containerDisk (e.g. `quay.io/containerdisks/ubuntu:22.04`), or one you publish to your org's [registry](/reference/image) and reference as `//image/:` or `/org//image/:`. * Cross‑org image links are rejected; the image must belong to the calling org or be a public registry reference. * `persist.volumeSet` is **required**. The boot disk is always a per‑replica PVC backed by a [volume set](#persistence-with-volume-sets); the image seeds it on first boot. See [Publishing & converting VM images](/guides/vm-images) for how to build and push a containerDisk and convert images from other formats. ### HTTP(S) source Boot from a disk image hosted over HTTP(S). Control Plane imports it into a persistent disk on first boot, so `persist.volumeSet` is **required**. ```yaml YAML theme={null} bootDisk: source: http: url: https://example.com/images/my-disk.qcow2 checksum: 'sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08' persist: volumeSet: cpln://volumeset/my-vm-root ``` * The importer accepts common disk formats — **qcow2, raw, VMDK, VHD/VHDX, VDI, ISO** — and gzip/xz‑compressed variants, converting them to the disk's native format on import. * `checksum` is optional but recommended: `sha256:` or `sha512:`. The import is verified against it. * Import runs once per replica's persistent disk; subsequent boots reuse the imported disk. ### Boot disk options | Field | Default | Notes | | :------------------------------ | :------- | :---------------------------------------------------------------------------------- | | `bootDisk.source.oci.image` | — | OCI containerDisk reference. Mutually exclusive with `http`. | | `bootDisk.source.http.url` | — | HTTP(S) disk image URL. Requires `persist.volumeSet`. | | `bootDisk.source.http.checksum` | — | `sha256:` or `sha512:`. | | `bootDisk.persist.volumeSet` | — | **Required.** `cpln://volumeset/`. The boot disk is always a per‑replica PVC. | | `bootDisk.bus` | `virtio` | Disk bus: `virtio`, `sata`, or `scsi`. | | `bootDisk.bootOrder` | `1` | Boot priority (1–16) when multiple bootable disks exist. | Object‑store sources (`s3://`, `gs://`) and snapshot restores are not yet exposed. To boot from an object store, use a signed `http(s)` URL. To boot from an existing volume set snapshot, use the volume set [`restoreVolume`](/reference/volumeset) command. ## Persistence with volume sets VM storage is durable only when it is backed by a [volume set](/reference/volumeset). A volume set provisions one PVC per replica and keeps its contents across reschedules. There are two distinct uses. ### Persisting the boot disk Set `bootDisk.persist.volumeSet` to make the root disk durable. The image source seeds the disk the first time; after that, the guest's changes to the root filesystem survive restarts and node moves. ```yaml YAML theme={null} vm: bootDisk: source: oci: image: quay.io/containerdisks/ubuntu:22.04 persist: volumeSet: cpln://volumeset/my-vm-root ``` The disk size comes from the volume set's `initialCapacity` (defaulting to `20Gi` if the boot volume set is omitted entirely), and the storage class comes from the volume set's performance class. ### Attaching additional data disks Additional volumes are attached to the VM as **block devices** through the container's `volumes` list. Each entry needs a `uri` and a `name`; the `name` identifies the device inside the guest. ```yaml YAML theme={null} containers: - name: vm cpu: 1000m memory: 2Gi volumes: - uri: cpln://volumeset/my-vm-data name: data bus: virtio ``` For VMs, a data volume is presented to the guest as a **raw block device** (e.g. `/dev/vdb`), **not** auto‑mounted at a path. This is different from container workloads, where a volume's `path` mounts it into the filesystem. The guest is responsible for partitioning, formatting, and mounting the device — typically once, then persisted on the device itself. The disk's serial is set to its volume `name`, so you can find it reliably at `/dev/disk/by-id/**` and mount it from cloud-init. Data‑disk volume fields: | Field | Default | Notes | | :---------- | :------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- | | `uri` | — | `cpln://volumeset/` for a data disk, or `cpln://secret/` to surface a [secret](/reference/secret) as a disk. | | `name` | — | Required. Device name inside the guest; also used as the disk serial for stable `by-id` lookup. | | `bus` | `virtio` | `virtio`, `sata`, or `scsi`. A `cpln://secret/` volume on `sata` or `scsi` is presented as a read‑only CD‑ROM (`/dev/sr0`); on `virtio` it's a block device. | | `bootOrder` | — | Optional boot priority if the disk is bootable. Not valid for secret volumes — a secret is not bootable. | Example: format and mount a data disk on first boot, idempotently, from cloud-init: ```yaml YAML theme={null} cloudInit: userData: | #cloud-config runcmd: - | DEV=/dev/disk/by-id/*data* if ! blkid $DEV; then mkfs.ext4 -L data $DEV; fi mkdir -p /mnt/data echo 'LABEL=data /mnt/data ext4 defaults,nofail 0 2' >> /etc/fstab mount -a ``` Volume set [considerations](/reference/workload/types#considerations-when-using-persistent-storage) apply: they are GVC‑scoped, a volume set is used by at most one workload (unless [shared](/reference/volumeset#shared-file-system)), and the URI must begin with `cpln://volumeset/`. ## Environment variables `containers[0].env` is delivered into the guest OS. A VM has no container process to set variables on, so Control Plane writes them through the guest's init system at boot: * **Linux** (cloud-init): to `/etc/cpln/environment` (reference it from a systemd unit with `EnvironmentFile=`), appended to `/etc/environment` for login shells, and exported from `/etc/profile.d/cpln-env.sh`. * **Windows** (cloudbase-init): set machine‑wide, so services and new sessions inherit them. Control Plane also provides `CPLN_GVC`, `CPLN_LOCATION`, `CPLN_ORG`, `CPLN_PROVIDER`, and `CPLN_WORKLOAD`. Values that reference a [secret](/reference/secret) (`cpln://secret/` or `cpln://secret/.`) are resolved to the secret's value, provided the workload identity has access to it. Manifest references (`cpln://reference/...`) are not supported for VMs — there is no pod for the guest to read them from. Multi‑line values are skipped; deliver those as a [secret](/reference/secret) disk instead. Env is baked into the guest at boot, so changes take effect when the VM next rolls to a new version and reboots — not live. ## Networking & ports A VM is subject to the same [firewall](/reference/workload/firewall) and service‑mesh policy as any other workload. * **Exposed ports** come from `containers[0].ports`. These are the ports other workloads, domains, and probes can reach, and they flow through the service mesh. * **Port 22 (SSH)** is always reachable internally by Control Plane — SSH authenticates with its own certificate trust (see [SSH access](#ssh-access)). * **A single network interface** is supported (`spec.vm.networks` accepts one entry, default name `default`). * **Service discovery:** other workloads reach the VM at `..cpln.local` on its exposed ports, exactly like a container workload. Inside the guest, Control Plane's internal service names (`*.cpln.local`) resolve through the platform DNS forwarder. On some Linux guests, large `cpln.local` answers resolve more reliably over TCP — adding `options use-vc` to `/etc/resolv.conf` from cloud-init forces TCP and avoids truncation issues. ## Connecting with RDP (Windows) A Windows VM has no public RDP endpoint by default. Use the [`cpln port-forward`](/guides/cli/cpln-port-forward) command to open a secure tunnel from your machine to the VM's RDP port (3389), then point any RDP client at `localhost`. This requires `connect` permission on the workload and never exposes RDP publicly. Ensure the Windows image has Remote Desktop enabled, the firewall allows it, and you have a user to log in as. You can do this in the image, or from [cloud-init](#cloud-init--platform-injection): ```yaml YAML theme={null} vm: guestOS: windows cloudInit: userData: | #ps1_sysnative Set-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server' -Name 'fDenyTSConnections' -Value 0 Enable-NetFirewallRule -DisplayGroup 'Remote Desktop' net user admin 'YourStrongPassword!' /add net localgroup administrators admin /add ``` Forward a local port to 3389 on a VM replica. The port does **not** need to be listed in the workload's `ports` — `cpln port-forward` reaches the guest directly: ```bash theme={null} cpln port-forward my-windows-vm 3389:3389 --gvc my-gvc --location aws-us-west-2 ``` Use a different local port (e.g. `13389:3389`) if 3389 is busy on your machine, and `--replica` to target a specific VM. Point any RDP client at the local end of the tunnel and log in with the guest credentials: ```text theme={null} localhost:3389 ``` On macOS use the **Windows App** (formerly Microsoft Remote Desktop); on Windows use **mstsc**; on Linux use a client such as **Remmina** or **xfreerdp**: ```bash theme={null} xfreerdp /v:localhost:3389 /u:admin ``` The same pattern works for any TCP service the guest listens on — `cpln port-forward` to the port and connect locally. The port does not need to be listed in the workload's `ports`; that list only governs in‑cluster service‑mesh traffic. ## Cloud-init & platform injection `spec.vm.cloudInit` provides the guest's [cloud-init](https://cloudinit.readthedocs.io/) user‑data. Control Plane **merges** your content with a small amount of platform configuration so the VM works inside the mesh — your cloud-init is preserved and runs alongside the injected pieces. ### Providing your own cloud-init Supply exactly one of: | Field | Use when | | :------------------------- | :-------------------------------------------------------------------------------------------------------------------- | | `cloudInit.userData` | Inline cloud-init (max 16 KiB). Convenient for non‑sensitive config. Not encrypted at rest in the data‑service. | | `cloudInit.userDataBase64` | Same as `userData`, base64‑encoded (max \~22 KB). | | `cloudInit.userDataSecret` | A [secret](/reference/secret) holding the user‑data (key `userdata` or `user-data`). Use this for sensitive payloads. | ```yaml YAML theme={null} cloudInit: userDataSecret: //secret/my-vm-cloudinit ``` ### What the platform injects On top of your user‑data, Control Plane adds (and keeps under its control): * **Network configuration.** A name‑based interface match with DNS pointed at the in‑cluster forwarder. This is platform‑managed and not user‑overridable: KubeVirt re‑randomizes the VM's MAC on every restart, and a MAC‑pinned network config would stall the guest after a reschedule. * **In‑cluster DNS.** So the guest can resolve `*.cpln.local` and other cluster service names, and so cross‑location ([wormhole](/reference/workload/general)) peers are reachable. * **Service‑mesh interception.** The VM's pod joins the Istio mesh; traffic on the exposed ports is mutually authenticated with the workload identity. * **SSH trust.** A platform SSH certificate authority is trusted by the guest and a platform user is provisioned, enabling certificate‑based SSH (see below). This is re‑applied on every boot so it survives image and platform updates. * **(Windows only)** A DNS bootstrap script that points the guest's adapters at the in‑cluster resolver and sets the cluster search suffixes. Set `spec.vm.guestOS` to `linux` (default) or `windows` so the correct per‑OS injection is applied. ### SSH access There are two ways to get your SSH keys into the guest, in addition to the platform's certificate trust: * **`cloudInit.sshPublicKeySecrets`** — a list (max 8) of [secrets](/reference/secret) holding public keys, injected for the default user. * **`spec.vm.accessCredentials`** — per‑user key delivery. Each entry maps a key secret to one or more guest `users`, delivered via `qemuGuestAgent` (default) or `configDrive`. ```yaml YAML theme={null} vm: accessCredentials: - sshPublicKeySecret: //secret/team-ssh-keys users: [ubuntu, ops] deliveryMethod: qemuGuestAgent ``` `cpln workload connect` and `cpln workload exec` log into the guest as the platform `cpln` user using a CA‑signed certificate — both the user and the trusted CA come from the platform's cloud‑init injection. They therefore require a **cloud‑init‑capable guest** (standard Linux cloud images, or Windows with cloudbase‑init). Minimal images that don't process cloud‑init (e.g. CirrOS) won't accept these connections; reach those over the serial console instead. ## Scaling & lifecycle * **Replicas** are controlled by `defaultOptions.autoscaling.minScale` / `maxScale`. Each replica is an independent VM with its own persistent disk(s). * **`runStrategy`** controls the power state: * `Always` (default) — keep the VM running; restart it if it stops. * `RerunOnFailure` — restart only on non‑zero exit. * `Manual` — start/stop is driven explicitly. * `Halted` — defined but powered off. Requires `minScale: 0`. * **`clock.timezone`** sets the guest clock (default `UTC`). * **`hostname`** / **`subdomain`** set the guest's hostname and DNS subdomain. * **`firmware`** selects the bootloader (`efi` default, or `bios`) and optional SMBIOS identifiers (`uuid`, `serial`, `smbios.*`). **Secure Boot is not yet available.** `firmware.secureBoot` is rejected by validation. It requires persistent EFI NVRAM, which is not yet provisioned; without it a Secure Boot guest would lose its boot state on reboot. ## Settings reference All fields below live under `spec.vm`. In the **Req.** column, **✓** means required, **—** optional, and **Cond.** conditionally required (see Notes). A VM must have a boot source — exactly one of `bootDisk.source.oci.image` or `bootDisk.source.http.url`. | Field | Req. | Type | Default | Notes | | :--------------------------------------- | :---: | :------------- | :--------------- | :--------------------------------------------------------------------- | | `bootDisk.source.oci.image` | Cond. | string | — | OCI containerDisk reference. Exactly one boot source; XOR with `http`. | | `bootDisk.source.http.url` | Cond. | string | — | HTTP(S) disk URL. Requires `persist.volumeSet`. | | `bootDisk.source.http.checksum` | — | string | — | `sha256:` / `sha512:`. | | `bootDisk.persist.volumeSet` | ✓ | string | — | `cpln://volumeset/`. The boot disk is always a per‑replica PVC. | | `bootDisk.bus` | — | enum | `virtio` | `virtio` / `sata` / `scsi`. | | `bootDisk.bootOrder` | — | int | `1` | 1–16. | | `cpu.sockets` | — | int | — | 1–32. Cores derive from container `cpu`. | | `cpu.threads` | — | int | — | 1–8. | | `firmware.bootloader` | — | enum | `efi` | `efi` / `bios`. | | `firmware.uuid` | — | string | generated | Fixed SMBIOS UUID (v4). | | `firmware.serial` | — | string | — | SMBIOS serial. | | `firmware.smbios.*` | — | string | — | `manufacturer`, `product`, `version`, `sku`, `family`. | | `guestOS` | — | enum | `linux` | `linux` / `windows`. | | `networks[0].name` | — | string | `default` | Single interface; masquerade only. | | `cloudInit.userData` | — | string | — | Inline cloud-init (≤16 KiB). XOR with the other `userData*`. | | `cloudInit.userDataBase64` | — | string | — | Base64 cloud-init. | | `cloudInit.userDataSecret` | — | secret link | — | Secret with `userdata` / `user-data`. | | `cloudInit.sshPublicKeySecrets` | — | secret link\[] | — | Up to 8. | | `accessCredentials[].sshPublicKeySecret` | Cond. | secret link | — | Required when an `accessCredentials` entry is present. | | `accessCredentials[].users` | Cond. | string\[] | — | Required per entry. 1–16 guest users. | | `accessCredentials[].deliveryMethod` | — | enum | `qemuGuestAgent` | `qemuGuestAgent` / `configDrive`. | | `runStrategy` | — | enum | `Always` | `Always` / `RerunOnFailure` / `Manual` / `Halted`. | | `clock.timezone` | — | string | `UTC` | e.g. `America/New_York`. | | `hostname` | — | string | — | `[a-z0-9-]`, ≤63. | | `subdomain` | — | string | — | `[a-z0-9-]`, ≤63. | ## Related * [Publishing & converting VM images](/guides/vm-images) * [Run VM workloads on your own mk8s cluster](/mk8s/add-ons/kubevirt) * [Volume sets](/reference/volumeset) * [Workload firewall](/reference/workload/firewall) * [Workload identity](/reference/identity) * [Custom metrics](/reference/workload/custom-metrics) # Volumes Source: https://docs.controlplane.com/reference/workload/volumes Mount cloud object storage, file storage, ephemeral scratch storage, and secrets to workload containers at runtime. ## Overview Cloud Object and File storage, ephemeral scratch storage and [Secrets](/reference/secret) can be mounted to directories of [containers](#containers) at runtime by adding one or more volumes. A volume consists of a `uri` and a mount `path`. The `uri` is prefixed with the provider scheme followed by the bucket/storage name (e.g., s3://my-s3-bucket). The mount `path` must be a unique absolute path (e.g., /s3-files). This path will be added to the container's file system and accessible by the running application. During the set up of a volume using the console, the `uri` name can be entered manually or an existing [Cloud Account](/reference/cloudaccount) can assist with looking up the name. The [identity](#identity) of the workload is used to authenticate to the provider's cloud storage API, or used for authorization to access the Control Plane secret. A [Cloud Account](/reference/cloudaccount) for each cloud storage provider, with the necessary access/roles, must exist and be associated with the workload [identity](#identity). Volumes can be shared between containers of the same workload. For example if two containers in a workload are each configured with the volume `uri: 'scratch://volume1', path: '/my/shared/data'` then changes to files in `/my/shared/data` will be visible to both containers. A maximum of 15 volumes can be added. ## Volume Providers | Volume Provider | URI Scheme | Mode | Example | | :-------------------------------------- | :--------------- | :-------------------- | :------------------------------------- | | CPLN Secret | cpln://secret | read-only | cpln://secret/secretname | | [CPLN Volume Set](/reference/volumeset) | cpln://volumeset | read-write | cpln://volumeset/my-volume-set | | AWS S3 | s3:// | read-only | s3://my-s3-bucket | | Google Cloud Storage | gs\:// | read-only | gs\://my-google-bucket | | Azure Blob Storage | azureblob:// | read-only | azureblob://my-azure-account/container | | Azure Files | azurefs\:// | read-write | azurefs\://my-azure-account/my-files | | Scratch (emptyDir) | scratch:// | read-write, ephemeral | scratch://volume1 | ## Secret Types The secret type will dictate how the secret will be mounted to the file system. * [Opaque Secret](/reference/secret#opaque) : * The `.payload` property is not required. * If the payload is base-64 encoded, the secret can be decoded at runtime by selecting the `Base64 decode at Runtime` checkbox when configuring the secret. * The configured path must contain at least one subpath (e.g., /path/subpath). The last path (or file name) will be mounted as a file and contain the payload. If a subpath is not given, the payload of the secret will be mounted as a file named `payload` (e.g., /path/payload). * [Azure](/reference/secret#azure-sdk), [Docker](/reference/secret#docker), and [GCP](/reference/secret#google-cloud-platform-gcp) Secrets: * The secret will be mounted to the specified path as the file name `___cpln___.secret`. * The configured path must contain at least one subpath (e.g., /path/subpath). The last path will be mounted as a directory and contain the `___cpln___.secret` file. * All other [Secret Types](/reference/secret#secret-types): * If the root secret is selected, the specified path will be mounted as a directory. The contents of the directory will contain files named as the key/property of the secret. The contents of each file will contain the value of the respective key. * If a key/property of a secret is selected, the secret will be mounted to the specified path as a file. The contents will include the value of the key/property. * The directory will include a file named `___cpln___.secret`. The contents of this file will be the JSON formatted output of the secret. ## Identity Configuration ### Authorizing Workload Access to Secrets A Workload that is configured with a [Volume](#volumes) that references a [Secret](/reference/secret) must be configured with an [Identity](/reference/secret#reveal-permission) bound to a [policy](/reference/policy) having the [reveal](/reference/secret#reveal-permission) permission. ### Object Store Access To allow a workload [identity](/reference/identity) the ability to authenticate to an object store, a [cloud access](/reference/identity#cloud-access-universal-cloud-identity) rule must be created for each provider. A [Cloud Account](/reference/cloudaccount) for each provider must exist in order to create the [cloud access](/reference/identity) rule. The following list contains the minimum roles/scopes that must be added to a [cloud access](/reference/identity#cloud-access-universal-cloud-identity) rule: * S3 (using an AWS Cloud Account) * Select `Create a new AWS role with existing policies` and choose `AmazonS3ReadOnlyAccess`. * Google Cloud Storage (using a Google Cloud Account) * Select `Create a new GCP service account`. * Resource: Storage -> Global -> Bucket -> `Select bucket name`. * Role: `Storage Legacy Bucket Reader` and `Storage Legacy Object Reader`. * Verify that the [Cloud Account for GCP](/reference/cloudaccount#gcp-details) is configured correctly. In particular, the Control Plane GCP service account requires the `Storage Admin` role. * Azure Blob Storage and Files (using an Azure Cloud Account) * Scope: Storage -> Region -> Storage Accounts -> `Select storage account`. * Role (for Azure Files) : [Reader and Data Access](https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#reader-and-data-access). * Role (for Azure Blobs) : `Storage Blob Data Reader`. ## Firewall Configuration To allow a Workload access to the object stores, the outbound requests of its [external firewall](#external) must either be set to `All Outbound Requests Allowed` or the hostnames listed below for the corresponding object store must be added to the `Outbound Hostname Allow List`. ```bash AWS theme={null} *.amazonaws.com ``` ```bash Azure Blob theme={null} *.blob.core.windows.net *.azure.com ``` ```bash Azure File theme={null} *.file.core.windows.net *.azure.com ``` ```bash GCP theme={null} *.googleapis.com ``` ## Limitations * Volumes are read-only, except for Azure Files. * The following Path names are reserved: * `/dev` * `/dev/log` * `/tmp` * `/var` * `/var/log` * Authentication to a provider is only facilitated through the workload [identity](#identity). The use of an AWS or Azure key to mount a bucket/container within a container will not work. * Properties of a mounted object store, such as cache policies and timeouts, cannot be configured by the user. Control Plane has optimized those values for each cloud provider. ## Secret Volume Provider A [Secret](/reference/secret) can be mapped as a read-only file by using a [Volume](#volumes). During the configuration of a [Volume](#volumes) using the console, the [Secret](/reference/secret) reference (e.g., `cpln://secret/SECRET_NAME`) can be entered manually or `Control-S` can be pressed to view and select the available [Secrets](/reference/secret). The Path must be a unique absolute path and, optionally, a file name (e.g., /secret/my-secret.txt) depending on the secret type. This path will be added to the container's file system and will be accessible by the running application. A maximum of 15 volumes can be added. # Release Notes Source: https://docs.controlplane.com/releases Latest features, updates, and changes across all Control Plane products including the platform, CLI, and Terraform provider. Stay up-to-date with the latest features, updates, and changes across all Control Plane products and services. ## Platform Release Notes Track platform-wide updates, new features, and improvements to the Control Plane platform. **[View Platform Release Notes](https://github.com/controlplane-com/product-updates/releases)** ### Subscribe to Platform Updates Stay informed about the latest platform releases: * **RSS Feed**: Subscribe via RSS at `https://github.com/controlplane-com/product-updates/releases.atom` * **GitHub Watch**: Click "Watch" → "Custom" → "Releases" on the [product-updates repository](https://github.com/controlplane-com/product-updates) * **RSS Readers**: Use services like Feedly, Inoreader, or NetNewsWire with the releases.atom URL * **Slack Integration**: Connect the RSS feed to your Slack channels * **Email Notifications**: Use services like Blogtrottr to receive updates via email ## CLI Release Notes View detailed release notes for the Control Plane Command Line Interface (CLI). **[View CLI Release Notes](/cli-reference/release-notes)** * Current Version: **3.16.0** * Latest Release: August 12, 2026 * [Installation Instructions](/cli-reference/installation) ## Terraform Provider Release Notes Track updates and changes to the Control Plane Terraform Provider. **[View Terraform Provider Changelog](https://github.com/controlplane-com/terraform-provider-cpln/blob/main/CHANGELOG.MD)** * Latest Version: **1.2.31** * [Installation Instructions](/terraform/installation) *** ## Getting Help If you have questions about any release or need assistance: * **Documentation**: Browse our comprehensive [documentation](/whatis) * **Support**: Contact our support team through your standard support channel # Install and Manage using the CLI Source: https://docs.controlplane.com/template-catalog/install-manage/cli Install, upgrade, and manage Control Plane Template Catalog releases using the CLI and Helm. Covers prerequisites, installation, listing revisions, and uninstalling templates. ## Prerequisites Install the Control Plane CLI. See [Installation](/cli-reference/installation). Install [Helm](https://helm.sh/docs/intro/install/) (v3 or later). ## Install a Template Browse the [Template Catalog](/template-catalog/overview) to find the template you want to deploy. * Each template page includes a full configuration reference and a snippet of the default `values.yaml` file. Create a `values.yaml` file to customize the template for your environment. This can include settings such as resource limits, replica counts, and any template-specific options. Your values file must follow the same structure and format as the template's default values file. Run the following command to install the template as a release: ```bash theme={null} cpln helm install oci://ghcr.io/controlplane-com/templates/ \ --version \ -f values.yaml ``` Replace: * `` — A unique name for this installation. * `` — The name of the template (example: `postgres`). * `` — The version to install (example: `3.2.0`). Omitting `--version` defaults to the latest version. Omitting `-f values.yaml` will deploy the template using its default values. **Example — install PostgreSQL v3.2.0 with custom values:** ```bash theme={null} cpln helm install my-postgres oci://ghcr.io/controlplane-com/templates/postgres \ --version 3.2.0 \ -f values.yaml ``` You can also override individual values inline using `--set` without a values file: ```bash theme={null} cpln helm install my-pg oci://ghcr.io/controlplane-com/templates/postgres \ --version 3.2.0 \ --set config.database=myapp \ --set config.username=myuser \ --set pgbouncer.enabled=true ``` `-f` and `--set` can also be combined — `--set` values take precedence over those in the file. ## Manage a Template ### View Releases List all installed releases in your organization: ```bash theme={null} cpln helm list ``` ### Release Details View full details for a specific release, including the manifest, values, and notes: ```bash theme={null} cpln helm get all ``` You can also retrieve individual pieces of release information: ```bash theme={null} # View the rendered manifest cpln helm get manifest # View the configured values cpln helm get values --all # View release notes cpln helm get notes ``` ### Upgrade To upgrade a release with new values or a new template version: ```bash theme={null} cpln helm upgrade oci://ghcr.io/controlplane-com/templates/ \ --version \ -f values.yaml ``` Any workloads affected by the change will roll out new deployments. Unchanged items will not be redeployed. ### Template Preview Generate a preview of the resources that will be created without deploying: ```bash theme={null} cpln helm template oci://ghcr.io/controlplane-com/templates/ \ --version \ -f values.yaml ``` ### Revisions View the revision history for a release: ```bash theme={null} cpln helm history ``` Roll back to a previous revision: ```bash theme={null} # Roll back to the previous revision cpln helm rollback # Roll back to a specific revision cpln helm rollback ``` ## Uninstall a Template Remove all resources created by the release and delete the release state: ```bash theme={null} cpln helm uninstall ``` ## Related Source files, default values, and chart definitions for all templates. OCI packages for all templates, hosted on the GitHub Container Registry. Deploy and manage Control Plane resources using Helm charts with the cpln helm command. # Install and Manage using Pulumi Source: https://docs.controlplane.com/template-catalog/install-manage/pulumi Install, upgrade, and manage Control Plane Template Catalog releases using the Pulumi provider. Covers prerequisites, CatalogTemplate resource configuration, and lifecycle management. ## Prerequisites Install the Pulumi CLI. See [Pulumi Provider](/iac/pulumi) for installation instructions. Install the [Control Plane Pulumi provider](https://www.pulumi.com/registry/packages/cpln/api-docs/catalogtemplate/) and configure your organization. See [Pulumi Provider](/iac/pulumi) for setup instructions. ## Install a Template Use the `CatalogTemplate` resource to install a template from the catalog. Browse the available templates in the [Template Catalog](/template-catalog/overview#available-templates) and identify the template name and version you want to install. Create a `values.yaml` file in your Pulumi project directory with the template's configuration. Refer to the specific template's documentation for available options. Add a `CatalogTemplate` resource to your Pulumi program, reading the values from the file: ```typescript theme={null} import * as cpln from '@pulumiverse/cpln'; import * as fs from 'fs'; const values = fs.readFileSync('values.yaml', 'utf8'); const release = new cpln.CatalogTemplate('example', { name: 'my-release', template: 'postgres', version: '1.0.0', gvc: 'my-gvc', values: values, }); ``` ```python theme={null} import pulumiverse_cpln as cpln from pathlib import Path values = Path("values.yaml").read_text() release = cpln.CatalogTemplate( "example", name="my-release", template="postgres", version="1.0.0", gvc="my-gvc", values=values, ) ``` ```go theme={null} package main import ( "os" "github.com/pulumi/pulumi/sdk/v3/go/pulumi" "github.com/pulumiverse/pulumi-cpln/sdk/go/cpln" ) func main() { pulumi.Run(func(ctx *pulumi.Context) error { valuesBytes, err := os.ReadFile("values.yaml") if err != nil { return err } _, err = cpln.NewCatalogTemplate(ctx, "example", &cpln.CatalogTemplateArgs{ Name: pulumi.String("my-release"), Template: pulumi.String("postgres"), Version: pulumi.String("1.0.0"), Gvc: pulumi.String("my-gvc"), Values: pulumi.String(string(valuesBytes)), }) return err }) } ``` ```csharp theme={null} using Pulumi; using Pulumiverse.Cpln; using System.IO; return await Deployment.RunAsync(() => { var values = File.ReadAllText("values.yaml"); var release = new CatalogTemplate("example", new CatalogTemplateArgs { Name = "my-release", Template = "postgres", Version = "1.0.0", Gvc = "my-gvc", Values = values, }); }); ``` Properties: * **name** — A unique release name for this installation. * **template** — The name of the catalog template (e.g., `postgres`). * **version** — The template version to install. * **gvc** — The GVC to deploy to. Leave empty if the template creates its own GVC. * **values** — YAML-formatted string to customize the template configuration. Deploy the stack: ```bash theme={null} pulumi up ``` Pulumi will provision the required Control Plane resources based on your configuration. ### Outputs After deploying, the resource exposes a `resources` output containing a list of all Control Plane resources created by the release. Each entry includes: * **kind** — The resource type (e.g., `workload`, `secret`, `gvc`). * **name** — The resource name. * **link** — The full Control Plane URL for the resource. ## Manage a Template ### Upgrade To upgrade a release with new values or a new template version, update the `version` property and/or your `values.yaml` file and re-deploy: ```bash theme={null} pulumi up ``` Any workloads affected by the change will roll out new deployments. Unchanged items will not be redeployed. ### Preview Changes Use `pulumi preview` to see the changes that will be applied before upgrading: ```bash theme={null} pulumi preview ``` ## Uninstall a Template Remove the `CatalogTemplate` resource from your program and deploy: ```bash theme={null} pulumi up ``` Alternatively, destroy all resources in the stack: ```bash theme={null} pulumi destroy ``` This deletes all Control Plane resources created by the release. # Install and Manage using Terraform Source: https://docs.controlplane.com/template-catalog/install-manage/terraform Install, upgrade, and manage Control Plane Template Catalog releases using the Terraform provider. Covers prerequisites, cpln_catalog_template resource configuration, and lifecycle management. ## Prerequisites Install [Terraform](https://www.terraform.io/downloads.html) (v0.13+). Add the [Control Plane provider](https://registry.terraform.io/providers/controlplane-com/cpln/latest/docs/resources/catalog_template) to your Terraform configuration. See [Terraform Provider](/iac/terraform) for setup instructions. ## Install a Template Use the `cpln_catalog_template` resource to install a template from the catalog. Browse the available templates in the [Template Catalog](/template-catalog/overview#available-templates) and identify the template name and version you want to install. Create a `values.yaml` file in your Terraform project directory with the template's configuration. Refer to the specific template's documentation for available options. Add a `cpln_catalog_template` resource to your Terraform configuration, referencing the values file: ```hcl main.tf theme={null} resource "cpln_catalog_template" "example" { name = "my-release" template = "postgres" version = "1.0.0" gvc = "my-gvc" values = file("${path.module}/values.yaml") } ``` Arguments: * **name** — A unique release name for this installation. * **template** — The name of the catalog template (e.g., `cockroach`). * **version** — The template version to install. * **gvc** — The GVC to deploy to. Leave empty if the template creates its own GVC. * **values** — YAML-formatted string to customize the template configuration. Initialize and apply the configuration: ```bash theme={null} terraform init terraform plan terraform apply ``` Terraform will provision the required Control Plane resources based on your configuration. ### Outputs After applying, the resource exposes a `resources` attribute containing a list of all Control Plane resources created by the release. Each entry includes: * **kind** — The resource type (e.g., `workload`, `secret`, `gvc`). * **name** — The resource name. * **link** — The full Control Plane URL for the resource. ## Manage a Template ### Upgrade To upgrade a release with new values or a new template version, update the `version` argument and/or your `values.yaml` file and re-apply: ```bash theme={null} terraform plan terraform apply ``` Any workloads affected by the change will roll out new deployments. Unchanged items will not be redeployed. ### Preview Changes Use `terraform plan` to preview the changes that will be applied before upgrading: ```bash theme={null} terraform plan ``` ## Uninstall a Template Remove the `cpln_catalog_template` resource from your configuration and apply: ```bash theme={null} terraform plan terraform apply ``` Alternatively, destroy the specific resource: ```bash theme={null} terraform destroy -target=cpln_catalog_template.example ``` # Install and Manage using the UI Source: https://docs.controlplane.com/template-catalog/install-manage/ui Install, upgrade, and manage Control Plane Template Catalog releases using the Console UI. Covers browsing templates, configuring releases, viewing revisions, and uninstalling. ## Install a Template Select **Catalog** under the templates dropdown in the navigation menu. Browse the catalog and select a template to install. Read through the **README** page to understand the template's configuration options and usage. Click **Create Template Release** On the **Install** page, provide the following: * **Release Name** — A unique name for this installation. * **GVC** — Select the GVC to deploy to. If no GVC select option is available, you will need to configure the GVC name in the values file. Review and modify the **values** file to customize the template for your environment. This can include settings such as resource limits, replica counts, and any template-specific options. Click **Install App** to deploy the template. Control Plane will provision the required resources based on your configuration and will automatically navigate you to the newly created release. The **Template** option generates raw YAML manifests that you can review and manually apply. The equivalent CLI install command is also shown — see [Install through the CLI](/template-catalog/install-manage/cli) for details. ## Manage a Template ### View Releases Select **Releases** under the templates dropdown in the navigation menu to view all created template releases. ### Release Details When viewing a release, the info section displays the template version, app version, GVC, number of items created, and a list of created items with their details. ### Upgrade To upgrade a release with new values or a new template version, select **Upgrade** and provide any updated values or the template version you wish to use. Click **Upgrade App** to apply the changes. Any workloads affected by the change will roll out new deployments. Unchanged items will not be redeployed. ### Template Preview From the **Upgrade** section, click **Template** to generate a preview YAML file of the items that will be created. This allows you to review changes before applying them. ### Revisions Click **Revisions** to view all revisions to the release, including upgrades and rollbacks. You can inspect individual changes and roll back to any previous revision. ## Uninstall a Template Navigate to the template release, click the **Actions** dropdown, and select **Uninstall**. # Template Catalog Source: https://docs.controlplane.com/template-catalog/overview Browse the Control Plane Template Catalog to quickly deploy databases, queues, and stateless services with instant provisioning, versioned releases, and full revision history. ## Overview Quickly deploy and manage applications such as databases, queues, and stateless services directly on Control Plane. Each template provisions the required Control Plane resources from user-provided values, producing a production-ready application in seconds. Configure resource limits, firewall rules, replica counts, backups, and more to fit your specific needs. Templates are versioned, allowing you to target a specific release when installing or upgrading. Every installation and revision is tracked, giving you the ability to upgrade or roll back at any time. ## Available Templates The catalog covers many of the most popular open-source projects and is continuously growing. Select a template below to learn more about its configuration and usage. All Control Plane template files are publicly available in the [templates repo](https://github.com/controlplane-com/templates). Workflow orchestration platform with CeleryExecutor, Redis, and PostgreSQL Distributed NoSQL database cluster with peer-to-peer replication, scheduled repair, and optional logical or physical backups Complete Change Data Capture pipeline bundling PostgreSQL HA, Apache Kafka, and Debezium Server with auto-wired credentials and DNS Open-source customer-support platform with a shared inbox, live chat widget, and real-time agent updates, backed by PostgreSQL and Redis High-performance analytical database with single-node and multi-shard cluster modes backed by object storage Distributed SQL database built for high availability and horizontal scaling OWASP Core Rule Set web application firewall that proxies and protects your workloads Self-hosted HTTP task queue and scheduler with retries, rate limiting, and Redis Sentinel Automated vulnerability scanning for Control Plane image registry images using Trivy, with HTML reports stored in S3 or Azure File Share Web-based database administration tool for managing multiple database connections Standalone Change Data Capture connector streaming database changes to Kafka, Redis, NATS, Kinesis, Pub/Sub, and more Open-source knowledge base and wiki with real-time collaborative editing, spaces, and local or S3 attachment storage Scheduled SQL analytics jobs over Parquet, CSV, and object storage — no server to keep running Distributed search and analytics engine with optional Kibana UI and automated SLM snapshot backups Distributed key-value store for coordination, service discovery, and configuration management Stretched etcd cluster with one member per location for cross-region coordination and consensus Continuously sync externally-stored secrets and parameters with Control Plane Self-hosted identity and access management with OAuth2, OpenID Connect, and SAML support Open-source publishing platform for blogs, newsletters, and paid memberships, backed by MySQL 8 Lightweight self-hosted Git service with pull requests, issues, and a package registry, backed by PostgreSQL Open-source, Sentry-compatible error tracking backed by highly available PostgreSQL and a Redis task queue Dashboards and alerting over your own datasources, backed by highly available PostgreSQL with optional multi-replica HA Grafana across multiple locations on one stretched PostgreSQL cluster, surviving the loss of a region Self-hosted AI agent with persistent memory, browser automation, and an OpenAI-compatible gateway API Open-source secrets-management platform with a web UI and REST API, backed by PostgreSQL and HA Redis Distributed event streaming platform in KRaft mode with Kafbat UI, REST Proxy, and Connect support Clustered identity and access management with single sign-on, OIDC/SAML, and a highly available PostgreSQL backing store Open-source LLM observability platform with trace ingestion, evaluation, and dashboards backed by ClickHouse and PostgreSQL Self-hosted newsletter and mailing list manager with campaigns, subscription pages, and a single-instance or highly available PostgreSQL backing store OpenAI-compatible LLM gateway with virtual API keys, spend tracking, budgets, and rate limiting, backed by PostgreSQL and Redis Distributed full-text search cluster with Galera replication, S3 data imports, and a web management UI Open-source relational database with persistent storage and optional phpMyAdmin management interface Fast, typo-tolerant search engine with instant indexing and a simple REST API Open-source business intelligence with dashboards, SQL editor, and scheduled reports, backed by highly available PostgreSQL Self-hosted long-term Prometheus metrics store with remote-write ingest, PromQL queries, and object storage backends High-performance S3-compatible distributed object storage with erasure coding Document-oriented NoSQL database with persistent storage and optional external load balancer access Highly available MongoDB replica set with automatic failover, HAProxy write routing, and logical or physical backups Open-source relational database with persistent storage, optional phpMyAdmin interface, and scheduled backups Workflow automation platform with editor, API, and webhooks on one endpoint, backed by highly available PostgreSQL High-performance messaging super cluster spanning multiple regions with pub/sub, queueing, and WebSocket support High-performance reverse proxy with path-based routing rules and configurable autoscaling No-code database and spreadsheet UI over PostgreSQL, with REST and GraphQL APIs Local LLM platform with Open WebUI, automatic model downloads, and optional GPU support Self-hosted, ChatGPT-style chat UI for LLMs, backed by an in-GVC Ollama server or any OpenAI-compatible API, with users, RAG, and model management Vault-compatible secrets engine with integrated raft storage and hands-free auto-unseal from a platform secret, AWS KMS, or GCP Cloud KMS Distributed search and analytics cluster with optional Dashboards UI, demo log pipeline, and snapshot backups OTLP trace and metrics ingestion feeding Control Plane tracing and any Prometheus-compatible store, with optional authenticated public endpoints High-performance PostgreSQL connection pooler, load balancer, and read/write splitting proxy written in Rust Active-active distributed PostgreSQL with Spock multi-master replication across global locations Apache Iceberg REST catalog that lets query engines share governed lakehouse tables PostgreSQL database with geospatial extension for location-aware applications One PostgreSQL cluster stretched across locations, with automatic promotion of a replica in a surviving region Relational database with persistent storage and optional scheduled backups to S3 or GCS Multi-replica PostgreSQL cluster with Patroni failover, etcd consensus, and HAProxy leader routing Metrics database with remote-write ingest, durable TSDB storage, and an optional Thanos sidecar for federation and block upload High-performance vector database for semantic search and RAG, with API-key auth, snapshots, and durable on-disk storage Single-replica message broker with persistent storage and built-in management UI Kafka-compatible event streaming platform with built-in Schema Registry, SASL auth, and optional web console Master-replica Redis cluster with Redis Sentinel for automatic failover Native Redis Cluster with sharding across 3 primaries and 3 replicas Master-replica Redis with Sentinel spanning multiple geographic locations S3-compatible object storage for backups and attachments, usable as the storage target by any template that speaks S3 Cron workload that syncs Control Plane dictionary secret keys as environment variables onto GVCs and workloads SFTP server backed by S3, GCS, or S3-compatible object storage, with a scale-to-zero mode that suspends when idle Self-hosted Supabase with PostgreSQL, Kong API gateway, PostgREST, GoTrue auth, Realtime, file storage, and Studio dashboard Mesh VPN gateway that connects your Tailscale network to Control Plane workloads Durable-execution platform for workflows that survive crashes and restarts, with Web UI and a highly available PostgreSQL backend Global PromQL query layer over your Prometheus servers with HA deduplication and an optional object-storage tier for long-term retention Distributed MySQL-compatible database with TiKV storage and PD cluster management Time-series database on PostgreSQL 18 with hypertables, compression, continuous aggregates, and optional PgBouncer and backups Patroni-managed HA TimescaleDB cluster on PostgreSQL 18 with etcd consensus, HAProxy leader routing, automatic failover, and optional PgBouncer and backups Open-source low-code platform for internal tools with the built-in ToolJet Database, a PostgreSQL backing store, and optional Redis for multi-replica scaling Distributed SQL engine that queries across PostgreSQL, MySQL, ClickHouse, MongoDB and more in a single federated query Open-source CRM with a customizable data model, REST and GraphQL APIs, and a background worker for automations Open-source API gateway with Redis-backed rate limiting, authentication, and traffic management Privacy-first, cookieless web and product analytics with a stateless multi-replica app tier and a single or highly available PostgreSQL store Open-source feature-flag server with admin UI, SDK APIs, and a highly available PostgreSQL backend Self-hosted uptime monitoring with HTTP, TCP, DNS, and ping checks, 90+ notification providers, and public status pages Self-hosted, Bitwarden-compatible password manager with a SQLite database on a persistent volume and scheduled snapshot backups AI-native vector database with Raft-consensus clustering, hybrid search, and pluggable AI modules for vectorization and generative search ## Installation & Management Install and manage templates using any of the supported methods: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs # Airflow Source: https://docs.controlplane.com/template-catalog/templates/airflow Deploy Apache Airflow on Control Plane using the Template Catalog. Covers configuration, volumes, scaling, and Celery workers with Redis broker and PostgreSQL metadata store. ## Overview Apache Airflow is an open-source platform for programmatically authoring, scheduling, and monitoring workflows. This template deploys a full Airflow stack using CeleryExecutor, with Redis as the task queue broker and PostgreSQL as the metadata database. Celery workers can optionally be autoscaled using KEDA based on queue depth. ### What Gets Created * **GVC** — A dedicated GVC across the specified locations. * **Airflow Webserver** — The Airflow web UI for managing DAGs, monitoring task execution, and viewing logs. * **Celery Workers** — Distributed task execution workers that process DAG tasks. * **Redis** — A Redis broker for the Celery task queue, with persistent storage. * **PostgreSQL** — A PostgreSQL database for Airflow metadata storage. * **Volume Sets** — Persistent storage for Airflow DAG data, PostgreSQL, and Redis. * **KEDA ScaledObject** (optional) — Automatically scales Celery workers up or down based on Redis queue length. * **Secret** — A dictionary secret containing the PostgreSQL credentials, JWT signing key, Fernet encryption key, and admin password, shared across all Airflow workloads. * **Identity & Policy** — An identity bound to the workloads with `reveal` access to the Airflow configuration secret. ## Pre-Deployment Checklist Before deploying, generate and set the following required values in `values.yaml`: | Value | How to generate | | -------------------------- | -------------------------------------------------------------------------------------------- | | `airflow.auth.jwtSecret` | `openssl rand -base64 48` | | `airflow.auth.fernetKey` | `python3 -c 'from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())'` | | `airflow.admin.password` | Choose a strong password | | `postgres.config.password` | Choose a strong password | ## Installation This template has no external prerequisites. To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} # Global Virtual Cloud (GVC) settings gvc: name: airflow locations: - name: aws-eu-central-1 # Postgres database configuration postgres: image: postgres:18 resources: minCpu: 250m maxCpu: 500m minMemory: 512Mi maxMemory: 1024Mi config: username: username password: password database: airflow volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) # Redis cache configuration redis: image: redis:7.4 resources: cpu: 250m memory: 512Mi volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) # Apache Airflow configuration airflow: webserver: image: apache/airflow:3.0.3 resources: cpu: 2000m memory: 3Gi celeryWorker: image: controlplanecorporation/celery:v1 resources: cpu: 256m memory: 512Mi webPort: 8080 # Port for accessing the Airflow web interface auth: jwtSecret: CHANGE_ME # REQUIRED: generate with "openssl rand -base64 48" jwtExpirationDelta: 3600 # JWT token expiration time in seconds jwtRefreshThreshold: 300 # Threshold before token expires to allow refresh (seconds) fernetKey: CHANGE_ME # REQUIRED: generate with "python3 -c 'from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())'" admin: username: admin password: CHANGE_ME # REQUIRED: change before deploying to production scheduler: dagDirListInterval: 10 # How often to check DAG folder (seconds) minFileProcessInterval: 10 # Minimum interval to process DAG files (seconds) celery: workerConcurrency: 1 # Number of tasks each worker can run concurrently volumeset: airflow: capacity: 10 # initial capacity in GiB (minimum is 10) # Firewall configuration firewallConfig: inboundAllowCIDR: - 0.0.0.0/0 # Restrict to specific IPs in production (e.g. - 203.0.113.0/24) # Git-sync configuration for DAG delivery gitSync: enabled: false repo: "" # Git repository URL (e.g. https://github.com/org/dags) branch: main # Branch to sync period: 60s # How often to sync subPath: "" # Optional subfolder within the repo containing DAGs auth: token: "" # Personal access token for private repos (leave empty for public repos) # KEDA (Kubernetes Event-driven Autoscaling) configuration # NOT SUPPORTED in gcp/us-central1 keda: enabled: true # Enable or disable KEDA autoscaling minScale: 1 # Minimum number of Celery workers maxScale: 3 # Maximum number of Celery workers scaleToZeroDelay: 300 # Time before scaling to zero (seconds) listLength: 3 # Queue length threshold to trigger scaling cooldownPeriod: 1 # Cooldown between scaling events (seconds) initialCooldownPeriod: 1 # Cooldown after startup before scaling (seconds) pollingInterval: 4 # Interval at which KEDA queries metrics (seconds) ``` ### GVC * `gvc.name` — The name of the GVC. Must be unique per deployment. * `gvc.locations` — List of cloud locations to deploy to (e.g., `aws-eu-central-1`). ### PostgreSQL * `postgres.image` — PostgreSQL Docker image. * `postgres.resources` — CPU and memory bounds for the PostgreSQL workload (`minCpu`, `maxCpu`, `minMemory`, `maxMemory`). * `postgres.config.username` / `postgres.config.password` — Database credentials. **Change the default password before deploying to production.** * `postgres.config.database` — Name of the Airflow metadata database (default: `airflow`). * `postgres.volumeset.capacity` — Persistent storage for PostgreSQL data (GiB, minimum 10). ### Redis * `redis.image` — Redis Docker image. * `redis.resources` — CPU and memory allocated to Redis. * `redis.volumeset.capacity` — Persistent storage for Redis data (GiB, minimum 10). ### Airflow Webserver and Workers * `airflow.webserver.image` / `airflow.celeryWorker.image` — Docker images for the webserver and Celery workers. * `airflow.webserver.resources` / `airflow.celeryWorker.resources` — CPU and memory per component. * `airflow.webPort` — Port the Airflow web UI listens on (default `8080`). ### Authentication Airflow 3.x requires three security credentials, all of which **must be changed before deploying to production**: * `airflow.auth.jwtSecret` — Secret key used to sign JWT tokens for API authentication. Generate a secure value with: ```bash theme={null} openssl rand -base64 48 ``` * `airflow.auth.fernetKey` — Key used to encrypt stored connections and variables. Generate with: ```bash theme={null} python3 -c 'from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())' ``` * `airflow.auth.jwtExpirationDelta` — Token lifetime in seconds (default `3600`). * `airflow.auth.jwtRefreshThreshold` — Seconds before expiry at which a token refresh is allowed (default `300`). ### Admin Account * `airflow.admin.username` — Username for the initial Airflow admin account (default: `admin`). * `airflow.admin.password` — Password for the initial admin account. **Change before deploying to production.** The admin user is created on first startup using Airflow's `SimpleAuthManager`. Credentials are written to a password file on the shared volume and re-applied on every container restart, so the password always reflects the current value in `values.yaml`. `SimpleAuthManager` is the default auth manager in Airflow 3.x and is suitable for development and internal deployments. For production deployments requiring SSO or LDAP, consider integrating an external auth provider via OAuth/OIDC. ### Scheduler * `airflow.scheduler.dagDirListInterval` — How often the scheduler scans the DAG folder for new or modified files (seconds). * `airflow.scheduler.minFileProcessInterval` — Minimum interval between processing the same DAG file (seconds). ### Celery * `airflow.celery.workerConcurrency` — Number of tasks a single Celery worker can execute concurrently. ### Storage * `volumeset.airflow.capacity` — Persistent storage for the Airflow home directory shared across workloads (GiB, minimum 10). PostgreSQL and Redis storage are configured under their respective sections (`postgres.volumeset.capacity` and `redis.volumeset.capacity`). The Airflow volume uses a shared (NFS-style) filesystem, allowing both the webserver and Celery workers to read DAGs and write logs to the same volume. ### Firewall * `firewallConfig.inboundAllowCIDR` — List of CIDR ranges allowed to reach the Airflow webserver. Defaults to `0.0.0.0/0` (public). **Restrict to specific IP ranges in production.** ### Git-Sync Git-sync runs as a sidecar container on the webserver and Celery worker workloads, continuously pulling DAGs from a Git repository into the shared Airflow volume. This is the recommended approach for managing DAGs in production. | Property | Description | | -------------------- | ----------------------------------------------------------------------------- | | `gitSync.enabled` | Enable or disable the git-sync sidecar | | `gitSync.repo` | Git repository URL (e.g. `https://github.com/org/dags`) | | `gitSync.branch` | Branch to sync (default: `main`) | | `gitSync.period` | Sync interval (default: `60s`) | | `gitSync.subPath` | Optional subfolder within the repo containing DAG files | | `gitSync.auth.token` | Personal access token for private repositories (leave empty for public repos) | When git-sync is disabled, DAGs can be placed manually in the `/opt/airflow/dags` directory on the Airflow volume. ### KEDA Autoscaling KEDA scales Celery workers automatically based on the Redis queue length. KEDA is not supported in `gcp/us-central1`. | Property | Description | | ---------------------------- | ---------------------------------------------------------- | | `keda.enabled` | Enable or disable KEDA autoscaling | | `keda.minScale` | Minimum number of Celery workers | | `keda.maxScale` | Maximum number of Celery workers | | `keda.scaleToZeroDelay` | Seconds of inactivity before scaling to zero | | `keda.listLength` | Redis queue length that triggers a scale-up | | `keda.cooldownPeriod` | Seconds to wait between scaling events | | `keda.initialCooldownPeriod` | Seconds after startup before autoscaling activates | | `keda.pollingInterval` | Interval at which KEDA queries Redis for metrics (seconds) | ### Connecting to Airflow Once deployed, the Airflow web UI is available at the workload's canonical endpoint: ```text theme={null} https://-airflow-webserver..cpln.app ``` Log in with the `airflow.admin.username` and `airflow.admin.password` set in `values.yaml`. This template creates a GVC with a default name defined in the values file. If you plan to deploy multiple instances, you **must assign a unique GVC name** for each deployment. ### API Access Airflow 3.x uses JWT-based authentication for API access. To obtain a token: ```bash theme={null} curl -X POST https:///auth/token \ -H "Content-Type: application/json" \ -d '{"username": "admin", "password": "your-password"}' ``` Use the returned token for subsequent API requests: ```bash theme={null} curl https:///api/v2/dags \ -H "Authorization: Bearer " ``` ## Production Considerations * **Change all `CHANGE_ME` values** before deploying — `jwtSecret`, `fernetKey`, `admin.password`, and `postgres.config.password` are all required. * **Restrict `firewallConfig.inboundAllowCIDR`** to trusted IP ranges to limit access to the Airflow UI. * **Enable git-sync** for reliable, version-controlled DAG delivery. * **Auth**: `SimpleAuthManager` is not recommended for deployments requiring enterprise SSO. Evaluate an OAuth/OIDC integration for those use cases. ## External References Official Apache Airflow documentation Learn about the CeleryExecutor and distributed task execution Kubernetes Event-driven Autoscaling documentation Official Redis documentation git-sync sidecar documentation View the source files, default values, and chart definition # Cassandra Source: https://docs.controlplane.com/template-catalog/templates/cassandra ## Overview Apache Cassandra is a distributed NoSQL database designed for high availability and linear scalability. This template deploys a Cassandra 5.0 cluster in a single location where each node owns a slice of the token ring and replicates data to peers according to the configured replication factor. Optional scheduled backups and periodic anti-entropy repair are included. This template does not create a GVC. You must deploy it into an existing GVC. ### What Gets Created * **Stateful Cassandra Workload** — A multi-node Cassandra cluster. Each replica gets its own persistent volume so SSTable data survives restarts. * **Volume Set** — One persistent volume per replica for Cassandra data. * **Identity & Policy** — An identity bound to the workload with `reveal` access to the credential secrets, and cloud storage access when backup is enabled. * **Secrets** — An opaque secret for the superuser password and a dictionary secret for the application user credentials. * **Cron Backup Workload** *(optional)* — When `backup.type` is `logical`, a standalone cron workload exports keyspace data as CSVs and uploads them to cloud storage. * **Sidecar Backup Container** *(optional)* — When `backup.type` is `physical`, a sidecar runs on each Cassandra replica, takes SSTable snapshots with `nodetool snapshot`, and syncs them to cloud storage. * **Repair Cron Workload** *(optional, enabled by default)* — Runs `nodetool repair` on a schedule to keep data consistent across replicas. ## Prerequisites This template has no external prerequisites unless backup is enabled. To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} replicas: 3 # replicationFactor must not exceed replicas replicationFactor: 1 # IMPORTANT: Change all credentials before deploying to production superuserPassword: supersecretpassword username: username password: password keyspaceName: mydatabase image: cassandra:5.0 cpu: 1 memory: 4Gi # JVM heap: leave ~50% of container memory for off-heap (bloom filters, page cache, etc.) # Cassandra 5.x uses G1GC — only MAX_HEAP_SIZE is valid; HEAP_NEWSIZE is ignored. jvmHeapSize: 2G clusterName: my-cassandra volumes: data: initialCapacity: 10 autoscaling: maxCapacity: 100 minFreePercentage: 20 scalingFactor: 1.5 multiZone: enabled: false internal_access: type: same-gvc # Options: same-gvc, same-org, workload-list workloads: #- //gvc/GVC_NAME/workload/WORKLOAD_NAME backup: enabled: false type: logical # options: logical, physical image: ghcr.io/controlplane-com/backup-images/cassandra-backup:5.0 schedule: "0 2 * * *" # daily at 2am UTC resources: cpu: 250m memory: 256Mi provider: aws # options: aws, gcp aws: bucket: my-backup-bucket region: us-east-1 cloudAccountName: my-backup-cloudaccount policyName: my-s3-policy prefix: cassandra/backups gcp: bucket: my-backup-bucket cloudAccountName: my-cloud-account prefix: cassandra/backups repair: enabled: true # Cron schedule for full cluster repair (must run within gc_grace_seconds = 10 days) schedule: "0 2 * * 0" ``` ### Replicas and Replication Factor These are two separate settings that work together: * **`replicas`** — how many Cassandra nodes are deployed. More nodes means more capacity and better throughput, as the token ring is split across more nodes. * **`replicationFactor`** — how many copies of each partition are stored across the cluster. A replication factor of 3 means every row exists on 3 different nodes, so the cluster can survive 2 node failures without data loss (with `QUORUM` consistency). `replicationFactor` must not exceed `replicas` — you cannot store 3 copies of data across only 2 nodes. For production, use at least 3 replicas with a replication factor of 3. This allows the cluster to survive a node failure while still achieving quorum. ### Resources and Storage * `cpu` / `memory` — CPU and memory allocated to each Cassandra node. * `jvmHeapSize` — Set to approximately 50% of `memory`. Cassandra relies heavily on off-heap memory for bloom filters, row cache, and OS page cache. * `volumes.data.initialCapacity` — Initial volume size in GiB per node (minimum 10). * `volumes.data.autoscaling.maxCapacity` — Maximum volume size in GiB. * `volumes.data.autoscaling.minFreePercentage` — Triggers a scale-up when free space falls below this percentage. * `volumes.data.autoscaling.scalingFactor` — Multiplier applied to current capacity on each scale-up. ### Multi-Zone When `multiZone.enabled: true`, Control Plane spreads replicas across availability zones within the location. With a replication factor of 3 across 3 zones, each zone holds one copy of every partition — the cluster survives a complete zone outage with no data loss when using `LOCAL_QUORUM` consistency. Verify your selected location supports multi-zone before enabling this option. ### Internal Access Controls which workloads can reach the Cassandra cluster: | Type | Description | | --------------- | --------------------------------------------------------------- | | `same-gvc` | Allow access from all workloads in the same GVC (recommended) | | `same-org` | Allow access from all workloads in the org | | `workload-list` | Allow access only from specific workloads listed in `workloads` | ### Connecting Each Cassandra replica is individually addressable. Provide multiple node hostnames as contact points in your application so it can discover the full cluster topology: ``` Host: {release-name}-cassandra-0.{gvc-name}.cpln.local {release-name}-cassandra-1.{gvc-name}.cpln.local {release-name}-cassandra-2.{gvc-name}.cpln.local Port: 9042 Username: {username} Password: {password} Keyspace: {keyspaceName} ``` ### Repair Cassandra uses eventual consistency — when nodes miss writes during downtime, data can drift out of sync. `nodetool repair` runs an anti-entropy process that compares and reconciles data across all replicas. Repair must complete across all nodes at least once within `gc_grace_seconds` (default: 10 days) to prevent deleted data from reappearing after a node recovers. * `repair.enabled` — Enable the scheduled repair job (recommended: `true`). * `repair.schedule` — Cron expression for repair frequency. The default weekly schedule satisfies the 10-day `gc_grace_seconds` requirement with margin. Do not disable repair in production or increase the interval beyond 10 days. Repair can be resource-intensive on large datasets — consider running it during low-traffic windows. ## Backup Two backup modes are available: | Mode | How it works | Best for | | ---------- | ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------- | | `logical` | Exports tables as CSVs using `cqlsh COPY TO`, uploads to cloud storage. Runs as a standalone cron workload. | Smaller datasets, portability | | `physical` | Creates SSTable snapshots with `nodetool snapshot`, syncs to cloud storage. Runs as a sidecar on each Cassandra replica. | Large datasets, faster backup/restore | Set `backup.enabled: true`, set `backup.type`, and fill in the cloud storage block for your provider. ### AWS S3 Prerequisites 1. Create an S3 bucket. Set `backup.aws.bucket` to its name and `backup.aws.region` to its region. 2. If you do not have a Cloud Account set up, refer to the docs to [Create a Cloud Account](/guides/create-cloud-account). Set `backup.aws.cloudAccountName` to its name. 3. Create an IAM policy with the following JSON, replacing `YOUR_BUCKET_NAME`: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket", "s3:GetObjectVersion", "s3:DeleteObjectVersion" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET_NAME", "arn:aws:s3:::YOUR_BUCKET_NAME/*" ] } ] } ``` 4. Set `backup.aws.policyName` to the name of the policy created in step 3. ### GCS Prerequisites 1. Create a GCS bucket. Set `backup.gcp.bucket` to its name. 2. If you do not have a Cloud Account set up, refer to the docs to [Create a Cloud Account](/guides/create-cloud-account). Set `backup.gcp.cloudAccountName` to its name. 3. Add the **Storage Admin** role to the GCP service account associated with the Cloud Account. ## Restoring a Backup ### Logical Restore Exec into the backup cron workload and run `restore.sh` with the timestamp of the backup to restore: ```bash theme={null} RESTORE_TIMESTAMP=2026-05-15T02-00-00Z /usr/local/bin/restore.sh ``` The timestamp matches the backup folder name in your bucket (e.g. `cassandra/backups/2026-05-15T02-00-00Z/`). The script downloads the CSVs and replays them into Cassandra using `cqlsh COPY FROM`. Existing rows with matching primary keys are overwritten; rows not in the backup are left in place. ### Physical Restore Physical backups are per-node — each replica backed up its own SSTable slice. Exec into the **backup sidecar container** on each replica and run: ```bash theme={null} RESTORE_TIMESTAMP=2026-05-15T02-00-00Z /usr/local/bin/restore.sh ``` The script downloads snapshot files for that replica, writes them to the shared volume, and calls `nodetool import` to load the SSTables without a restart. Repeat this on every replica. Because each node owns a different token range, restoring only one replica leaves the cluster with incomplete data. ## Important Notes * **Scaling up** — Adding replicas after initial deployment does not automatically rebalance data. Run `nodetool rebuild` on new nodes and `nodetool cleanup` on existing nodes after scaling. * **JVM heap** — Set `jvmHeapSize` to approximately 50% of `memory`. Cassandra relies on off-heap memory for bloom filters, row cache, and OS page cache. * **gc\_grace\_seconds** — The default is 10 days. Ensure repair runs at least once within this window on all nodes, or deleted data may reappear after a node recovers from downtime. * **GVC naming** — This template does not create a GVC. Deploy it into an existing GVC. If you run multiple Cassandra clusters in the same org, give each a distinct `clusterName`. ## External References Official Apache Cassandra documentation Client drivers for connecting to Cassandra View the source files, default values, and chart definition # CDC Pipeline Source: https://docs.controlplane.com/template-catalog/templates/cdc-pipeline Deploy a complete Change Data Capture pipeline on Control Plane in a single install. Bundles PostgreSQL HA (Patroni + etcd + HAProxy), Apache Kafka (KRaft), and Debezium Server with auto-coordinated credentials, WAL settings, and internal DNS. ## Overview The CDC Pipeline is a meta-template that deploys a complete, pre-wired Change Data Capture pipeline in a single install. It bundles three production-ready templates — PostgreSQL HA, Apache Kafka, and Debezium Server — and automatically coordinates the credentials, WAL settings, and internal hostnames that would otherwise require manual configuration between them. ### Architecture * **PostgreSQL HA** (Patroni + etcd + HAProxy) — Source database with logical replication enabled (`wal_level = logical`). HAProxy routes writes to the current primary; replicas are addressable directly. * **Kafka** (KRaft, no ZooKeeper) — Event streaming platform. Debezium publishes change events here; consumers read from it. * **Debezium Server** — CDC connector that tails the PostgreSQL WAL, converts row-level changes to structured events, and produces them to Kafka via SASL\_PLAINTEXT. Internal DNS names are automatically derived from the release name at install time: | Service | Internal Hostname | | ------------------------ | -------------------------------------------------------- | | PostgreSQL (via HAProxy) | `{release-name}-postgres-ha-proxy.{gvc}.cpln.local:5432` | | Kafka | `{release-name}-cluster.{gvc}.cpln.local:9092` | | Debezium | `{release-name}-debezium.{gvc}.cpln.local` | ### What Gets Created Everything from each bundled template: * **From PostgreSQL HA**: Stateful PostgreSQL Workload (3 replicas), etcd cluster, HAProxy workload, Volume Sets, secrets, identity and policy. * **From Kafka**: Stateful Kafka broker workloads (KRaft, 3 replicas), Kafka Exporter, JMX Exporter, Kafbat UI, Volume Sets, secrets, identity and policy. * **From Debezium Server**: Standard Debezium Server Workload, Volume Set (file offset storage), secrets, identity and policy. This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites This template has no external prerequisites — PostgreSQL, Kafka, and Debezium are all deployed together and auto-configured. To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The first thing to do after installing is **replace all `changeme-*` placeholder values** with real secrets. The template validates at deploy time that shared credentials are consistent — mismatches are caught before any workload starts. ### Shared Credentials These values are used by multiple components and must be set once in a coordinated way. The default `values.yaml` pre-wires them: **PostgreSQL ↔ Debezium:** | Setting | PostgreSQL path | Debezium path | | ------------- | --------------------------------------------- | ------------------------------------------ | | Database name | `postgres-highly-available.postgres.database` | `debezium-server.source.database.name` | | Username | `postgres-highly-available.postgres.username` | `debezium-server.source.database.user` | | Password | `postgres-highly-available.postgres.password` | `debezium-server.source.database.password` | **Kafka ↔ Debezium:** | Setting | Kafka path | Debezium path | | ------------- | --------------------------------------------- | ----------------------------------------- | | SASL username | `kafka.kafka.listeners.client.sasl.users` | `debezium-server.sink.kafka.saslUsername` | | SASL password | `kafka.kafka.listeners.client.sasl.passwords` | `debezium-server.sink.kafka.saslPassword` | ### Deploy-time Validation The template validates at install time that: * `postgres-highly-available.postgres.walLevel` is set to `logical` (required for CDC) * Database credentials match between the PostgreSQL and Debezium blocks * Debezium's Kafka SASL username appears in Kafka's configured user list If any of these are mismatched, the install fails with a descriptive error before any resources are created. ### Key Configuration Sections #### What Tables to Capture Set `debezium-server.source.tableIncludeList` to a comma-separated list of `schema.table` names: ```yaml theme={null} debezium-server: source: tableIncludeList: "public.users,public.orders,public.products" ``` Leave empty to capture all tables. #### Debezium Heartbeat (Recommended) The default configuration enables Debezium heartbeats every 5 seconds to keep the replication slot active and prevent WAL accumulation during low-traffic periods. After the pipeline is running, create the heartbeat table in PostgreSQL: ```sql theme={null} CREATE TABLE IF NOT EXISTS debezium_heartbeat (id INT PRIMARY KEY, ts TIMESTAMPTZ); INSERT INTO debezium_heartbeat VALUES (1, now()); ``` #### Resource Sizing Adjust resources per component based on your workload: ```yaml theme={null} postgres-highly-available: resources: minCpu: 500m minMemory: 1Gi maxCpu: 1 maxMemory: 2Gi kafka: kafka: cpu: 1000m memory: 2000Mi debezium-server: resources: cpu: 500m memory: 512Mi ``` #### Component Versions | Component | Template Version | Software Version | | --------------- | ---------------- | -------------------------- | | PostgreSQL HA | 2.4.2 | PostgreSQL 17 (Patroni) | | Kafka | 4.0.1 | Apache Kafka 3.9.1 (KRaft) | | Debezium Server | 1.1.1 | Debezium 3.0 | **Versions `1.0.0` and `1.0.1` did not compact the etcd cluster inside the bundled PostgreSQL HA store**, so etcd's backend grows with time alone and goes read-only once it reaches its 2 GiB quota — after roughly 110 days — taking PostgreSQL failover with it. This template bundles PostgreSQL HA unconditionally, with no toggle to opt out, so **every install on `1.0.0` or `1.0.1` is affected**; see [etcd History Compaction](/template-catalog/templates/postgres-highly-available#etcd-history-compaction) for the mechanism and the symptoms. Upgrade to `1.0.2` or later to turn compaction on: that stops further growth but cannot shrink a backend that has already grown, and a cluster that has already raised a `NOSPACE` alarm needs operator recovery rather than an upgrade. ### Using External PostgreSQL or Kafka To replace the bundled PostgreSQL or Kafka with an external instance, override the hostname/bootstrap servers explicitly. The template will still deploy the other bundled components: ```yaml theme={null} debezium-server: source: database: hostname: "my-external-postgres.example.com" sink: kafka: bootstrapServers: "my-external-kafka.example.com:9092" ``` When using an external PostgreSQL, ensure logical replication is already enabled (`wal_level = logical`) before installing. ## Connecting Consumers to Kafka Applications consuming change events connect to Kafka using SASL\_PLAINTEXT. The internal Kafka bootstrap address: ``` {release-name}-cluster.{gvc}.cpln.local:9092 ``` Example Kafka client properties: ```properties theme={null} bootstrap.servers={release-name}-cluster.{gvc}.cpln.local:9092 security.protocol=SASL_PLAINTEXT sasl.mechanism=PLAIN sasl.jaas.config=org.apache.kafka.common.security.plain.PlainLoginModule required \ username="debezium" \ password="your-kafka-debezium-password"; ``` Change events are published to topics named `{serverName}.{schema}.{table}` — e.g. `dbserver1.public.users`. ## Kafbat UI The Kafbat UI (Kafka web dashboard) is included and enabled by default. Access it via `cpln workload connect` or configure its `firewall.external_inboundAllowCIDR` to expose it externally. ## External References Deploy Debezium Server standalone against any PostgreSQL, MySQL, or other source The HA PostgreSQL template bundled in this pipeline The Kafka template bundled in this pipeline Full Debezium connector and server reference # Chatwoot Source: https://docs.controlplane.com/template-catalog/templates/chatwoot Deploy Chatwoot on Control Plane using the Template Catalog. Open-source customer engagement with a live-chat widget, shared inboxes, and an omni-channel agent desk, backed by a Sidekiq worker, a pgvector-capable PostgreSQL, and a bundled Redis. ## Overview Chatwoot is an open-source customer engagement platform — an embeddable live-chat widget, a shared email inbox, and an omni-channel agent desk in one application. This template deploys the **Community Edition** (`-ce`, MIT core): a Rails web tier serving the dashboard, REST API, widget, and WebSockets on port `3000`, a Sidekiq worker for background jobs and migrations, a bundled single-node Redis, and a highly available pgvector-capable PostgreSQL cluster. Attachments go to a persistent volume or to object storage. ### Architecture * **Chatwoot web** — A stateful workload running the official image, serving the agent dashboard, the REST and public APIs, the chat widget, and the ActionCable WebSocket endpoint on port `3000`. Scale it with `chatwoot.replicas`. * **Chatwoot worker** — A standard workload running Sidekiq from the same image. It also runs the database migrations and the first-run bootstrap on boot, so it is a fixed single replica; background throughput scales through `worker.concurrency` instead. * **PostgreSQL (HA, default)** — The [postgres-highly-available](/template-catalog/templates/postgres-highly-available) template as a subchart: 3× Patroni PostgreSQL 17.5, 3× etcd, and a HAProxy leader endpoint Chatwoot connects through. Its image ships **pgvector**, which Chatwoot's schema requires. * **PostgreSQL (dev/lightweight, optional)** — The single-instance [postgres](/template-catalog/templates/postgres) template instead, on a pgvector image. * **Redis** — A bundled single-node stateful workload with AOF persistence and password authentication, carrying the Sidekiq queues, the ActionCable pub/sub channel that drives every live update, the cache, and the one-time install-onboarding flag. ### What Gets Created * **Stateful Chatwoot Web Workload** — `{release}-chatwoot` on port `3000`, at `chatwoot.replicas` replicas, with `/api` as its readiness probe and `/health` as its liveness probe. * **Standard Chatwoot Worker Workload** — `{release}-chatwoot-worker`, a fixed single replica running Sidekiq, migrations, and the first-run bootstrap. It has no inbound access and no ports. * **Stateful Redis Workload** — `{release}-chatwoot-redis` on port `6379`, pinned to one node, reachable only from inside the GVC. * **Database Workloads** — HA mode: a stateful Patroni PostgreSQL workload, a stateful etcd workload, and a HAProxy leader-routing workload. Single-instance mode: one stateful PostgreSQL workload. * **Volume Sets** — `{release}-chatwoot-redis-vs` for the Redis AOF file at `/data`, `{release}-chatwoot-storage` for local attachments at `/app/storage` (only when `storage.type` is `local`), plus the database subchart's volumes. * **Secrets** — A template-created dictionary secret (`{release}-chatwoot-creds`) holding the bundled Redis password, two opaque start-script secrets for the web and worker containers, and the database credentials from the subchart. Your prerequisite keys secret is *not* created by the template — see [Prerequisites](#prerequisites). * **Identity & Policy** — One identity shared by the web and worker workloads, and a policy granting it `reveal` on exactly the secrets those workloads read: the database credentials, the two start scripts, the Redis credentials secret, your prerequisite secret, and — when configured — the S3 key and SMTP secrets. In keyless S3 mode the identity also carries the cloud account link and your bucket-scoped IAM policy. This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites Chatwoot signs its sessions and encrypts sensitive columns with four keys that you supply through a [dictionary secret](/guides/create-secret/dictionary) created **before** installing. The values are never passed through Helm values. All four keys are required. Generate them with `openssl`: ```bash theme={null} cpln secret create-dictionary --name my-chatwoot-secrets \ --entry SECRET_KEY_BASE="$(openssl rand -hex 64)" \ --entry ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY="$(openssl rand -hex 16)" \ --entry ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY="$(openssl rand -hex 16)" \ --entry ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT="$(openssl rand -hex 16)" ``` Set `secrets.name` to the secret's name (default `my-chatwoot-secrets`). Secrets are org-level, so no GVC flag is involved. Store a copy somewhere safe, outside Control Plane. All four keys are write-once for the life of the installation. Change the database password (`postgresHA.postgres.password` or `postgres.config.password`) and the Redis password (`redis.auth.password`) from their placeholder defaults before installing. Both seed their component on first boot and are not updated by later value edits. Create the keys secret **before** installing — the workloads reference it by name, and a missing secret pauses the deployment indefinitely, which looks like a broken install. The keys are also write-once: rotating `SECRET_KEY_BASE` logs out every user, and rotating any `ACTIVE_RECORD_ENCRYPTION_*` key makes stored two-factor secrets undecryptable, locking out every agent who enabled MFA. Everything else works with the defaults. Three optional features need their own setup first: * **Object-storage attachments** — an existing bucket plus either a Control Plane cloud account (AWS S3) or a static-key secret (S3-compatible servers). See [Attachment Storage](#attachment-storage). Required if you plan to run more than one web replica. * **Authenticated SMTP** — a dictionary secret holding `SMTP_USERNAME` and `SMTP_PASSWORD`. See [Email](#email). * **Database backups** — a bucket and access setup for one of the supported providers. See [Backing Up](#backing-up). Once your keys secret exists, install the template using your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## First Run Chatwoot ships no default account. The first browser session to reach the endpoint runs the onboarding wizard and creates the super admin. On the default HA database path a first install takes roughly eight minutes end to end: the Patroni cluster elects a leader, the worker then loads the schema and runs 158 migrations, and only then does the web tier pass its readiness probe. The single-instance database path is closer to three and a half minutes. Browse to the canonical `*.cpln.app` endpoint of the `{release}-chatwoot` workload. It redirects to `/installation/onboarding`, where the form creates your account and the super admin user. Do this as soon as the workload is ready — the wizard is unauthenticated until it is completed. In the dashboard, add an inbox — a website widget for live chat, or an API channel for a custom integration. The widget snippet and the API channel identifier are issued there. Agent invitations, password resets, and email-channel replies are all delivered by mail. Set up [SMTP](#email) before you invite your team. Once an inbox exists, incoming visitor messages and agent replies travel over the WebSocket connection: an agent sees a new message without refreshing, and the widget receives the agent's reply live. ## Configuration The default `values.yaml` for this template: ```yaml theme={null} chatwoot: image: chatwoot/chatwoot:v4.16.2-ce # Community Edition (MIT core); pin a released tag replicas: 1 # >1 REQUIRES storage.type s3 or s3-compatible frontendUrl: "" # public base URL used in links; empty = derive from the canonical endpoint resources: minCpu: 250m maxCpu: 1000m minMemory: 512Mi maxMemory: 2Gi # a Rails process under boot migrations OOMs below this worker: # Sidekiq; single replica — it also runs migrations and the first-run bootstrap concurrency: 10 # SIDEKIQ_CONCURRENCY; also the worker's Postgres pool size resources: minCpu: 250m maxCpu: 1000m minMemory: 512Mi maxMemory: 2Gi secrets: name: my-chatwoot-secrets # dictionary secret with the four root-of-trust keys — MUST exist before install storage: type: local # local | s3 | s3-compatible (object storage required for replicas > 1) local: volumeset: capacity: 10 # GiB (minimum 10); mounted at /app/storage s3: # AWS S3 — keyless only (no static keys accepted) bucket: my-chatwoot-bucket region: us-east-1 cloudAccountName: my-s3-cloud-account # Control Plane cloud account policyName: my-chatwoot-s3-policy # your bucket-scoped IAM policy s3Compatible: # MinIO / SeaweedFS / Spaces — static keys bucket: my-chatwoot-bucket region: us-east-1 # any value; most S3-compatible servers ignore it endpoint: http://my-minio-workload:9000 # S3 API address, with scheme and port forcePathStyle: true # true for most S3-compatible servers auth: secretName: my-chatwoot-s3-keys # dictionary secret with STORAGE_ACCESS_KEY_ID + STORAGE_SECRET_ACCESS_KEY smtp: enabled: false # off = agent invites, password resets and email replies FAIL address: smtp.example.com # SMTP_ADDRESS (note: not SMTP_HOST) port: 587 domain: "" # HELO domain; empty = omitted authentication: login # plain | login | cram_md5; empty = unauthenticated relay enableStarttlsAuto: true # false for a plaintext mail catcher fromEmail: Chatwoot # MAILER_SENDER_EMAIL auth: secretName: "" # optional dictionary secret with SMTP_USERNAME + SMTP_PASSWORD publicAccess: enabled: true # HTTPS UI, API, widget and WebSockets on the auto *.cpln.app endpoint internalAccess: type: same-gvc # options: none, same-gvc, same-org, workload-list workloads: [] # used only with workload-list, e.g. //gvc/GVC_NAME/workload/WORKLOAD_NAME postgresHA: # default: highly available PostgreSQL 17.5, pgvector native enabled: true postgres: username: chatwoot password: change-me-chatwoot-pg # change before installing database: chatwoot replicas: 3 resources: # per Patroni replica; the default install runs 3 of these minCpu: 250m maxCpu: 1000m minMemory: 512Mi maxMemory: 2Gi volumeset: capacity: 10 # GiB per replica (minimum 10) backup: enabled: false # optional — see Backing Up mode: logical # logical | wal-g logical: image: ghcr.io/controlplane-com/backup-images/postgres-backup:17.1.0 schedule: "0 2 * * *" # daily at 2am UTC walg: intervalSeconds: 21600 provider: aws # aws | gcp | minio aws: bucket: my-chatwoot-pg-backup-bucket region: us-east-1 cloudAccountName: my-s3-cloud-account policyName: my-chatwoot-pg-backup-policy prefix: postgres/backups gcp: bucket: my-chatwoot-pg-backup-bucket cloudAccountName: my-gcs-cloud-account prefix: postgres/backups minio: endpoint: http://my-minio-workload:9000 bucket: my-chatwoot-pg-backup-bucket accessKey: my-minio-username secretKey: my-minio-password prefix: postgres/backups postgres: # dev/lightweight: single-instance PostgreSQL (disable postgresHA first) enabled: false image: pgvector/pgvector:pg18 # MUST carry pgvector — stock postgres:18 does not config: username: chatwoot password: change-me-chatwoot-pg # change before installing database: chatwoot resources: minCpu: 250m maxCpu: 1000m minMemory: 512Mi maxMemory: 1Gi volumeset: capacity: 10 # GiB (minimum 10) backup: enabled: false # optional — see Backing Up image: ghcr.io/controlplane-com/backup-images/postgres-backup:18.1.0 schedule: "0 2 * * *" # daily at 2am UTC provider: aws # aws | gcp | minio aws: bucket: my-chatwoot-pg-backup-bucket region: us-east-1 cloudAccountName: my-s3-cloud-account policyName: my-chatwoot-pg-backup-policy prefix: postgres/backups gcp: bucket: my-chatwoot-pg-backup-bucket cloudAccountName: my-gcs-cloud-account prefix: postgres/backups minio: endpoint: http://my-minio-workload:9000 bucket: my-chatwoot-pg-backup-bucket accessKey: my-minio-username secretKey: my-minio-password prefix: postgres/backups redis: # bundled single node — queues, live updates, cache (required) image: redis:8 auth: password: change-me-chatwoot-redis # letters/digits/-/_ only; change before installing resources: minCpu: 100m maxCpu: 400m minMemory: 256Mi maxMemory: 512Mi volumeset: capacity: 10 # GiB (minimum 10); AOF at /data ``` ### Chatwoot Web * `chatwoot.image` — The Community Edition image, used by both the web and worker workloads. Pin a released `-ce` tag. * `chatwoot.replicas` — Web-tier replica count. Anything above `1` requires object storage; see [Scaling](#scaling). * `chatwoot.frontendUrl` — The public base URL Chatwoot puts in the widget snippet, email links, and callbacks, including the `https://` scheme. Leave it empty to derive it from the web workload's canonical endpoint; set it when you serve Chatwoot on a custom domain. The worker advertises the same URL, so mailer links always point at the web tier. * `chatwoot.resources` — CPU and memory bounds for the web container. The memory ceiling leaves headroom for the Rails boot path. ### Sidekiq Worker * `worker.concurrency` — Number of background jobs Sidekiq processes in parallel. It also sets this process's PostgreSQL connection-pool size. * `worker.resources` — CPU and memory bounds for the worker container. The worker is a fixed single replica: it runs the database migrations and the first-run bootstrap, which are not safe to run concurrently on a fresh database. Scale background throughput with `worker.concurrency` instead. The worker workload has no probes — Sidekiq exposes no port, and its failure mode is process exit, which restarts the container. It therefore reports `ready` as soon as it is scheduled, even while it is still migrating. Worker readiness is not a health signal; read its logs instead. ### Attachment Storage `storage.type` decides where uploaded files and images are kept. Attachments are written to a persistent volume set mounted at `/app/storage` on the web workload, and survive restarts, redeploys, and upgrades under the same release name. ```yaml theme={null} storage: type: local local: volumeset: capacity: 10 # GiB (minimum 10) ``` The volume is attached to the web workload only, so the Sidekiq worker cannot read attachments in this mode — attachment emails and ActiveStorage analyze and purge jobs cannot succeed. Local storage also works only with a single replica. Use object storage for production. AWS S3 uses a Control Plane cloud identity: no credentials are stored anywhere, and the workload's identity vends temporary credentials at runtime. This is the only supported way to reach AWS S3 — the chart accepts static keys for S3-compatible endpoints only. Create an S3 bucket. Set `storage.s3.bucket` and `storage.s3.region` to match. If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) for your AWS account. Set `storage.s3.cloudAccountName` to its name. Create an AWS IAM policy with the JSON below (replace `YOUR_BUCKET_NAME` with your bucket), then set `storage.s3.policyName` to the policy's name: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET_NAME", "arn:aws:s3:::YOUR_BUCKET_NAME/*" ] } ] } ``` Set `storage.type: s3`. The template attaches the cloud account and your policy to the Chatwoot identity, and no access keys exist anywhere. S3-compatible servers cannot federate with a cloud account, so they use static access keys held in a dictionary secret. Create the bucket on your server. For the [MinIO](/template-catalog/templates/minio) template deployed in the same GVC, the S3 API address is `http://WORKLOAD_NAME.GVC_NAME.cpln.local:9000`. Set `storage.type: s3-compatible`, `storage.s3Compatible.endpoint` to the S3 API address including scheme and port, and keep `storage.s3Compatible.forcePathStyle: true` (required by most S3-compatible servers). Create a dictionary secret with the server's credentials **before** installing, and set `storage.s3Compatible.auth.secretName` to its name: ```bash theme={null} cpln secret create-dictionary --name my-chatwoot-s3-keys \ --entry STORAGE_ACCESS_KEY_ID=YOUR_ACCESS_KEY \ --entry STORAGE_SECRET_ACCESS_KEY=YOUR_SECRET_KEY ``` The Chatwoot identity is granted `reveal` on exactly this secret. Chatwoot serves an attachment by redirecting the browser to the storage endpoint. `storage.s3Compatible.endpoint` must therefore be an address your agents' and visitors' browsers can reach — an in-GVC-only address such as `http://my-minio.my-gvc.cpln.local:9000` makes attachments appear broken outside the GVC, even though the upload itself succeeded. ### Scaling `chatwoot.replicas` sets how many web replicas run. Replicas share the database and the bundled Redis, so a message received by one replica reaches a WebSocket client connected to another. ```yaml theme={null} chatwoot: replicas: 2 storage: type: s3 s3: bucket: my-chatwoot-bucket region: us-east-1 cloudAccountName: my-s3-cloud-account policyName: my-chatwoot-s3-policy ``` `chatwoot.replicas` above `1` requires `storage.type` `s3` or `s3-compatible`. Local attachments live on a per-replica volume, so a file uploaded through one replica would `404` from another. The chart refuses to render the combination. At two replicas, forcing a redeployment of the web workload served every request without a single HTTP error, and a live agent reply still reached the visitor's socket while both replicas were serving. ### Email SMTP is **off by default**, and a default install works without it — the onboarding wizard creates the super admin with no email confirmation, and that account signs in with its password. ```yaml theme={null} smtp: enabled: true address: smtp.example.com port: 587 domain: example.com authentication: login enableStarttlsAuto: true fromEmail: Chatwoot auth: secretName: my-chatwoot-smtp # dictionary secret with SMTP_USERNAME + SMTP_PASSWORD ``` * `smtp.address` — The relay hostname. Chatwoot reads `SMTP_ADDRESS`, not `SMTP_HOST`. * `smtp.domain` — The HELO domain. Empty omits it. * `smtp.authentication` — `plain`, `login`, or `cram_md5`. Leave it empty for an unauthenticated relay. * `smtp.enableStarttlsAuto` — Set `false` only for a plaintext relay such as a local mail catcher. * `smtp.fromEmail` — The `From` address on outbound mail. * `smtp.auth.secretName` — Optional dictionary secret with the relay credentials, created before installing: ```bash theme={null} cpln secret create-dictionary --name my-chatwoot-smtp \ --entry SMTP_USERNAME=apikey --entry SMTP_PASSWORD=YOUR_PASSWORD ``` Leave it empty for a relay that needs no authentication. When it is set, the Chatwoot identity is granted `reveal` on exactly that secret. With SMTP disabled, Chatwoot falls back to `sendmail`, which is absent from the image, so nothing is delivered: agent invitations, password resets, and email-channel replies all fail. Configure `smtp.*` before inviting agents. ### Access * `publicAccess.enabled` — Serve the dashboard, API, widget, and WebSockets on the auto-assigned `*.cpln.app` HTTPS endpoint (default). Because the widget and visitors live outside the GVC, public access is on by default. Set to `false` for an internal-only instance: external requests are refused at the edge, and in-GVC callers still reach it per `internalAccess`. * `internalAccess.type` — Controls which workloads can reach the Chatwoot web tier over the internal network: | Type | Description | | --------------- | ---------------------------------------------------------------------- | | `none` | No internal access. | | `same-gvc` | Allow access from all workloads in the same GVC (default). | | `same-org` | Allow access from all workloads in the same organization. | | `workload-list` | Allow access only from workloads listed in `internalAccess.workloads`. | The worker and the bundled Redis are never publicly reachable: the worker accepts no inbound traffic at all, and Redis accepts only same-GVC traffic. ### Database Enable exactly one of `postgresHA` (default) or `postgres` — the chart refuses to render with both or neither enabled. Chatwoot is wired to the active database automatically: the HAProxy leader endpoint in HA mode, or the single instance directly. * **`postgresHA` (default)** — 3× Patroni PostgreSQL 17.5 with etcd and a HAProxy leader endpoint. Its image ships pgvector natively, which is why it is the default. A first install on this path takes roughly eight minutes. * **`postgres`** — One PostgreSQL instance for lighter, non-production deployments. It reaches ready in a few minutes but has no failover. In both modes, **change the database password before installing** (`postgresHA.postgres.password` / `postgres.config.password`) — it seeds the database on first boot and is not updated by later value edits. **Template version `1.0.0` did not compact the etcd cluster inside the bundled highly available database**, so etcd's backend grows with time alone and goes read-only once it reaches its 2 GiB quota — after roughly 110 days — taking PostgreSQL failover with it. Only installs running the HA database are affected (`postgresHA.enabled`, the default here); see [etcd History Compaction](/template-catalog/templates/postgres-highly-available#etcd-history-compaction) for the mechanism and the symptoms. Upgrade to `1.0.1` or later to turn compaction on: that stops further growth but cannot shrink a backend that has already grown, and a cluster that has already raised a `NOSPACE` alarm needs operator recovery rather than an upgrade. Choose the database mode **before** installing — it cannot be switched later. Flipping `postgresHA.enabled` / `postgres.enabled` on a live release points Chatwoot at a different, empty database (a separate volume set, and a different PostgreSQL major version), so the app re-runs onboarding and your existing conversations are orphaned rather than migrated. In single-instance mode, `postgres.image` must carry pgvector. Chatwoot's schema runs `CREATE EXTENSION "vector"`, which the stock `postgres:` image does not provide. The chart refuses to render against a stock `postgres:` image rather than letting the install fail later as a cryptic schema error — keep the default `pgvector/pgvector:pg18` unless you supply your own pgvector build. On the HA path the worker crash-loops a handful of times during the first install, logging `PG::ConnectionBad … server closed the connection unexpectedly` while Patroni elects a leader. This is expected and self-heals as soon as the leader accepts writes. ### Redis * `redis.image` — The bundled Redis image. * `redis.auth.password` — Password for the bundled Redis, embedded in Chatwoot's `REDIS_URL`. **Change it before installing.** Only letters, digits, `-`, and `_` are accepted; other characters would corrupt the connection URL, and the chart rejects them. * `redis.resources` — CPU and memory bounds for the Redis container. * `redis.volumeset.capacity` — Volume size in GiB for the append-only file at `/data`. Persistence is on and eviction is disabled, so a queued job is never silently dropped. Redis is required and runs as a **single node by design**, not as a simplification: it carries the pub/sub channel behind every live update, and Redis does not propagate published messages between replicas, so a multi-node topology would silently drop a share of the broadcasts that make the dashboard and widget update in real time. That single node has two operational consequences worth planning for. **Restart the web workload after any Redis restart.** If the bundled Redis restarts — a redeploy, a reschedule, or an upgrade — the queues and the cache reconnect on their own, but the web tier's real-time subscriber does not. Live updates then stop silently: nothing is logged, the health endpoint stays green, and agents and visitors simply stop seeing new messages until they refresh. Force a redeployment of the `{release}-chatwoot` workload to restore it, which takes about three to four minutes. ```bash theme={null} cpln workload force-redeployment {release}-chatwoot --gvc GVC_NAME ``` **Expect a few minutes of `503`s during a Helm upgrade.** An upgrade restarts the bundled Redis, and the web readiness endpoint reports unhealthy without Redis, so every web replica leaves the load balancer until Redis is back — measured at roughly 32% failed requests across a three-minute window, even at two replicas. Upgrade during a quiet period. A redeployment of the web workload alone does not restart Redis and rolls cleanly. ## Connecting | What | Value | | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | | Public UI, API, and widget | `https://.cpln.app` — `status.canonicalEndpoint` of `{release}-chatwoot` (`cpln workload get {release}-chatwoot -o yaml`) | | WebSocket endpoint | `wss://.cpln.app/cable` — used by the dashboard and the widget, no separate configuration | | Health checks | `/api` (readiness — reports PostgreSQL and Redis status) and `/health` (liveness), both unauthenticated | | Internal (same GVC) | `http://{release}-chatwoot.{gvc}.cpln.local:3000` | | Login | The super admin account created by the onboarding wizard | | PostgreSQL (internal, HA mode) | `{release}-postgres-ha-proxy.{gvc}.cpln.local:5432`, credentials in the `{release}-postgres-config` secret | | PostgreSQL (internal, single mode) | `{release}-postgres.{gvc}.cpln.local:5432`, credentials from `postgres.config` | | Redis (internal) | `{release}-chatwoot-redis.{gvc}.cpln.local:6379`, password from `redis.auth.password` | ## Managing the Installation Chatwoot keeps instance-wide settings in its database rather than in environment variables, so they are managed in the application after install, not through Helm values. Sign in as the super admin and open `https://.cpln.app/super_admin`. Under **Settings** you can change instance configuration, including `ENABLE_ACCOUNT_SIGNUP`, which controls whether visitors can create their own accounts. **Self-serve signup is off on a fresh install** — accounts are created by the onboarding wizard and by agent invitations until you turn it on. ## Backing Up Database backups are optional and disabled by default. They cover the PostgreSQL database — the conversations, contacts, inboxes, and users that make up your Chatwoot instance. Attachments are not included; in object-storage mode they live in your bucket, and in local mode on the attachment volume set. Enable backups with `postgresHA.backup.enabled` or `postgres.backup.enabled` (matching your database mode), and complete the storage setup for your provider **before** installing. The values below are shown under `backup.*` — set them within the enabled database block. Create an S3 bucket. Set `backup.aws.bucket` and `backup.aws.region` to match. If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) for your AWS account. Set `backup.aws.cloudAccountName` to its name. Create an AWS IAM policy with the JSON below (replace `YOUR_BUCKET`), then set `backup.aws.policyName` to the policy's name: ```json theme={null} { "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket", "s3:GetObjectVersion", "s3:DeleteObjectVersion" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET", "arn:aws:s3:::YOUR_BUCKET/*" ] }] } ``` Create a GCS bucket. Set `backup.gcp.bucket` to its name. If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) for your GCP project. Set `backup.gcp.cloudAccountName` to its name — access is keyless. Grant the `Storage Object Admin` role (`roles/storage.objectAdmin`) on that bucket to the GCP service account created for the Cloud Account. Create your bucket on the server. Set `backup.minio.bucket` to its name. Set `backup.minio.endpoint` to the S3 API address including scheme and port. For the [MinIO](/template-catalog/templates/minio) template deployed in the same GVC, this is `http://WORKLOAD_NAME.GVC_NAME.cpln.local:9000`. Set `backup.minio.accessKey` and `backup.minio.secretKey` to credentials with access to the bucket. In HA mode, `backup.mode` selects `logical` (a scheduled `pg_dump` run by a cron workload) or `wal-g` (continuous WAL archiving). The single-instance mode takes scheduled logical dumps. `backup.prefix` is the key prefix within the bucket, and `backup.*.schedule` is a standard cron expression in UTC. ## Important Notes * **Create the keys secret before installing** — `secrets.name` must point at an existing dictionary secret holding `SECRET_KEY_BASE` and the three `ACTIVE_RECORD_ENCRYPTION_*` keys. A missing secret pauses the deployment and the install looks broken until it exists. * **All four keys are write-once** — rotating `SECRET_KEY_BASE` logs out every user, and rotating an `ACTIVE_RECORD_ENCRYPTION_*` key makes stored two-factor secrets undecryptable. Back the values up outside Control Plane. * **Complete the onboarding wizard right after install** — the first browser session to reach the endpoint creates the super admin account, with no email confirmation. * **Restart the web workload after any Redis restart** — queues and cache recover on their own, but live updates stay dead, silently, until the web tier restarts. * **A Helm upgrade restarts the bundled Redis and costs a few minutes of `503`s** — the web readiness probe fails while Redis is down, so replicas leave the load balancer. Upgrade during a quiet period. * **Choose the database mode before installing** — switching `postgresHA` and `postgres` on a live release points Chatwoot at a different, empty database. * **In single-instance mode the database image must carry pgvector** — keep `postgres.image` on a pgvector build; the chart refuses to render against a stock `postgres:` image. * **`chatwoot.replicas` above `1` requires object storage** — local attachments are per-replica and would `404` across replicas. * **In `local` storage mode the worker cannot read attachments** — the volume is attached to the web workload only, so attachment emails and ActiveStorage analyze and purge jobs fail. Use object storage for production. * **AWS S3 is keyless only** — a cloud account plus a bucket-scoped IAM policy. Static keys are accepted only for S3-compatible endpoints. * **With SMTP off, no mail is delivered** — agent invites, password resets, and email-channel replies all fail. Configure `smtp.*` before inviting agents. * **Do not scale the worker** — it is a fixed singleton that also runs migrations and the first-run bootstrap. Scale background throughput with `worker.concurrency`. * **Self-serve signup is managed in the app, not in values** — turn it on at `/super_admin` → **Settings**; it is off on a fresh install. * **Enterprise features are not included** — the `-ce` image omits SSO/SAML and SCIM, audit logs, agent capacity management, custom branding, SLA policies, and Captain AI. The core is MIT-licensed. * **Data survives restarts and upgrades** — conversations live in the database volume sets and local attachments in the storage volume set. Uninstalling deletes those volume sets and everything in them; your keys secret is yours and survives an uninstall. ## External References Official self-hosting documentation Every setting the Chatwoot application reads from its environment What the Community Edition image includes and excludes Inboxes, channels, agents, and day-to-day usage Source code and release notes View the source files, default values, and chart definition # ClickHouse Source: https://docs.controlplane.com/template-catalog/templates/clickhouse Deploy ClickHouse on Control Plane using the Template Catalog. Covers configuration, volumes, scaling, and ClickHouse Keeper coordination with S3, GCS, Azure Blob Storage, or Hetzner Object Storage. ## Overview ClickHouse is a high-performance, column-oriented analytical database designed for real-time querying and data warehousing at scale. This template deploys ClickHouse in either **single-node** or **cluster** mode depending on how locations are configured, backed by object storage (AWS S3, GCS, Azure Blob Storage, or Hetzner Object Storage) for long-term scalable storage and a local volume for fast read caching. ### Deployment Modes | Mode | Configuration | Keeper | | ------------------------ | -------------------------- | --------------------------------- | | **Single-node** | 1 location, `replicas: 1` | Not deployed | | **Single-shard cluster** | 1 location, `replicas` > 1 | Deployed | | **Multi-shard cluster** | 3+ locations | Deployed across first 3 locations | 2 locations is not supported — use 1 (single-node or single-shard) or 3+. ### What Gets Created * **GVC** — A dedicated GVC across the specified locations. * **Stateful ClickHouse Server Workload** — The main analytical database workload with configurable replicas per location. * **Stateful ClickHouse Keeper Workload** *(cluster mode only)* — The coordination service workload (1 replica per location, always 3 total). * **Volume Sets** — Persistent storage for the server (metadata, state, and system files), and for Keeper in cluster mode. * **Secrets** — A database config secret with credentials and cluster name, startup script secrets for ClickHouse Server and Keeper, and a storage configuration secret for the selected provider (AWS S3, GCS, Azure Blob Storage, or Hetzner Object Storage). * **Identity & Policy** — An identity bound to the workloads with `reveal` access to the template secrets, and cloud access for reading and writing to object storage. ### Architecture In cluster mode, each location maps to one ClickHouse Keeper replica, forming a 3-node quorum for distributed coordination. ClickHouse Server replicas communicate with Keeper using Control Plane's internal DNS. In single-node mode, no Keeper is deployed. Primary data is stored in the configured object storage bucket in all modes; a local scratch volume serves as a fast read cache. To minimize network egress costs, deploy all locations in the same cloud provider and keep your object storage bucket in the same region(s). Using 1 replica per location for the ClickHouse server workload is sufficient for most cluster deployments. ## Prerequisites Before installing this template, configure object storage access for your chosen provider. ### AWS S3 1. Create an S3 bucket. Note the bucket name and region — you will set these as `aws.bucket` and `aws.region` in your values file. 2. If you do not have a Control Plane Cloud Account set up, follow the [Create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) guide. Set `aws.cloudAccountName` to the name of your Cloud Account. 3. Create an IAM policy with the following JSON, replacing `YOUR_BUCKET_NAME`: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket", "s3:GetObjectVersion", "s3:DeleteObjectVersion" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET_NAME", "arn:aws:s3:::YOUR_BUCKET_NAME/*" ] } ] } ``` 4. Set `aws.policyName` in your values file to the name of the policy created in step 3. ### GCS ClickHouse requires S3-compatible HMAC authentication for GCS. A Control Plane Cloud Account is not required. 1. Create a GCS bucket. Set `gcp.bucket` in your values file to the bucket name. 2. In the GCP console, navigate to **Settings > Interoperability** and click **Create a key for a service account**. 3. Click **Create new account**, name your service account, and assign the **Storage Object Admin** role under Permissions. 4. Copy the generated HMAC key and set `gcp.accessKeyId` and `gcp.secretAccessKey` in your values file. Alternatively, use the `gcloud` CLI: ```bash theme={null} gcloud config set project YOUR_PROJECT_ID gcloud storage buckets create gs://YOUR_BUCKET_NAME --location=NAM4 gcloud iam service-accounts create clickhouse-storage gcloud projects add-iam-policy-binding $(gcloud config get-value project) \ --member="serviceAccount:clickhouse-storage@$(gcloud config get-value project).iam.gserviceaccount.com" \ --role="roles/storage.objectAdmin" gsutil hmac create clickhouse-storage@$(gcloud config get-value project).iam.gserviceaccount.com ``` ### Azure Blob Storage ClickHouse uses Azure's native Blob Storage SDK. A Control Plane Cloud Account is not required — authentication uses a storage account access key directly. 1. In the [Azure Portal](https://portal.azure.com), go to **Storage accounts → Create**. Use **Standard** performance, **LRS** redundancy, and leave hierarchical namespace off. 2. Inside the storage account, go to **Containers → + Container** and create a container (e.g. `clickhouse-data`). Set access level to **Private**. 3. Go to **Security + networking → Access keys** and copy either `key1` or `key2`. 4. Set the following in your values file: * `azure.storageAccount` — the storage account name * `azure.container` — the container name from step 2 * `azure.accountKey` — the access key from step 3 Alternatively, use the Azure CLI: ```bash theme={null} az storage account create \ --name YOUR_STORAGE_ACCOUNT \ --resource-group YOUR_RESOURCE_GROUP \ --sku Standard_LRS az storage container create \ --name clickhouse-data \ --account-name YOUR_STORAGE_ACCOUNT az storage account keys list \ --account-name YOUR_STORAGE_ACCOUNT \ --resource-group YOUR_RESOURCE_GROUP \ --query "[0].value" -o tsv ``` ### Hetzner Object Storage Hetzner Object Storage is S3-compatible. A Control Plane Cloud Account is not required — authentication uses an access key pair. Available regions: | Region | Location | | ------ | -------------------- | | `nbg1` | Nuremberg, Germany | | `hel1` | Helsinki, Finland | | `fsn1` | Falkenstein, Germany | 1. In the Hetzner Cloud console, go to **Object Storage** and create a bucket. Note the bucket name and region. 2. Go to **Security → S3 Credentials** and click **Generate credentials**. Save the access key and secret key immediately — the secret will not be shown again. 3. Set the following in your values file: * `hetzner.bucket` — the bucket name * `hetzner.region` — the region code (e.g. `nbg1`) * `hetzner.accessKeyId` — the access key from step 2 * `hetzner.secretAccessKey` — the secret key from step 2 ## Installation To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} gvc: name: clickhouse-gvc # Single-node mode: exactly 1 location with replicas: 1 — no Keeper, uses S3/GCS for storage # Single-shard cluster: 1 location with replicas > 1 — Keeper required # Multi-shard cluster: 3 or more locations — Keeper required # Note: 2 locations is not supported locations: - name: aws-us-east-2 replicas: 1 - name: aws-us-west-2 replicas: 1 - name: aws-us-east-1 replicas: 1 provider: aws # Options: aws, gcp, azure, or hetzner aws: # If enabled, all fields below are required - See prerequisites for guidance bucket: clickhouse-s3-bucket # Name of your S3 bucket region: us-east-1 # Region of your S3 bucket cloudAccountName: clickhouse-s3-cloudaccount # Name of your Cloud Account policyName: clickhouse-s3-policy # Name of your pre-created policy to allow access to the S3 bucket gcp: # If enabled, all fields below are required - See prerequisites for guidance bucket: clickhouse-gcs-bucket # Name of your GCS bucket accessKeyId: gcs-access-key-id # Access key ID for your GCS service account secretAccessKey: gcs-secret-access-key # Secret access key for your GCS service account azure: # If enabled, all fields below are required - See prerequisites for guidance storageAccount: myclickhousestorage # Name of your Azure Storage Account container: clickhouse-data # Name of your Blob Storage container accountKey: my-azure-storage-account-key # Access key for your Azure Storage Account hetzner: # If enabled, all fields below are required - See prerequisites for guidance bucket: clickhouse-hetzner-bucket # Name of your Hetzner Object Storage bucket region: nbg1 # Region of your bucket. Options: nbg1, hel1, fsn1 accessKeyId: my-hetzner-access-key-id # Access key ID for your Hetzner Object Storage secretAccessKey: my-hetzner-secret-access-key # Secret access key for your Hetzner Object Storage clusterName: my_cluster # Used in cluster mode only database: # Automatically create a database on initialization using the default user name: mydatabase password: mypassword volumeset: server: capacity: 10 # initial capacity in GiB (minimum is 10) keeper: capacity: 10 # initial capacity in GiB (minimum is 10) - cluster mode only server: image: clickhouse/clickhouse-server:25.10 resources: cpu: 2 memory: 2Gi internal_access: type: same-gvc # options: same-gvc, same-org, workload-list workloads: # Note: can only be used if type is same-gvc or workload-list #- //gvc/GVC_NAME/workload/WORKLOAD_NAME keeper: # cluster mode only image: clickhouse/clickhouse-keeper:25.10 resources: cpu: 2 memory: 2Gi internal_access: type: same-gvc # options: same-gvc, same-org, workload-list workloads: # Note: can only be used if type is same-gvc or workload-list #- //gvc/GVC_NAME/workload/WORKLOAD_NAME ``` This template creates a GVC with a default name defined in the values file. If you plan to deploy multiple instances, you **must assign a unique GVC name** for each deployment. ### Provider and Object Storage Set `provider` to `aws`, `gcp`, `azure`, or `hetzner`, then fill in the corresponding section. Only the active provider's fields are used. **AWS S3** | Field | Description | | ---------------------- | ------------------------------------------------------ | | `aws.bucket` | Name of the S3 bucket | | `aws.region` | AWS region where the bucket resides | | `aws.cloudAccountName` | Name of the Control Plane Cloud Account with S3 access | | `aws.policyName` | Name of the IAM policy granting access to the bucket | **GCS** | Field | Description | | --------------------- | -------------------------------------------------- | | `gcp.bucket` | Name of the GCS bucket | | `gcp.accessKeyId` | HMAC access key ID for the GCS service account | | `gcp.secretAccessKey` | HMAC secret access key for the GCS service account | **Azure Blob Storage** | Field | Description | | ---------------------- | --------------------------------------------- | | `azure.storageAccount` | Name of the Azure Storage Account | | `azure.container` | Name of the Blob Storage container | | `azure.accountKey` | Storage account access key (`key1` or `key2`) | **Hetzner Object Storage** | Field | Description | | ------------------------- | ----------------------------------------- | | `hetzner.bucket` | Name of the Hetzner Object Storage bucket | | `hetzner.region` | Bucket region — `nbg1`, `hel1`, or `fsn1` | | `hetzner.accessKeyId` | S3-compatible access key ID | | `hetzner.secretAccessKey` | S3-compatible secret access key | ### Cluster and Database * `clusterName` — The name used for distributed DDL queries. Only relevant in cluster mode. * `database.name` — Database created automatically on first initialization. * `database.password` — Password for the default ClickHouse user. **Change before deploying to production.** These values are only applied on first initialization when the data directory is empty. Updating them after the initial deployment will have no effect on the running cluster. To change credentials or the database name on an existing instance, use ClickHouse's native commands (e.g. `ALTER USER`, `RENAME DATABASE`). ### Images * `server.image` — ClickHouse Server container image. * `keeper.image` — ClickHouse Keeper container image. Only used in cluster mode. ### Resources and Storage * `server.resources` / `keeper.resources` — CPU and memory allocated to each workload. * `volumeset.server.capacity` — Persistent volume size in GiB for server state (minimum 10). * `volumeset.keeper.capacity` — Persistent volume size in GiB for Keeper state (minimum 10). Only used in cluster mode. ### Internal Access Both `server.internal_access` and `keeper.internal_access` control which workloads can reach each component: | Type | Description | | --------------- | --------------------------------------------------------------- | | `same-gvc` | Allow access from all workloads in the same GVC | | `same-org` | Allow access from all workloads in the same organization | | `workload-list` | Allow access only from specific workloads listed in `workloads` | ### Connecting to ClickHouse Once deployed, connect using the ClickHouse client from a workload in the same GVC: ```bash theme={null} clickhouse-client --host $WORKLOAD_NAME --password $PASSWORD ``` ## External References Official ClickHouse documentation Integrating ClickHouse with AWS S3 Integrating ClickHouse with Google Cloud Storage Integrating ClickHouse with Azure Blob Storage Integrating ClickHouse with S3-compatible providers including Hetzner Create a Control Plane Cloud Account for AWS access View the source files, default values, and chart definition # CockroachDB Source: https://docs.controlplane.com/template-catalog/templates/cockroach Deploy CockroachDB on Control Plane using the Template Catalog. Covers configuration, volumes, scaling, and multi-region Raft consensus with automatic failover. ## Overview CockroachDB is a distributed SQL database that provides automatic replication, horizontal scalability, and built-in fault tolerance across multiple regions. This template deploys a multi-region CockroachDB cluster on Control Plane as a stateful workload with replica-direct load balancing. Each location runs a configurable number of replicas that discover and join one another using Control Plane's internal DNS. On first deployment, the cluster initializes itself, creates a database and user, registers all regions, and sets the survival goal to `SURVIVE REGION FAILURE`. ### What Gets Created * **GVC** — A dedicated GVC across the specified locations. * **Stateful CockroachDB Workload** — CockroachDB (`v25.4.0`) with per-location replica scaling and replica-direct load balancing. * **Standard PgBouncer Workload** *(enabled by default)* — A connection pooler that multiplexes application connections across all CockroachDB nodes. When enabled, this becomes the primary connection endpoint. * **Volume Set** — Persistent ext4 storage (general-purpose-ssd) with final snapshot creation and 7-day retention. * **Identity & Policy** — An identity bound to the workload with `reveal` access to the startup and user secrets, and cloud storage access when backup is enabled. * **Secrets** — A startup script for cluster join/initialization and an opaque secret for the database user credential. * **Backup Cron Workload** *(optional)* — A scheduled job that triggers a CockroachDB `BACKUP` SQL command to stream data directly to AWS S3 or GCS. ### Architecture CockroachDB uses the [Raft consensus protocol](https://www.cockroachlabs.com/docs/stable/architecture/replication-layer#raft) to replicate data across nodes. Each Control Plane location maps to a CockroachDB locality region, and replicas advertise their address via internal DNS (`replica-N.WORKLOAD.LOCATION.GVC.cpln.local`). With 3 or more regions and the `SURVIVE REGION FAILURE` survival goal, the cluster tolerates the complete loss of one region without impacting availability. ## Installation This template has no external prerequisites unless backup is enabled. To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} gvc: name: cockroach-gvc locations: - name: aws-us-west-2 replicas: 3 - name: aws-us-east-2 replicas: 3 - name: aws-eu-central-1 replicas: 3 image: cockroachdb/cockroach:v25.4.0 resources: cpu: 2 memory: 4Gi database: name: mydb user: myuser volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) autoscaling: enabled: false # Set to true to enable autoscaling maxCapacity: 100 # Maximum capacity in GiB when autoscaling is enabled minFreePercentage: 10 # Minimum free percentage to trigger scaling when autoscaling is enabled scalingFactor: 1.2 # Scaling factor to determine how much to scale up when autoscaling is triggered internal_access: type: same-gvc # options: same-gvc, same-org, workload-list; used for CockroachDB when pgbouncer is disabled workloads: # Note: can only be used if type is workload-list #- //gvc/GVC_NAME/workload/WORKLOAD_NAME pgbouncer: enabled: true image: edoburu/pgbouncer:v1.25.1-p0 poolMode: transaction # options: session, transaction, statement defaultPoolSize: 25 # number of real CockroachDB connections PgBouncer maintains per pod maxClientConn: 250 # maximum number of client connections PgBouncer accepts per pod maxDbConnections: 100 # hard cap on total CockroachDB connections regardless of how many PgBouncer pods are running minReplicas: 2 maxReplicas: 4 serverCheckDelay: 30 # seconds between idle server connection health checks serverConnectTimeout: 2 # seconds before giving up on a new server connection serverLoginRetry: 0 # seconds before retrying a failed server login; 0 = no caching of failures clientLoginTimeout: 10 # seconds before rejecting a client waiting for login queryWaitTimeout: 10 # seconds before rejecting a logged-in client waiting for a server connection internal_access: type: same-gvc # options: same-gvc, same-org, workload-list; controls who can connect to PgBouncer workloads: # Note: can only be used if type is workload-list #- //gvc/GVC_NAME/workload/WORKLOAD_NAME resources: cpu: 200m minCpu: 100m memory: 1Gi minMemory: 128Mi backup: enabled: false image: controlplanecorporation/cockroach-backup:1.0 schedule: "0 2 * * *" activeDeadlineSeconds: 14400 # hard kill after 4 hours if backup hangs location: aws-us-east-2 # run the backup job in the same region as your storage bucket resources: cpu: 500m memory: 512Mi provider: aws # options: aws, gcp aws: bucket: my-backup-bucket region: us-east-1 cloudAccountName: my-backup-cloudaccount policyName: my-backup-policy prefix: cockroach/backups gcp: bucket: my-backup-bucket cloudAccountName: my-backup-cloudaccount prefix: cockroach/backups ``` ### Locations and Replicas Configure the `gvc.locations` section to control which regions the cluster spans and how many replicas run in each. While CockroachDB can run on 1 location, a minimum of 3 locations with 3 replicas per location is recommended. This is the minimum required for CockroachDB to survive a full region failure. Setting a location's `replicas` to `0` suspends the workload in that location without removing it from the configuration. ### Database Initialization The `database` section specifies a database and user to create automatically when the cluster first initializes: ```yaml theme={null} database: name: mydb user: myuser ``` The created user is granted full access to the specified database. These values are only applied on the first initialization. If the cluster has already been initialized, they are skipped on restart or upgrade. To change credentials or the database name on an existing cluster, use CockroachDB's native commands (e.g. `ALTER USER`, `RENAME DATABASE`). ### Resources and Storage * `resources.cpu` and `resources.memory` set the CPU and memory allocated to each CockroachDB replica. * `volumeset.capacity` sets the initial persistent volume size in GiB (minimum 10). * `volumeset.autoscaling.enabled` — Enable automatic volume expansion as data grows. * `volumeset.autoscaling.maxCapacity` — Maximum volume size in GiB. * `volumeset.autoscaling.minFreePercentage` — Triggers a scale-up when free space falls below this percentage. * `volumeset.autoscaling.scalingFactor` — Multiplier applied to the current capacity on each scale-up. ### Internal Access The `internal_access` section controls which workloads can reach CockroachDB directly. When PgBouncer is enabled, `internal_access` only applies to direct CockroachDB connections — use `pgbouncer.internal_access` to control who can connect through PgBouncer. | Type | Description | | --------------- | --------------------------------------------------------------- | | `same-gvc` | Allow access from all workloads in the same GVC | | `same-org` | Allow access from all workloads in the same organization | | `workload-list` | Allow access only from specific workloads listed in `workloads` | When using `workload-list`, specify each workload using its full link format: ```yaml theme={null} internal_access: type: workload-list workloads: - //gvc/GVC_NAME/workload/WORKLOAD_NAME ``` ### PgBouncer Connection Pooling PgBouncer is enabled by default. It multiplexes application connections into a smaller pool of real database connections, reducing overhead and protecting CockroachDB from connection exhaustion under high concurrency. It connects to all CockroachDB nodes across all locations, so failover and load distribution are handled transparently. When enabled, PgBouncer becomes the primary connection endpoint — connect to it instead of CockroachDB directly: ``` RELEASE_NAME-pgbouncer.GVC_NAME.cpln.local:5432 ``` **Pool modes:** | Mode | Behavior | | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `transaction` | Connection held only for the duration of a transaction. Best for most web and API workloads. Not compatible with `SET` variables, temporary tables, or advisory locks. | | `session` | Connection held for the entire client session. Compatible with all features but provides less connection reuse. | | `statement` | Connection returned after every statement. Transactions are not supported. Rarely used. | * `pgbouncer.defaultPoolSize` — Real CockroachDB connections PgBouncer maintains per pod. * `pgbouncer.maxClientConn` — Maximum client connections PgBouncer accepts per pod. * `pgbouncer.maxDbConnections` — Hard cap on total CockroachDB connections shared across all PgBouncer pods. Set this to a value your cluster can safely handle regardless of how many pods are running. * `pgbouncer.minReplicas` / `pgbouncer.maxReplicas` — PgBouncer autoscales on RPS between these bounds. * `pgbouncer.internal_access` — Controls which workloads can connect to PgBouncer (same options as `internal_access`). ### Connecting to CockroachDB When PgBouncer is enabled (default), connect through it on port 5432: ```bash theme={null} psql "host=RELEASE_NAME-pgbouncer.GVC_NAME.cpln.local port=5432 dbname=mydb user=myuser sslmode=disable" ``` When PgBouncer is disabled, connect directly to CockroachDB on port 26257: ```bash theme={null} cockroach sql --insecure --host=RELEASE_NAME-cockroach.GVC_NAME.cpln.local:26257 ``` This template deploys CockroachDB in insecure mode (no TLS). It is intended for internal workloads that connect through Control Plane's internal network, where mTLS is handled by Control Plane. The DB Console (HTTP UI) runs on port 8080 for monitoring cluster health, query performance, and node status. It is not exposed externally — access it via port forward and open `http://localhost:8080` in your browser. ### Application Retry Logic Your application must implement retry logic on database connections. PgBouncer routes around failed CockroachDB nodes, but transient errors are still surfaced to the application during failover events — such as a location outage or rolling restarts — while PgBouncer cycles through backends and Raft leader elections complete. Without retries, these transient errors will propagate directly to the client. This template creates a GVC with a default name defined in the values file. If you plan to deploy multiple instances, you **must assign a unique GVC name** for each deployment. ## Backup Backup is disabled by default. When enabled, a cron workload triggers a CockroachDB `BACKUP` SQL command on the configured schedule. CockroachDB nodes stream backup data directly to cloud storage using their own workload identity — the backup job only issues the SQL command and does not transfer data itself. * `backup.enabled` — Enable scheduled backups. * `backup.schedule` — Cron expression for backup frequency (default: daily at 2am UTC). * `backup.provider` — `aws` or `gcp`. * `backup.location` — The Control Plane location where the backup cron job runs. **Set this to the region closest to your storage bucket** to minimize cross-region transfer latency. * `backup.activeDeadlineSeconds` — Hard timeout for the backup job in seconds (default: `14400` / 4 hours). The job is killed if it exceeds this limit. * `backup.resources.cpu` / `backup.resources.memory` — Resources for the backup cron container. ### AWS S3 Before enabling backup with `provider: aws`, complete the following in your AWS account: 1. Create an S3 bucket. Set `backup.aws.bucket` to its name and `backup.aws.region` to its region. 2. If you do not have a Cloud Account set up, refer to the docs to [Create a Cloud Account](/guides/create-cloud-account). Set `backup.aws.cloudAccountName` to its name. 3. Create an IAM policy with the following JSON, replacing `YOUR_BUCKET_NAME`: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket", "s3:GetObjectVersion", "s3:DeleteObjectVersion" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET_NAME", "arn:aws:s3:::YOUR_BUCKET_NAME/*" ] } ] } ``` 4. Set `backup.aws.policyName` to the name of the policy created in step 3. 5. Set `backup.aws.prefix` to the folder path where backups will be stored. ### GCS Before enabling backup with `provider: gcp`, complete the following in your GCP account: 1. Create a GCS bucket. Set `backup.gcp.bucket` to its name. 2. If you do not have a Cloud Account set up, refer to the docs to [Create a Cloud Account](/guides/create-cloud-account). Set `backup.gcp.cloudAccountName` to its name. 3. Add the **Storage Admin** role to the GCP service account associated with the Cloud Account. 4. Set `backup.gcp.prefix` to the folder path where backups will be stored. ## Restoring a Backup Backups are stored at `BUCKET/PREFIX/`. Run `cockroach sql` from a machine with network access to the cluster. **AWS S3** ```sh theme={null} cockroach sql --insecure \ --host="RELEASE_NAME-cockroach.GVC_NAME.cpln.local:26257" \ --execute="RESTORE FROM LATEST IN 's3://BUCKET_NAME/PREFIX?AUTH=implicit&AWS_REGION=BUCKET_REGION';" ``` **GCS** ```sh theme={null} cockroach sql --insecure \ --host="RELEASE_NAME-cockroach.GVC_NAME.cpln.local:26257" \ --execute="RESTORE FROM LATEST IN 'gs://BUCKET_NAME/PREFIX?AUTH=implicit';" ``` ## External References Official CockroachDB documentation Learn about multi-region deployments Configure region failure survival goals Source code for the CockroachDB backup container image View the source files, default values, and chart definition # Coraza WAF Source: https://docs.controlplane.com/template-catalog/templates/coraza Deploy Coraza WAF on Control Plane using the Template Catalog. Covers configuration, OWASP CRS rules, and reverse proxy setup for filtering traffic to target workloads. ## Overview Coraza is an open-source Web Application Firewall (WAF) that integrates the OWASP Core Rule Set (CRS) for comprehensive protection against common web attacks. This template uses the [Coraza Caddy](https://github.com/coreruleset/coraza-crs-docker) image, which runs Coraza as a plugin inside the Caddy web server to handle request inspection and proxying. It sits in front of a target workload, filtering all incoming requests before forwarding them. ### What Gets Created * **Workload** — A Coraza WAF container running with OWASP CRS, configured as a reverse proxy to your target workload. * **Secrets** — A startup script secret that configures the Caddy reverse proxy, and a custom rules secret (suffixed `-coraza-custom-rules`) containing an initial example rule, editable after installation to define your own WAF policies. * **Identity & Policy** — An identity bound to the workload with `reveal` access to the startup and custom rules secrets. This template does not create a GVC. You must deploy it into an existing GVC alongside your target workload. ## Prerequisites The target workload must be reachable from the Coraza WAF workload. Before installing, ensure the target workload's internal access is configured to allow traffic from the WAF: * Set the target workload's `internal_access` to `same-gvc`, `same-org`, or use `workload-list` to explicitly allow the Coraza workload. ## Installation To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} image: ghcr.io/coreruleset/coraza-crs@sha256:eed7280e0de4820507b500b1ee10de820c175165d5cce329609bf34f32977af8 # MUST BE CHANGED targetWorkload: my-workload.my-gvc.cpln.local # Internal name of the workload to proxy traffic to targetPort: 8080 # Port of the target workload WAFPort: 80 # Port on the WAF workload exposed to the internet resources: cpu: 50m memory: 128Mi multiZone: false diskBodyInspection: true # When true, request bodies exceeding the in-memory limit are buffered to disk for inspection ``` ### Target Workload * `targetWorkload` — The internal DNS name of the workload to proxy traffic to. Uses the format `WORKLOAD_NAME.GVC_NAME.cpln.local`. **This must be changed before deploying.** * `targetPort` — The port on the target workload that Coraza should forward requests to. * `WAFPort` — The port on the Coraza workload exposed to the internet (default `80`). ### Resources * `resources.cpu` / `resources.memory` — CPU and memory allocated to the WAF workload. * `multiZone` — When `true`, deploys replicas across multiple zones for higher availability. Not all locations support multi-zone deployments. Confirm that your target location supports multi-zone before enabling this option. ### Body Inspection * `diskBodyInspection` — Controls how request bodies larger than the 512KB in-memory limit are handled: | Value | Behavior | | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | `true` (default) | Bodies exceeding 512KB are buffered to disk at `/tmp/coraza`, enabling full inspection up to 12.5MB | | `false` | All body inspection stays in memory; bodies up to 12.5MB are held in memory, avoiding disk I/O at the cost of higher memory pressure on large requests | ### Custom Rules After installation, custom WAF rules can be added by editing the secret named `-coraza-custom-rules`. The secret includes an example rule to get started: ```text theme={null} SecRule REQUEST_URI "@rx attack" "id:1001,phase:1,deny,msg:'Blocked attack attempt'" ``` After modifying the custom rules secret, you must restart the workload replicas for the changes to take effect. ### Logging All Coraza logs are sent to `/dev/stdout` and are readable in the Control Plane built-in logging interface. Logging behavior can be adjusted through environment variables in the workload configuration after installation. ## External References Official Coraza WAF documentation and tutorials Core Rule Set documentation and custom rule authoring Coraza CRS Docker image reference View the source files, default values, and chart definition # CPLN Task Runner Source: https://docs.controlplane.com/template-catalog/templates/cpln-task-runner Deploy CPLN Task Runner on Control Plane using the Template Catalog. Covers configuration, scaling, and HTTP-based task queuing with retry, scheduling, and rate limiting. ## Overview Control Plane Task Runner is a self-hosted task queue and scheduler service similar to Google Cloud Tasks. It provides HTTP-based task enqueuing with automatic retry, delayed and scheduled execution, per-client rate limiting, and multi-queue support with priority levels. ### What Gets Created * **API Workload** — HTTP endpoint for enqueuing tasks, managing clients, and health checks. Scales between 1 and 3 replicas by default. * **Worker Workload** — Background processor that picks tasks off the queue and executes them. Scales between 1 and 5 replicas by default. * **Redis with Sentinel** — A Redis instance with Redis Sentinel for high-availability task persistence and coordination. Sentinel monitors the Redis primary and handles automatic failover. * **Secret** — An opaque secret containing Redis and Sentinel passwords and the admin API key (when `createSecret` is `true`). * **Identity & Policy** — An identity bound to the workloads with `reveal` access to the task runner secrets. ### Architecture The API workload receives task submissions over HTTP and pushes them into Redis. The Worker workload polls Redis and executes tasks by making outbound HTTP requests to the configured target URLs. Both workloads connect to Redis via Sentinel for failover resilience. This template does not create a GVC. You must deploy it into an existing GVC. ## Installation This template has no external prerequisites. To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} # Same image for both API and Worker workloads image: controlplanecorporation/cpln-task-runner:0.4 # API configuration api: enabled: true replicas: min: 1 max: 3 port: 8080 public: enabled: true pathPrefix: "" # Path prefix for public endpoint (empty for root) resources: cpu: 500m memory: 512Mi env: logLevel: info # debug, info, warn, error adminApiKey: "" # Admin API key for protected endpoints (leave empty to disable) otelEndpoint: "" # OpenTelemetry endpoint (leave empty to disable tracing) connectRetries: 30 retryIntervalSec: 2 # Worker configuration worker: enabled: true replicas: min: 1 max: 5 port: 8082 # Used for health checks resources: cpu: 1 memory: 1Gi env: logLevel: info concurrency: 10 # Concurrent workers per replica taskTimeoutSec: 1800 # Task timeout (default: 30 minutes) maxRetry: 5 # Maximum retry attempts allowPrivateUrls: false # Allow tasks to target private/internal URLs cbFailureThreshold: 5 # Circuit breaker failure threshold cbTimeoutSec: 30 # Circuit breaker timeout in seconds connectRetries: 30 retryIntervalSec: 2 otelEndpoint: "" # Creates a secret for Redis + Sentinel passwords and admin API key createSecret: true secretName: task-runner-secrets # Must match names below if createSecret is true # Redis configuration redis: redisPassword: mypassword # Change before deploying to production sentinelPassword: mypassword # Change before deploying to production admin: fromSecret: name: task-runner-secrets apiKeyKey: admin-api-key redis: auth: fromSecret: enabled: true name: task-runner-secrets passwordKey: redis-password persistence: enabled: true sentinel: auth: fromSecret: enabled: true name: task-runner-secrets passwordKey: redis-sentinel-password persistence: enabled: true ``` ### API Workload * `api.enabled` — Enable or disable the API workload. * `api.replicas` — Min/max replica count for autoscaling (default: 1–3). * `api.port` — Container port (default `8080`). * `api.public.enabled` — Expose the API to the public internet. * `api.public.pathPrefix` — Optional path prefix for the public endpoint. Leave empty to serve from the root. * `api.resources` — CPU and memory allocated to each API replica. * `api.env.logLevel` — Log verbosity: `debug`, `info`, `warn`, or `error`. * `api.env.adminApiKey` — API key for admin-protected endpoints. Leave empty to disable admin authentication. * `api.env.connectRetries` / `api.env.retryIntervalSec` — Redis connection retry behavior on startup. ### Worker Workload * `worker.enabled` — Enable or disable the Worker workload. * `worker.replicas` — Min/max replica count for autoscaling (default: 1–5). * `worker.resources` — CPU and memory allocated to each Worker replica. * `worker.env.concurrency` — Number of tasks a single Worker replica can execute concurrently. * `worker.env.taskTimeoutSec` — Maximum duration in seconds before a task execution is considered failed (default `1800`). * `worker.env.maxRetry` — Maximum number of retry attempts for a failed task. * `worker.env.allowPrivateUrls` — When `true`, allows tasks to target internal/private URLs. * `worker.env.cbFailureThreshold` / `worker.env.cbTimeoutSec` — Circuit breaker settings to stop hammering failing endpoints. ### Secrets and Redis When `createSecret` is `true`, the template automatically creates a secret named by `secretName` containing the Redis password, Sentinel password, and admin API key. The `redis.*.fromSecret` fields are pre-wired to reference this secret. When using an existing secret (`createSecret: false`), update the `redis.*.fromSecret` fields to point to your secret name and the correct keys. Change the default `redis.redisPassword` and `redis.sentinelPassword` values before deploying to production. ### Enqueuing Tasks Once deployed, submit tasks to the API workload's public endpoint: ```bash theme={null} curl -X POST https://your-api-endpoint/v1/enqueue \ -H "Content-Type: application/json" \ -d '{ "client_id": "my-service", "queue": "default", "task": { "url": "https://api.example.com/webhook", "method": "POST", "headers": {"Content-Type": "application/json"}, "body": "{\"event\": \"user.created\"}" } }' ``` ### Admin Endpoints When `api.env.adminApiKey` is set, admin endpoints require the `X-Admin-Key` header: ```bash theme={null} # List clients curl https://your-api-endpoint/admin/clients \ -H "X-Admin-Key: your-admin-key" # Create or update a client curl -X POST https://your-api-endpoint/admin/clients/set \ -H "X-Admin-Key: your-admin-key" \ -H "Content-Type: application/json" \ -d '{ "client_id": "new-service", "tier": "premium", "enabled": true }' ``` ### Rate Limiting Rate limits are applied per client and configured via the admin API. Available tiers: | Tier | Requests/min | Max Concurrent | | ------------ | ------------ | -------------- | | `free` | 10 | 1 | | `basic` | 100 | 5 | | `premium` | 1,000 | 20 | | `enterprise` | 5,000 | 50 | ### OpenTelemetry To enable distributed tracing, set `otelEndpoint` in both `api.env` and `worker.env`. In your GVC configuration, ensure the **Tracing Provider** is set to **Control Plane**, then use the default HTTP collector endpoint: ```text theme={null} tracing.controlplane:4318 ``` ## External References OpenTelemetry distributed tracing documentation View the source files, default values, and chart definition # CPLN Trivy Source: https://docs.controlplane.com/template-catalog/templates/cpln-trivy Automated vulnerability scanning for images stored in a Control Plane image registry. Scans each image using Trivy, stores HTML reports in S3 or Azure File Share, and tags images with a direct link to their report. ## Overview CPLN Trivy automates vulnerability scanning for every image in your Control Plane image registry. A scheduled daemon queries the registry for unscanned images, runs Trivy against each one, and stores an HTML report in S3 or an Azure File Share. After each scan, the image is tagged with a direct link to its report — visible in the Control Plane console. ### Architecture * **daemon** (cron workload) — Runs on a configurable schedule, queries the registry for images that do not yet have a `cpln/trivy-scan` tag, and orchestrates scanning. Includes a **trivy-api** sidecar that wraps the Trivy CLI and returns HTML vulnerability reports. * **web-server** (serverless workload) — Receives scan reports from the daemon, stores them in the configured storage backend, and serves them publicly via URL. After each scan, the daemon writes two tags to the image: | Tag | Value | | ---------------------- | ------------------------------------ | | `cpln/trivy-scan` | URL to the HTML vulnerability report | | `cpln/trivy-scan-time` | Timestamp of the scan | Each run scans images that do not yet have a `cpln/trivy-scan` tag. When `rescanAfter` is set (default `7d`), images whose last scan is older than that window are scanned again and their report is refreshed in place at the same URL. Setting `rescanAfter` to `""` disables rescanning — then re-scanning an image requires removing its `cpln/trivy-scan` tag first. ### What Gets Created * **Cron Daemon Workload** — Trivy daemon with trivy-api sidecar, runs on a cron schedule. * **Serverless Web-Server Workload** — Report storage and serving, autoscales from 1–3 replicas. * **Identity & Policy** — Identity bound to each workload with access to the configured storage cloud account, plus `reveal` access to the referenced credentials secret. * **Secret** — CPLN secret storing the shared bearer token between the daemon and web-server. The service account key itself lives in an opaque secret you create beforehand (see Prerequisites). This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites ### Service Account Trivy authenticates against the Control Plane image registry using a service account key stored in an **opaque** secret. Create a Control Plane service account (or use an existing one). Set `serviceAccountName` in `values.yaml` to its name — the template grants it image `pull` and `view` permissions automatically. Generate a key for the service account and copy the key value. It cannot be retrieved later. ```bash theme={null} echo -n "your-service-account-key" | cpln secret create-opaque --name trivy-credentials --encoding plain -f - ``` The template grants the workload identity `reveal` access automatically: ```yaml theme={null} trivyAuth: secretName: trivy-credentials ``` ### Storage Choose a storage backend — either AWS S3 or Azure File Share. Set `storage.type` to the appropriate value and configure only that section. Create an S3 bucket in your AWS account to store scan reports. Set `storage.s3.bucket` and `storage.s3.region`. If you do not have one, [create an AWS Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) in Control Plane. Set `storage.s3.cloudAccountName` to its name. Create an IAM policy scoped to your bucket (replace `YOUR_BUCKET_NAME`) and set `storage.s3.policyName` to its name: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET_NAME", "arn:aws:s3:::YOUR_BUCKET_NAME/*" ] } ] } ``` Create an Azure storage account and file share. Set `storage.azureFileshare.accountName` and `storage.azureFileshare.fileShare`. If you do not have one, [create an Azure Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) in Control Plane. Set `storage.azureFileshare.cloudAccountName` to its name. Set `storage.azureFileshare.scope` to the full Azure resource scope for role assignment: `/subscriptions//resourceGroups//providers/Microsoft.Storage/storageAccounts/` To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} # Storage backend for vulnerability reports. Options: "s3" or "azureFileshare" storage: type: s3 s3: cloudAccountName: my-aws-cloud-account bucket: trivy-reports-bucket region: us-east-1 policyName: my-trivy-s3-policy # IAM policy scoped to the bucket (see Prerequisites) azureFileshare: cloudAccountName: my-azure-cloud-account accountName: mystorageaccount fileShare: trivy-reports scope: "" # /subscriptions//resourceGroups//providers/Microsoft.Storage/storageAccounts/ # Bearer token shared between the daemon and web-server. Change before deploying to production. postToken: changeme # Authentication for Trivy to pull images from the Control Plane image registry. # Name of a pre-existing CPLN opaque secret whose payload is the service account key (see Prerequisites). trivyAuth: secretName: trivy-credentials # Control Plane service account that Trivy uses to pull images serviceAccountName: trivy-service-account # Cron schedule for the scanning daemon schedule: "*/59 * * * *" # Rescan images whose last scan is older than this (e.g. "7d", "24h"). Empty = scan once only. rescanAfter: 7d daemon: image: ghcr.io/controlplane-com/cpln-trivy-daemon:1.2.0 resources: cpu: 1 memory: 1Gi firewall: outboundAllowCIDR: - 0.0.0.0/0 trivyApi: image: ghcr.io/controlplane-com/cpln-trivy-trivy-api:1.2.0 resources: cpu: 2 memory: 4Gi webServer: image: ghcr.io/controlplane-com/cpln-trivy-web-server:1.2.0 resources: cpu: 150m memory: 128Mi autoscaling: minScale: 1 maxScale: 3 firewall: inboundAllowCIDR: - 0.0.0.0/0 outboundAllowCIDR: - 0.0.0.0/0 ``` ### Configuration Reference | Parameter | Default | Description | | ----------------------------------------- | ----------------------- | ----------------------------------------------------------------------------------------------------- | | `storage.type` | `s3` | Storage backend for reports. Options: `s3`, `azureFileshare` | | `storage.s3.cloudAccountName` | — | AWS Cloud Account name registered in Control Plane | | `storage.s3.bucket` | — | S3 bucket name | | `storage.s3.region` | — | AWS region (e.g. `us-east-1`) | | `storage.s3.policyName` | — | Name of the IAM policy scoped to the bucket | | `storage.azureFileshare.cloudAccountName` | — | Azure Cloud Account name registered in Control Plane | | `storage.azureFileshare.accountName` | — | Azure storage account name | | `storage.azureFileshare.fileShare` | — | Azure file share name | | `storage.azureFileshare.scope` | — | Full Azure resource scope for role assignment | | `postToken` | `changeme` | Shared bearer token between daemon and web-server | | `trivyAuth.secretName` | `trivy-credentials` | Name of an existing opaque secret whose payload is the service account key | | `serviceAccountName` | `trivy-service-account` | Service account Trivy uses to pull images | | `schedule` | `*/59 * * * *` | Cron schedule for the scanning daemon | | `rescanAfter` | `7d` | Rescan images whose last scan is older than this (`d` or `h`). Empty string disables rescanning | | `daemon.resources.cpu` | `1` | CPU for the daemon container | | `daemon.resources.memory` | `1Gi` | Memory for the daemon container | | `trivyApi.resources.cpu` | `2` | CPU for the trivy-api sidecar | | `trivyApi.resources.memory` | `4Gi` | Memory for the trivy-api sidecar | | `webServer.autoscaling.minScale` | `1` | Minimum web-server replicas | | `webServer.autoscaling.maxScale` | `3` | Maximum web-server replicas | Change `postToken` from its default `changeme` value before any production deployment. This token authenticates report submissions from the daemon to the web-server. Report URLs are publicly accessible by default — they contain an unguessable SHA-256 hash, but no authentication. Restrict `webServer.firewall.inboundAllowCIDR` if reports must stay private. ## Viewing Reports Once the daemon has run, navigate to any scanned image in the Control Plane console. The `cpln/trivy-scan` tag on the image contains a direct URL to the HTML vulnerability report. Opening that URL serves the report from the web-server. To list all scanned images via CLI: ```bash theme={null} cpln image query --tag cpln/trivy-scan --max -1 -o json | jq '.items[].name' ``` ## Maintenance ### Periodic Re-Scans With `rescanAfter` set (default `7d`), re-scans happen automatically: any image whose last scan is older than the window is scanned again on the next daemon run, and its report is refreshed at the same URL. Adjust the window (e.g. `24h` for daily) or set it to `""` to scan each image only once. ### Force an Immediate Re-Scan To re-scan an image before its `rescanAfter` window elapses, remove its scan tags: ```bash theme={null} cpln image tag my-image:latest --remove cpln/trivy-scan --remove cpln/trivy-scan-time ``` To reset all scan tags and trigger a full re-scan on the next daemon run: ```bash theme={null} cpln image query --tag cpln/trivy-scan --max -1 -o json | jq -r '.items[].name' | \ xargs -I{} cpln image tag {} --remove cpln/trivy-scan --remove cpln/trivy-scan-time ``` The daemon will pick the images up on its next scheduled run. ## External References Official Trivy vulnerability scanner documentation Set up AWS or Azure cloud accounts for storage access # DBeaver Source: https://docs.controlplane.com/template-catalog/templates/dbeaver Deploy DBeaver on Control Plane using the Template Catalog. Covers configuration, volumes, scaling, and web-based database administration with CloudBeaver. ## Overview DBeaver is a web-based database administration tool that provides a modern interface for managing multiple database connections. This template deploys the self-hosted [CloudBeaver](https://github.com/dbeaver/cloudbeaver) web application — the browser-based edition of DBeaver — with automatic admin user creation, giving you immediate access to a full-featured SQL editor, connection manager, and data browser for a wide range of database systems including PostgreSQL, MySQL, MariaDB, MongoDB, Redis, SQLite, Oracle, SQL Server, and more. ### What Gets Created * **Workload** — A DBeaver web application container with persistent storage and an admin user pre-configured on startup. * **Volume Set** — Persistent storage for workspace data, connections, and settings. * **Secret** — An opaque secret storing the admin username and password, injected into the container at startup. * **Identity & Policy** — An identity bound to the workload with `reveal` access to the admin credentials secret. This template does not create a GVC. You must deploy it into an existing GVC. ## Installation This template has no external prerequisites. To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} image: dbeaver/cloudbeaver:25.2.0 admin: name: adminusername password: Password123 resources: cpu: 500m memory: 512Mi volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) ``` ### Admin Credentials * `admin.name` — Username for the admin account created on first startup. * `admin.password` — Password for the admin account. **Change this before deploying.** These values are stored as a Control Plane secret and injected into the container at startup. No manual user creation is required — log in with these credentials immediately after deployment. ### Resources and Storage * `resources.cpu` / `resources.memory` — CPU and memory allocated to the CloudBeaver workload. * `volumeset.capacity` — Persistent volume size in GiB for workspace data, connections, and settings (minimum 10). ### Accessing CloudBeaver Once deployed, open the workload's endpoint in your browser and log in with the admin credentials configured in your values file. ## External References Official DBeaver CloudBeaver documentation CloudBeaver open-source repository View the source files, default values, and chart definition # Debezium Server Source: https://docs.controlplane.com/template-catalog/templates/debezium-server Deploy Debezium Server on Control Plane for standalone Change Data Capture (CDC). Streams database changes from PostgreSQL, MySQL, MongoDB, SQL Server, or Oracle to Kafka, Redis, NATS, HTTP, AWS Kinesis, GCP Pub/Sub, Apache Pulsar, or Azure Event Hubs. ## Overview Debezium Server is a standalone Change Data Capture (CDC) application that tails a database's transaction log and streams row-level change events to a messaging system. Unlike Debezium connectors that run inside Kafka Connect, Debezium Server runs as a self-contained process — no Kafka Connect cluster required. This template deploys Debezium Server on Control Plane with configurable source connectors, multiple sink options, flexible offset storage, and Universal Cloud Identity integration for AWS and GCP sinks. ### Architecture * **Debezium workload** — Reads the source database's replication stream (WAL for PostgreSQL, binlog for MySQL, oplog for MongoDB), converts each change to a structured event, and delivers it to the configured sink. Stateful when using file-based offset storage; stateless when using Redis or JDBC offset storage. * **pgdog.toml / application.properties** — Rendered as a secret and mounted at startup. Defines the source, sink, serialization format, and offset storage settings. ### What Gets Created * **Standard Debezium Server Workload** — Single-replica CDC workload. * **Volume Set** — For offset and schema history persistence *(only when using file offset storage)*. * **Identity & Policy** — Identity with access to credential secrets, and cloud account access when using Kinesis or Pub/Sub sinks. * **Secrets** — Three opaque secrets: Debezium configuration (`application.properties`), credentials (passwords for source, sink, offset storage), and a startup entrypoint script. This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites ### Source Database Configure your source database to allow Debezium to read its replication stream before deploying. Add the following to `postgresql.conf` and restart PostgreSQL: ``` wal_level = logical max_replication_slots = 4 max_wal_senders = 4 ``` When using the [PostgreSQL Highly Available](/template-catalog/templates/postgres-highly-available) template, set `postgres.walLevel: logical` in its values before installing. ```sql theme={null} CREATE PUBLICATION dbz_publication FOR ALL TABLES; ``` Debezium creates the replication slot automatically on first connect. ```sql theme={null} GRANT USAGE ON SCHEMA public TO debezium; GRANT SELECT ON ALL TABLES IN SCHEMA public TO debezium; ALTER USER debezium REPLICATION; ``` Add the following to `my.cnf` and restart MySQL: ``` server-id = 1 log_bin = mysql-bin binlog_format = ROW binlog_row_image = FULL ``` ```sql theme={null} GRANT SELECT, RELOAD, SHOW DATABASES, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'debezium'@'%'; ``` MongoDB must be running as a replica set. Debezium tails the oplog; no additional configuration is needed beyond connection credentials with `read` access to the databases you want to capture. CDC must be enabled on the SQL Server instance and on each database/table you want to capture: ```sql theme={null} EXEC sys.sp_cdc_enable_db; EXEC sys.sp_cdc_enable_table @source_schema = 'dbo', @source_name = 'my_table', @role_name = NULL; ``` ### Cloud Sinks (Kinesis / Pub/Sub) For AWS Kinesis or GCP Pub/Sub sinks, set up a Cloud Account in Control Plane before installing: * **Kinesis**: [Create an AWS Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) and enable it under `sink.kinesis.cloudAccount`. * **Pub/Sub**: [Create a GCP Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) and enable it under `sink.pubsub.cloudAccount`. To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration ### Quick Start Examples **PostgreSQL → Kafka:** ```yaml theme={null} source: type: postgres database: hostname: my-postgres.my-gvc.cpln.local port: 5432 name: mydb user: debezium password: secret serverName: myserver tableIncludeList: "public.users,public.orders" postgres: slotName: debezium_slot publicationName: dbz_publication sink: type: kafka kafka: bootstrapServers: kafka.my-gvc.cpln.local:9092 topic: cdc-events ``` **MySQL → Redis Streams:** ```yaml theme={null} source: type: mysql database: hostname: mysql.my-gvc.cpln.local port: 3306 name: mydb user: debezium password: secret serverName: myserver mysql: serverId: 85744 includeSchemaChanges: true sink: type: redis redis: address: redis.my-gvc.cpln.local:6379 streamName: cdc-stream ``` **PostgreSQL → AWS Kinesis (Universal Cloud Identity):** ```yaml theme={null} source: type: postgres database: hostname: my-rds.us-east-1.rds.amazonaws.com port: 5432 name: mydb user: debezium password: secret serverName: myserver sink: type: kinesis kinesis: region: us-east-1 streamName: cdc-events credentialsProvider: default cloudAccount: enabled: true name: my-aws-account ``` ### Full `values.yaml` ```yaml theme={null} image: quay.io/debezium/server:3.0 resources: cpu: 500m memory: 512Mi source: type: postgres # options: postgres, mysql, mongodb, sqlserver, oracle database: hostname: "" port: 5432 name: "" user: "" password: "" serverName: "dbserver1" tableIncludeList: "" tableExcludeList: "" postgres: slotName: "debezium" publicationName: "dbz_publication" pluginName: "pgoutput" # options: pgoutput, decoderbufs slotDropOnStop: false heartbeatIntervalMs: 0 # set to 5000 for HA/patroni setups heartbeatActionQuery: "" mysql: serverId: 85744 includeSchemaChanges: true mongodb: connectionString: "" replicaSet: "" sqlserver: databaseNames: "" snapshotMode: "initial" # options: initial, schema_only, initial_only oracle: pdbName: "" logMiningStrategy: "online_catalog" offset: storage: file # options: file, redis, jdbc flushIntervalMs: 10000 flushTimeoutMs: 60000 file: filename: "/debezium/data/offsets.dat" redis: address: "" key: "debezium:offsets" password: "" ssl: false jdbc: url: "" user: "" password: "" tableName: "debezium_offsets" schemaHistory: # MySQL and SQL Server only storage: file # options: file, redis, jdbc file: filename: "/debezium/data/schema-history.dat" redis: address: "" key: "debezium:schema-history" password: "" ssl: false jdbc: url: "" user: "" password: "" tableName: "debezium_schema_history" errors: retryDelayInitialMs: 300 retryDelayMaxMs: 10000 maxRetries: -1 # -1 = infinite retries sink: type: kafka # options: kafka, redis, nats-jetstream, http, kinesis, pubsub, pulsar, eventhubs kafka: bootstrapServers: "" topic: "" securityProtocol: "PLAINTEXT" saslMechanism: "" saslUsername: "" saslPassword: "" redis: address: "" password: "" ssl: false streamName: "" nats: url: "" subject: "" username: "" password: "" http: url: "" headers: {} authType: "" # options: none, basic, bearer username: "" password: "" bearerToken: "" kinesis: region: "" streamName: "" credentialsProvider: "default" cloudAccount: enabled: false name: "" pubsub: projectId: "" topic: "" cloudAccount: enabled: false name: "" pulsar: serviceUrl: "" topic: "" authPluginClassName: "" authToken: "" eventhubs: connectionString: "" hubName: "" format: key: json # options: json, avro, protobuf value: json schemaRegistry: url: "" username: "" password: "" volumeset: capacity: 10 # GiB — only used with file offset storage performanceClass: general-purpose-ssd firewall: internal: inboundAllowType: same-gvc # options: none, same-gvc, same-org, workload-list workloads: [] external: outboundAllowCIDR: - 0.0.0.0/0 ``` ### Supported Sources | Database | Default Port | Key Settings | | ---------- | ------------ | ------------------------------------------- | | PostgreSQL | `5432` | `slotName`, `publicationName`, `pluginName` | | MySQL | `3306` | `serverId`, `includeSchemaChanges` | | MongoDB | `27017` | `connectionString`, `replicaSet` | | SQL Server | `1433` | `databaseNames`, `snapshotMode` | | Oracle | `1521` | `pdbName`, `logMiningStrategy` | ### Supported Sinks | Sink | Key Settings | Notes | | ---------------- | ----------------------------- | ----------------------------- | | Kafka | `bootstrapServers`, `topic` | No Kafka Connect required | | Redis | `address`, `streamName` | Redis Streams | | NATS JetStream | `url`, `subject` | Cloud-native messaging | | HTTP | `url` | Webhooks and custom endpoints | | AWS Kinesis | `region`, `streamName` | Uses Universal Cloud Identity | | GCP Pub/Sub | `projectId`, `topic` | Uses Universal Cloud Identity | | Apache Pulsar | `serviceUrl`, `topic` | Optional token auth | | Azure Event Hubs | `connectionString`, `hubName` | Kafka-compatible protocol | ### Offset Storage Debezium tracks its position in the source database's replication stream using offset storage. If the process restarts, it resumes from the last committed offset. | Backend | Persistence | Notes | | ------- | -------------------------------------- | --------------------------------------------------------------------- | | `file` | Volume Set mounted at `/debezium/data` | Default. Simple, requires a Volume Set. | | `redis` | Redis key | No Volume Set needed. Use with an existing Redis instance. | | `jdbc` | Relational table | No Volume Set needed. Use with an existing PostgreSQL/MySQL instance. | ### Schema History (MySQL and SQL Server only) MySQL and SQL Server connectors track DDL changes in a schema history store. Configure `source.schemaHistory.storage` with the same backend options as offset storage (`file`, `redis`, or `jdbc`). ### Serialization Formats Events can be serialized as JSON, Avro, or Protobuf. For Avro or Protobuf, configure a Schema Registry: ```yaml theme={null} format: key: avro value: avro schemaRegistry: url: http://schema-registry.my-gvc.cpln.local:8081 username: "" password: "" ``` ### Universal Cloud Identity AWS Kinesis and GCP Pub/Sub sinks support credential-free access via Control Plane's Universal Cloud Identity — no access keys or service account JSON files are needed. Enable by setting `cloudAccount.enabled: true` and providing the Cloud Account name registered in Control Plane: ```yaml theme={null} sink: type: kinesis kinesis: region: us-east-1 streamName: my-stream credentialsProvider: default cloudAccount: enabled: true name: my-aws-account ``` ## Health Checks Debezium Server exposes Quarkus health endpoints: | Endpoint | Purpose | | ----------------- | ------------------------------------------ | | `/q/health/ready` | Connector is ready and connected to source | | `/q/health/live` | Server process is alive | ## Troubleshooting * Verify database hostname and port are reachable from the GVC (check `firewall.external.outboundAllowCIDR`). * Confirm the database user has the required replication permissions. * View startup logs: `cpln workload logs {release-name}-debezium --gvc {gvc}` * **File storage**: confirm the Volume Set is mounted and has sufficient capacity. * **Redis/JDBC**: verify connectivity, credentials, and that the backend is accessible within the GVC. * Verify the sink endpoint is reachable from the workload. * For Kinesis/Pub/Sub: confirm the Cloud Account is configured and has the correct IAM/IAM permissions. * For Kafka with SASL: verify `saslUsername`, `saslPassword`, and `saslMechanism` match the broker config. ## External References Full Debezium Server configuration reference Source connector configuration for each supported database Deploy a complete CDC pipeline (PostgreSQL HA + Kafka + Debezium) in one step Set up AWS or GCP cloud accounts for Kinesis and Pub/Sub sinks # Docmost Source: https://docs.controlplane.com/template-catalog/templates/docmost Deploy Docmost on Control Plane using the Template Catalog. Open-source knowledge base and wiki with real-time collaborative editing, spaces, and permissions, backed by PostgreSQL and Redis with local or S3 attachment storage. ## Overview Docmost is an open-source knowledge base and wiki — a Confluence or Notion alternative with real-time collaborative editing, spaces, and per-space permissions. This template deploys the Docmost server — UI, REST API, and collaboration websockets on port `3000` — backed by a PostgreSQL instance for documents and a bundled Redis for queues and realtime coordination, with attachments stored either on a persistent volume or in an S3 bucket. ### Architecture * **Docmost server** — A stateful workload running the single official image, which serves the web UI, the REST API, and the Yjs collaboration websocket on port `3000`. It runs its database migrations automatically on boot and scales horizontally through `docmost.replicas`. * **PostgreSQL** — The [postgres](/template-catalog/templates/postgres) template as a subchart. It holds every page, user, space, and permission. * **Redis** — A bundled single-node Redis with AOF persistence, deployed by this chart. It carries the background job queues, the socket.io adapter, and cross-replica collaboration sync. Docmost requires it: the server does not report healthy without a reachable Redis. * **Attachment storage** — Either a persistent volume mounted at `/app/data/storage` (default) or an S3 bucket you own. ### What Gets Created * **Stateful Docmost Workload** — The Docmost server on port `3000` (`{release}-docmost`), with `docmost.replicas` replicas and configurable CPU and memory. * **Stateful Redis Workload** — The bundled single-node Redis (`{release}-docmost-redis`) with password authentication. * **Stateful PostgreSQL Workload** — One PostgreSQL instance from the subchart, with its own volume set. * **Volume Sets** — One for local attachments (mounted only when `storage.type` is `local`), one for the Redis AOF file, and one for the PostgreSQL data directory. * **Secret** — A template-created dictionary secret (`{release}-docmost-creds`) holding the PostgreSQL and Redis credentials used to assemble `DATABASE_URL` and `REDIS_URL`. Your prerequisite `APP_SECRET` secret is *not* created by the template — see [Prerequisites](#prerequisites). * **Identity & Policy** — An identity bound to the Docmost workload, and a policy granting it `reveal` on exactly the secrets it reads: the credentials secret, your prerequisite secret, and — when configured — the S3 key and SMTP secrets. In keyless S3 mode the identity also carries the cloud account link and your bucket-scoped IAM policy. This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites Docmost signs its sessions, invitation links, and share links with an `APP_SECRET` that you supply through an [opaque secret](/guides/create-secret/opaque) created **before** installing. The value is never passed through Helm values. The payload is a single random string of at least 32 characters: ```bash theme={null} printf '%s' "$(openssl rand -hex 32)" | \ cpln secret create-opaque --name my-docmost-app-secret --encoding plain -f - ``` Use `printf` rather than `echo` so the payload carries no trailing newline. Set `secrets.name` to the secret's name (default `my-docmost-app-secret`). Secrets are org-level, so no GVC flag is involved. Store a copy somewhere safe, outside Control Plane. Create the secret **before** installing. Installing without it succeeds, but the deployment then pauses with `The secret my-docmost-app-secret no longer exists. Workload updates are paused until the secret is added or the reference to the secret removed.` and never becomes ready. `APP_SECRET` is also write-once: rotating it logs out every user and invalidates every outstanding invitation and share link. Stored documents are unaffected. Everything else works with the defaults. Two optional features need their own setup first: * **S3 attachment storage** — an existing bucket plus either a Control Plane cloud account (AWS) or a static-key secret (S3-compatible servers). See [Attachment Storage](#attachment-storage). Required if you plan to run more than one replica. * **Authenticated SMTP** — a dictionary secret holding `SMTP_USERNAME` and `SMTP_PASSWORD`. See [Email](#email). Once your `APP_SECRET` secret exists, install the template using your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## First Run Docmost ships no default account, and the first person to reach the instance creates the workspace and its owner account. PostgreSQL and Redis come up first, then Docmost applies its schema migrations before it starts serving. A default install typically reaches ready in about a minute. Browse to the canonical `*.cpln.app` endpoint of the `{release}-docmost` workload and complete the setup form to create the workspace and the admin account. Do this as soon as the workload is ready. Member invitations are delivered only by email. Configure [SMTP](#email) before you invite your team. ## Configuration The default `values.yaml` for this template: ```yaml theme={null} docmost: image: docmost/docmost:0.95.0 replicas: 1 # >1 REQUIRES storage.type: s3; replicas coordinate via Redis appUrl: "" # public base URL used in links; empty = derive from the canonical endpoint resources: minCpu: 250m maxCpu: 1000m minMemory: 512Mi maxMemory: 2Gi # headroom for boot migrations and large imports secrets: name: my-docmost-app-secret # opaque secret holding APP_SECRET — MUST exist before install storage: type: local # local | s3 (s3 required for replicas > 1) local: volumeset: capacity: 10 # GiB (minimum 10); mounted at /app/data/storage s3: bucket: my-docmost-bucket region: us-east-1 endpoint: "" # set for S3-compatible servers (e.g. http://my-minio:9000) forcePathStyle: false # true for most S3-compatible servers (MinIO) cloudAccountName: my-s3-cloud-account # keyless AWS auth; used only when auth.secretName is empty policyName: my-docmost-s3-policy # your pre-created bucket-scoped IAM policy auth: secretName: "" # dictionary secret with AWS_S3_ACCESS_KEY_ID + AWS_S3_SECRET_ACCESS_KEY; S3-compatible servers only fileUploadSizeLimit: 50mb # max single attachment size smtp: enabled: false # off = member invites cannot be delivered host: smtp.example.com port: 587 secure: false # false = STARTTLS/plain (587); true = implicit TLS (465) fromAddress: no-reply@example.com fromName: Docmost auth: secretName: "" # optional dictionary secret with SMTP_USERNAME + SMTP_PASSWORD publicAccess: enabled: true # HTTPS UI + websockets on the auto *.cpln.app endpoint; false = internal-only internalAccess: type: same-gvc # options: none, same-gvc, same-org, workload-list workloads: [] # used only with workload-list, e.g. //gvc/GVC_NAME/workload/WORKLOAD_NAME postgres: # documents, users, spaces image: postgres:18 config: username: docmost password: change-me-docmost-pg # change before installing database: docmost resources: minCpu: 250m maxCpu: 1000m minMemory: 512Mi maxMemory: 1Gi volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) redis: # bundled single node — queues + realtime coordination (required) image: redis:8 auth: password: change-me-docmost-redis # change before installing resources: minCpu: 100m maxCpu: 400m minMemory: 256Mi maxMemory: 512Mi volumeset: capacity: 10 # GiB (minimum 10); AOF at /data ``` ### Docmost Server * `image` — The official Docmost image. Pin a released tag. * `replicas` — Number of Docmost replicas. Anything above `1` requires `storage.type: s3`; the chart fails the render with a clear message otherwise. See [Scaling](#scaling). * `appUrl` — The public base URL Docmost puts in invitation and share links, including the `https://` scheme. Leave it empty to derive it from the workload's canonical endpoint; set it when you serve Docmost on a custom domain. * `resources` — CPU and memory bounds for the Docmost container. The memory ceiling leaves headroom for the boot migrations and large imports. ### Attachment Storage `storage.type` decides where uploaded files, images, and avatars are kept. Attachments are written to a persistent volume set mounted at `/app/data/storage`, and survive restarts, redeploys, and upgrades under the same release name. ```yaml theme={null} storage: type: local local: volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) ``` Local storage works only with a single replica — each replica would get its own volume, so attachments uploaded through one replica would be missing from the others. AWS S3 uses a Control Plane cloud identity: no credentials are stored, and the workload's identity vends temporary credentials at runtime. This is the only supported way to reach AWS S3 — the chart rejects static keys unless `storage.s3.endpoint` is set. Create an S3 bucket. Set `storage.s3.bucket` and `storage.s3.region` to match. If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) for your AWS account. Set `storage.s3.cloudAccountName` to its name. Create an AWS IAM policy with the JSON below (replace `YOUR_BUCKET_NAME` with your bucket), then set `storage.s3.policyName` to the policy's name: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET_NAME", "arn:aws:s3:::YOUR_BUCKET_NAME/*" ] } ] } ``` Keep `storage.s3.auth.secretName` empty. The template then attaches the cloud account and your policy to the Docmost identity, and no access keys exist anywhere. S3-compatible servers cannot federate with a cloud account, so they use static access keys held in a dictionary secret. Create the bucket on your server. For the [MinIO](/template-catalog/templates/minio) template deployed in the same GVC, the S3 API address is `http://WORKLOAD_NAME.GVC_NAME.cpln.local:9000`. Set `storage.s3.endpoint` to the S3 API address including scheme and port, and `storage.s3.forcePathStyle: true` (required by most S3-compatible servers). Create a dictionary secret with the server's credentials and set `storage.s3.auth.secretName` to its name: ```bash theme={null} cpln secret create-dictionary --name my-docmost-s3-keys \ --entry AWS_S3_ACCESS_KEY_ID=YOUR_ACCESS_KEY \ --entry AWS_S3_SECRET_ACCESS_KEY=YOUR_SECRET_KEY ``` The Docmost identity is granted `reveal` on exactly this secret. `storage.fileUploadSizeLimit` caps the size of a single attachment (default `50mb`). An upload larger than `storage.fileUploadSizeLimit` is stored truncated at the limit rather than rejected with an error — the file appears in the page but is incomplete. Raise the limit before importing large attachments. ### Scaling `docmost.replicas` sets how many Docmost replicas run. Replicas coordinate through the bundled Redis, which carries both the job queues and the collaborative-editing updates, so a document edited on one replica appears live for a reader connected to another. ```yaml theme={null} docmost: replicas: 2 storage: type: s3 s3: bucket: my-docmost-bucket region: us-east-1 cloudAccountName: my-s3-cloud-account policyName: my-docmost-s3-policy ``` `docmost.replicas` above `1` requires `storage.type: s3`. Local attachments live on per-replica volumes, so an attachment uploaded through one replica would return a `404` from another. The chart refuses to render the combination. At two replicas, a rolling upgrade served every health check without a single failure, and killing a replica outright cost one failed request before the replacement rejoined automatically. ### Access * `publicAccess.enabled` — Serve the UI, API, and collaboration websockets on the auto-assigned `*.cpln.app` HTTPS endpoint (default). Set to `false` for an internal-only instance: external requests are then refused at the edge, and in-GVC callers still reach it per `internalAccess`. * `internalAccess.type` — Controls which workloads can reach Docmost over the internal network: | Type | Description | | --------------- | ---------------------------------------------------------------------- | | `none` | No internal access. | | `same-gvc` | Allow access from all workloads in the same GVC (default). | | `same-org` | Allow access from all workloads in the same organization. | | `workload-list` | Allow access only from workloads listed in `internalAccess.workloads`. | Flipping `publicAccess.enabled` changes the load balancer configuration, so the public endpoint may return `503` for a minute or two after the upgrade reports success. ### Email SMTP is **off by default**, and a default install works without it — the first visitor still creates the workspace and the admin account, and that account signs in with its password. ```yaml theme={null} smtp: enabled: true host: smtp.example.com port: 587 secure: false fromAddress: no-reply@example.com fromName: Docmost auth: secretName: my-docmost-smtp # dictionary secret with SMTP_USERNAME + SMTP_PASSWORD ``` For an authenticated relay, create the credentials secret before installing and reference it by name: ```bash theme={null} cpln secret create-dictionary --name my-docmost-smtp \ --entry SMTP_USERNAME=apikey --entry SMTP_PASSWORD=YOUR_PASSWORD ``` Leave `smtp.auth.secretName` empty for a relay that does not require authentication. When it is set, the Docmost identity is granted `reveal` on exactly that secret. With SMTP disabled there is no way to deliver a member invitation. The image sends no mail and writes no invitation link to the workload logs, so an invitation created in the UI never reaches its recipient. Configure `smtp.*` for any workspace with more than one member. ### Databases * `postgres.config.username` / `password` / `database` — Credentials for the bundled PostgreSQL from the [postgres](/template-catalog/templates/postgres) subchart. **Change the password before installing** — it seeds the database on first boot and is not updated by later value edits. * `postgres.resources` / `postgres.volumeset.capacity` — CPU and memory bounds and the initial data volume size in GiB (minimum 10). * `redis.auth.password` — Password for the bundled Redis, wired into Docmost's `REDIS_URL`. **Change it before installing.** Redis requires authentication: an unauthenticated client is refused. * `redis.resources` / `redis.volumeset.capacity` — CPU and memory bounds and the volume size for the Redis AOF file at `/data`. Redis is not optional. Docmost's health check reports the Redis connection alongside the database one, and the workload does not become ready without it. A Redis restart briefly fails readiness on the Docmost workload, which recovers on its own without restarting the application container. ## Connecting | What | Value | | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | | Public UI / API | `https://.cpln.app` — `status.canonicalEndpoint` of `{release}-docmost` (`cpln workload get {release}-docmost -o yaml`) | | Collaboration websocket | `wss://.cpln.app/collab` — used by the editor, no separate configuration | | Health check | `https://.cpln.app/api/health` (readiness) and `/api/health/live` (liveness) — unauthenticated | | Internal (same GVC) | `http://{release}-docmost.{gvc}.cpln.local:3000` | | Login | The admin account created on first visit | | PostgreSQL (internal) | `{release}-postgres.{gvc}.cpln.local:5432`, credentials from `postgres.config` | | Redis (internal) | `{release}-docmost-redis.{gvc}.cpln.local:6379`, password from `redis.auth.password` | ## Important Notes * **Create the `APP_SECRET` secret before installing** — `secrets.name` must point at an existing opaque secret (plain encoding) whose payload is a random string of at least 32 characters. A missing secret pauses the deployment until the secret exists, and the install looks broken until then. * **`APP_SECRET` is write-once** — rotating it logs out every user and invalidates outstanding invitation and share links. Stored documents are unaffected. Back the value up outside Control Plane. * **Claim the workspace right after install** — the first visitor to complete the setup form becomes the workspace owner. * **With SMTP off, member invitations cannot be delivered** — no mail is sent and no invitation link is written to the logs. Configure `smtp.*` before inviting anyone. * **`docmost.replicas` above `1` requires `storage.type: s3`** — local attachments are per-replica and would `404` across replicas. * **AWS S3 is keyless only** — use a cloud account plus a bucket-scoped IAM policy. Static keys are accepted only when `storage.s3.endpoint` points at an S3-compatible server. * **Uploads larger than `storage.fileUploadSizeLimit` are truncated, not rejected** — raise the limit before importing large attachments. * **Change `postgres.config.password` and `redis.auth.password` before installing** — both seed their component on first boot and are not updated by later value edits. * **Pages survive restarts and upgrades** — documents live in the PostgreSQL volume set and local attachments in the storage volume set. Uninstalling deletes those volume sets and everything in them; your `APP_SECRET` secret is yours and survives an uninstall. * **Docmost is licensed under the AGPL** — this template deploys the open-source edition. ## External References Official Docmost documentation Every setting the Docmost server reads from its environment Local and S3 attachment storage options SMTP settings for invitations and notifications Source code and release notes View the source files, default values, and chart definition # DuckDB Source: https://docs.controlplane.com/template-catalog/templates/duckdb Deploy DuckDB on Control Plane using the Template Catalog. A cron workload that runs your SQL script on a schedule and exits — a batch job runner, not a query service. Covers scheduling, object storage, attached databases, and credential handling. ## Overview DuckDB is an in-process analytical SQL engine that reads and writes Parquet, CSV and JSON directly, queries object storage over the S3 API, and can attach live PostgreSQL, MySQL and SQLite databases. This template runs a SQL script of yours on a cron schedule and exits. **This is a scheduled job runner, not a query service.** The workload is a cron job: it starts, runs your script, and terminates. It binds no port, has no endpoint, and nothing is listening after install — that is correct behavior, not a broken deployment. If you want an always-on SQL endpoint that BI tools and JDBC clients connect to, install [trino](/template-catalog/templates/trino) instead. The read path overlaps Trino: both query Parquet in object storage and join across attached databases. The difference is cost shape. Trino keeps a coordinator and workers running so a query can arrive at any moment; DuckDB here consumes nothing between runs and bills only for the minutes its job is executing. Pick this template when the work is a known transform on a known schedule, and Trino when a human or a dashboard needs to ask ad-hoc questions. ### What Gets Created * **Cron Workload** — `{release}-duckdb`, running the official `duckdb/duckdb` image once per schedule. No ports, no load balancer, `internal.inboundAllowType: none` and an empty inbound CIDR list. Outbound is open. * **Preamble Secret** — `{release}-duckdb-preamble`, an [opaque secret](/guides/create-secret/opaque) of `SET` statements mounted at `/etc/duckdb/preamble.sql` and executed before your script. * **Script Secret** *(optional)* — `{release}-duckdb-script`, holding `sql.inline`, mounted at `/etc/duckdb/job.sql`. Not created at all when you supply your own secret through `sql.secretName`. * **Identity & Policy** — `{release}-duckdb-identity` with a policy granting `reveal` on exactly the secrets this release mounts — the preamble, the script, your `secretEnv` secrets and your object-store credential secret. When `objectStore.type: aws`, the identity also carries the cloud-account binding. * **No Volume Sets** — no volume is attached. See [Memory and Storage](#memory-and-storage). This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites * **None for a default install.** The shipped `sql.inline` is a self-test that needs no credentials and no cloud account. * **Outbound access to `extensions.duckdb.org:443`** on every run. The template's firewall allows all outbound traffic, so this works out of the box unless your organization restricts egress. See [Extensions](#extensions). * **For `objectStore.type: aws`** — an AWS bucket, a Control Plane cloud account, and a bucket-scoped IAM policy. See [Object Storage](#object-storage). * **For `objectStore.type: s3-compatible`** — a reachable S3-compatible endpoint and a [dictionary secret](/guides/create-secret/dictionary) holding its access keys, created **before** install. * **For `sql.secretName`** — an [opaque secret](/guides/create-secret/opaque) containing your SQL, created **before** install. * **For `secretEnv[]`** — every referenced secret must exist **before** install. ## Installation Install the template using your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} # DuckDB runs a SQL script on a schedule and exits. It binds no port and is NOT a # query service — if you want an always-on SQL endpoint, install `trino` instead. image: duckdb/duckdb:1.5.5 # ─── Schedule ───────────────────────────────────────────────────────────────── schedule: "0 2 * * *" # cron expression in UTC — always quote it (e.g. "*/15 * * * *") suspend: false # true = never run automatically; start it with `cpln workload cron start` activeDeadlineSeconds: 3600 # a run still going is terminated within ~2 min of this # ─── SQL ────────────────────────────────────────────────────────────────────── # The script the job runs. `secretName` wins when both are set. sql: inline: | -- Default self-test. Replace with your own transform. SELECT 'duckdb-template-ok' AS status, version() AS duckdb_version, now() AS run_at; secretName: "" # opaque secret (encoding plain, payload = SQL) used INSTEAD of inline, e.g. my-duckdb-script # Environment variables sourced from Control Plane secrets, readable in your SQL # as getenv('NAME'). For an ATTACHed database name the entry after the driver's # own password variable (PGPASSWORD, MYSQL_PWD) — ATTACH takes a string literal, # so getenv() cannot be concatenated into its connection string. secretEnv: [] # secretEnv: # - name: PGPASSWORD # secretName: my-postgres-credentials # must exist BEFORE install # secretKey: password # omit for an opaque secret (uses its payload) # ─── Resources ──────────────────────────────────────────────────────────────── # DuckDB reads the HOST's RAM and core count, not the container's limits, so the # template derives memory_limit and threads from these and sets them explicitly. # The job mounts no volume, so every job must fit in memory — raise maxMemory # rather than relying on spill. maxCpu:minCpu may not exceed 4:1. resources: minCpu: 500m maxCpu: 2000m # also sets DuckDB threads: one per whole core, minimum 1 minMemory: 1Gi maxMemory: 4Gi tuning: memoryLimitPercent: 60 # DuckDB memory_limit = this percent of maxMemory (20–80) # ─── Object storage ─────────────────────────────────────────────────────────── # Registers a DuckDB S3 secret so your SQL can read and write s3:// paths. objectStore: type: none # options: none, aws, s3-compatible aws: # keyless — credentials come from the workload identity, no keys anywhere region: us-east-1 cloudAccountName: my-s3-cloud-account # must exist BEFORE install policyName: my-duckdb-bucket-policy # IAM policy scoped to your bucket (see README) s3Compatible: # SeaweedFS, MinIO, Cloudflare R2, GCS interoperability, Tigris endpoint: my-seaweedfs.my-gvc.cpln.local:8333 # host:port, no http:// prefix region: us-east-1 urlStyle: path # options: path, vhost useSsl: false credentialsSecretName: my-duckdb-s3-credentials # dictionary secret: access-key-id, secret-access-key ``` ### Schedule * `image` — The DuckDB container image. The chart is shipped and tested on DuckDB 1.5.5. * `schedule` — A five-field cron expression interpreted in **UTC**. Always quote it; the chart rejects anything that is not exactly five fields. * `suspend` — `true` installs the job without ever running it automatically. Trigger it by hand with `cpln workload cron start`. * `activeDeadlineSeconds` — Upper bound on a single run's duration. `activeDeadlineSeconds` is not a hard cut. The deadline is *detected* on time, but the container is terminated within roughly **two minutes** of it, and keeps consuming its full CPU and memory allocation for that window. Size the value with that slack in mind rather than treating it as an exact ceiling — and see [Confirming a Run Succeeded](#confirming-a-run-succeeded), because a run killed this way can still print the success marker on its way out. Runs never overlap (`concurrencyPolicy: Forbid`) and a failed run is not retried (`restartPolicy: Never`) — the next scheduled run simply starts as normal. ### SQL Script Your script comes from one of two places, and `sql.secretName` wins when both are set: | Source | When to use it | | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `sql.inline` | The default. The script lives in your values and ships with the release. | | `sql.secretName` | The script is too large for a values file, or you want to change it without a Helm upgrade. Point it at an [opaque secret](/guides/create-secret/opaque) whose payload is the SQL. | With `sql.secretName` set, the template's own script secret is not created at all and the workload mounts your secret directly: ```bash theme={null} printf '%s' "SELECT 42 AS answer;" | cpln secret create-opaque \ --name my-duckdb-script --encoding plain -f - ``` The chart refuses to render when both are empty — there would be nothing to run. Before your script, the job executes a template-generated preamble containing `.bail on` and the derived `memory_limit`, `threads`, `temp_directory` and `extension_directory` settings. Because the preamble runs first, a `SET` in your own script always wins. ### Credentials in SQL `secretEnv[]` turns Control Plane secrets into container environment variables, readable from your SQL with `getenv('NAME')`. The value never appears in your values file or in the rendered workload spec — only a `cpln://secret/...` reference does. | Field | Description | | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- | | `name` | `UPPER_SNAKE_CASE` environment variable name. Must be unique across entries. | | `secretName` | Name of the Control Plane secret. It must exist **before** install. | | `secretKey` | Key within a [dictionary secret](/guides/create-secret/dictionary). Omit it for an [opaque secret](/guides/create-secret/opaque), which uses its payload. | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_SESSION_TOKEN` and `AWS_DEFAULT_REGION` are rejected — object-store credentials are owned by `objectStore`, and setting them here would silently override it. **`getenv()` cannot be concatenated into an `ATTACH` string.** `ATTACH` takes a string *literal*, not an expression, so a connection string built with `'... password=' || getenv('PGPASSWORD')` fails to parse: ```text theme={null} Parser Error: syntax error at or near "||" ``` This applies equally to the `postgres`, `mysql` and `sqlite` attach types. Instead, name the `secretEnv` entry after the database driver's own password variable and leave the password out of the connection string entirely — the driver reads it from the environment: | Attach type | Name the `secretEnv` entry | | ----------- | -------------------------- | | `postgres` | `PGPASSWORD` | | `mysql` | `MYSQL_PWD` | ```yaml theme={null} secretEnv: - name: PGPASSWORD secretName: my-postgres-credentials # must exist BEFORE install secretKey: password # omit for an opaque secret (uses its payload) ``` ```sql theme={null} ATTACH 'dbname=postgres user=postgres host=my-postgres.my-gvc.cpln.local' AS pg (TYPE postgres, READ_ONLY); COPY (SELECT * FROM pg.public.events) TO 's3://my-bucket/events.parquet' (FORMAT parquet); ``` `getenv('NAME')` still works anywhere an ordinary expression is allowed — a `WHERE` clause, a computed column, a `COPY` destination built with `||`. The restriction is specific to `ATTACH`. If a credential truly must sit inline, put the whole connection string into a `sql.secretName` secret instead. Templates deployed in the same GVC are reachable at `{workload-name}.{gvc}.cpln.local` — for example [postgres](/template-catalog/templates/postgres) on `5432` or [mysql](/template-catalog/templates/mysql) on `3306`. ### Resources and Tuning DuckDB reads the *host machine's* RAM and core count rather than the container's limits, so left alone it would size itself for hardware it does not have and get OOM-killed. The template therefore derives both settings from your values and writes them into the preamble explicitly: | Setting | Derived from | At the defaults | | -------------- | ---------------------------------------------------- | ---------------------- | | `memory_limit` | `tuning.memoryLimitPercent` of `resources.maxMemory` | `2457MiB` (60% of 4Gi) | | `threads` | One per whole core of `resources.maxCpu`, minimum 1 | `2` | `tuning.memoryLimitPercent` must be between 20 and 80. Above 80 is the default DuckDB behavior that gets containers killed; below 20 wastes the container. The chart validates the resource block at render time and refuses to install with a message naming the value to fix: `minMemory` may not exceed `maxMemory`, `minCpu` may not exceed `maxCpu`, and `maxCpu:minCpu` may not exceed 4:1 (a Control Plane limit). ### Memory and Storage **No volume is attached to this workload**, which has two consequences worth planning around. Every job must fit in memory. Size the work with `resources.maxMemory` and `tuning.memoryLimitPercent` rather than relying on spill. DuckDB's out-of-core operators do still function, but they spill to container-local scratch at `/tmp/duckdb-temp` bounded by container disk — not to a sized, persistent volume. Larger-than-memory processing is not a capability this template offers. No `.duckdb` database file is kept either. Every run starts from an empty in-memory database, so results must be written somewhere durable: object storage, or a table in an attached database. ### Extensions DuckDB extensions are written to `/tmp/duckdb-extensions`, which is container-local scratch. Because there is no cache volume, **extensions are re-downloaded from `extensions.duckdb.org` on every single run**. This is a real per-run dependency: a job that runs fine today will fail if egress to that host is later blocked. The commonly used `httpfs` and `aws` extensions autoload on first use of an `s3://` path, so most scripts never issue an explicit `INSTALL`. ### Object Storage `objectStore.type` decides whether the preamble registers a DuckDB S3 secret. Anything other than `none` lets your SQL read and write `s3://` paths directly. No S3 secret is registered. The job can still read local files, HTTP URLs and attached databases. ```yaml theme={null} objectStore: type: none ``` The job receives short-lived credentials from its own workload identity — no access keys are created, stored or injected anywhere. Create your S3 bucket and set `objectStore.aws.region` to its region. If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) for your AWS account. Set `objectStore.aws.cloudAccountName` to its name. Create an AWS IAM policy with the JSON below (replace `YOUR_BUCKET_NAME`), then set `objectStore.aws.policyName` to the policy's name: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:ListBucket", "s3:GetBucketLocation" ], "Resource": "arn:aws:s3:::YOUR_BUCKET_NAME" }, { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject" ], "Resource": "arn:aws:s3:::YOUR_BUCKET_NAME/*" } ] } ``` Drop `s3:PutObject` and `s3:DeleteObject` if the job only reads. Set `objectStore.type: aws`. The chart requires `region`, `cloudAccountName` and `policyName` in this mode and refuses to render without them. Covers [SeaweedFS](/template-catalog/templates/seaweedfs), [MinIO](/template-catalog/templates/minio), Cloudflare R2, Tigris, and Google Cloud Storage through its S3 interoperability endpoint. These servers cannot federate with a cloud account, so they use static keys held in a secret. Create the bucket on your server, plus an access key and secret key pair with read and write access to it. For Google Cloud Storage these are HMAC keys, created under Cloud Storage → Settings → Interoperability. Create a [dictionary secret](/guides/create-secret/dictionary) with exactly these two keys, and set `objectStore.s3Compatible.credentialsSecretName` to its name: ```bash theme={null} cpln secret create-dictionary --name my-duckdb-s3-credentials \ --entry access-key-id=YOUR_ACCESS_KEY \ --entry secret-access-key=YOUR_SECRET_KEY ``` The DuckDB identity is granted `reveal` on exactly this secret. Set `objectStore.type: s3-compatible` and `objectStore.s3Compatible.endpoint` to the server's `host:port` with **no** `http://` or `https://` prefix — the chart rejects a scheme and tells you to use `useSsl` instead. | Provider | `endpoint` | `urlStyle` | `useSsl` | | ------------------------- | --------------------------------------- | ---------- | -------- | | SeaweedFS in the same GVC | `my-seaweedfs.my-gvc.cpln.local:8333` | `path` | `false` | | MinIO in the same GVC | `my-minio.my-gvc.cpln.local:9000` | `path` | `false` | | Google Cloud Storage | `storage.googleapis.com` | `path` | `true` | | Cloudflare R2 | `.r2.cloudflarestorage.com` | `path` | `true` | | AWS S3 with static keys | `s3.us-east-1.amazonaws.com` | `vhost` | `true` | ## Connecting This template exposes nothing to connect to — it is a job, not a server. Observe and drive it instead: | What | How | | ------------------ | ------------------------------------------------------------------------------------- | | Public URL | None. The workload binds no port and accepts no inbound traffic. | | Internal host:port | None. | | Job output | `cpln logs '{gvc="GVC_NAME", workload="RELEASE_NAME-duckdb"}' --limit 200 --since 1h` | | Run history | `cpln workload cron get RELEASE_NAME-duckdb --gvc GVC_NAME` | | Run it now | `cpln workload cron start RELEASE_NAME-duckdb --gvc GVC_NAME` | | Credentials | None are issued. The job reads the secrets you name in `secretEnv` and `objectStore`. | ## Confirming a Run Succeeded A run succeeded only when **both** of these are true: 1. The line `duckdb-job-complete` appears in the job's log output. 2. The run's status is `Successful` in `cpln workload cron get`. Check both, never either one alone. Each covers a hole in the other: * **The marker alone is not enough.** A run terminated for exceeding `activeDeadlineSeconds` can finish its script inside the termination lag and print the marker on its way out, while the platform has already recorded the run as failed. * **The status alone is not enough.** DuckDB's CLI can exit `0` on some failed scripts ([upstream issue #16574](https://github.com/duckdb/duckdb/issues/16574)), which would leave the run looking successful. The marker is emitted by a final statement the template appends after your script, and the preamble's `.bail on` stops execution at the first error — so a SQL error aborts the script and the marker is never printed. Alerting on the pair is what makes the signal trustworthy in both directions. ## Important Notes * **This is a batch job, not a query service.** Nothing is listening after install; that is correct behavior. For an always-on SQL endpoint, use [trino](/template-catalog/templates/trino). * **Check both success signals** — the `duckdb-job-complete` marker *and* a run status of `Successful`. Neither one alone is reliable. * **`activeDeadlineSeconds` terminates a run within roughly two minutes of the deadline**, not exactly at it, and the container bills for that window. * **Every job must fit in memory.** No volume is attached, so raise `resources.maxMemory` rather than relying on spill. * **Never `SET memory_limit` higher than the container.** Change `tuning.memoryLimitPercent` instead — DuckDB left to itself targets 80% of the *host* machine and gets OOM-killed. * **No `.duckdb` file is kept.** Every run starts from an empty in-memory database; write results to object storage or an attached database. * **Extensions are re-downloaded on every run** from `extensions.duckdb.org`. Every execution depends on that host being reachable. * **Prerequisite secrets must exist before install**, and uninstalling the release does not delete them — the template only removes the secrets it created itself. * **Installing this template several times is scale-out, not high availability.** Separate releases with different scripts or schedules run independently, but there is no failover: if tonight's container dies, tonight's job did not happen. * **One script per install, by design.** Multi-step, conditional or retrying pipelines belong in [airflow](/template-catalog/templates/airflow). ## External References Official DuckDB documentation The CLI flags this template uses to run your script Every setting available to a preamble or script Reading and writing s3:// paths with httpfs How memory and thread settings affect performance View the source files, default values, and chart definition # Elasticsearch Source: https://docs.controlplane.com/template-catalog/templates/elasticsearch ## Overview Elasticsearch is a distributed search and analytics engine built on Apache Lucene. This template deploys a production-ready Elasticsearch 8.17.0 cluster with automatic master election, optional Kibana for visualization and management, and automated snapshot backups to AWS S3 or GCS via Elasticsearch's built-in Snapshot Lifecycle Management (SLM). This template does not create a GVC. You must deploy it into an existing GVC. ### What Gets Created * **Stateful Elasticsearch Workload** — A multi-node Elasticsearch cluster. Each replica gets its own persistent volume so index data and shards survive restarts. * **Volume Set** — One persistent volume per replica for Elasticsearch data. * **Standard Kibana Workload** *(optional, enabled by default)* — The Elasticsearch web UI for querying data, managing indices, and monitoring cluster health. * **Standard Backup Setup Workload** *(optional)* — A one-time job that waits for the cluster to be healthy, registers the snapshot repository with Elasticsearch, and creates the SLM policy. Runs once and can be removed after initial setup. * **Identity & Policy** — An identity bound to the workloads with `reveal` access to the configuration secrets, and cloud storage access when backup is enabled. * **Secrets** — Configuration secrets for Elasticsearch and Kibana credentials and cluster settings. ## Prerequisites This template has no external prerequisites unless backup is enabled. To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} image: docker.elastic.co/elasticsearch/elasticsearch:8.17.0 replicas: 3 # Must be odd for master quorum (3, 5, 7, ...) clusterName: my-elasticsearch-cluster # JVM heap size per node — set to ~50% of maxMemory, hard cap at 30g jvmHeap: 3g resources: minCpu: 1 minMemory: 2Gi maxCpu: 2 maxMemory: 6Gi volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) autoscaling: enabled: false maxCapacity: 100 minFreePercentage: 10 scalingFactor: 1.2 multiZone: enabled: false # Set to true to schedule replicas across availability zones internal_access: type: same-gvc # options: same-gvc, same-org, workload-list workloads: #- //gvc/GVC_NAME/workload/WORKLOAD_NAME kibana: enabled: true image: docker.elastic.co/kibana/kibana:8.17.0 resources: cpu: 500m memory: 2Gi internal_access: type: same-gvc # options: same-gvc, same-org, workload-list workloads: #- //gvc/GVC_NAME/workload/WORKLOAD_NAME backup: enabled: false remove_setup_workload: false # Set to true after setup completes to reduce resource usage provider: aws # options: aws or gcp # Snapshot schedule in Quartz cron format (6 fields: seconds minutes hours day-of-month month day-of-week) schedule: "0 0 2 * * ?" retention: maxAge: 30d # Delete snapshots older than this maxCount: 30 # Keep at most this many snapshots aws: bucket: my-s3-bucket region: us-east-1 prefix: elasticsearch-snapshots cloudAccountName: my-cloud-account policyName: my-backup-policy gcp: bucket: my-gcs-bucket prefix: elasticsearch-snapshots cloudAccountName: my-cloud-account ``` ### Cluster Settings * `replicas` — Number of Elasticsearch nodes. **Must be an odd number** (3, 5, 7) to maintain a valid master election quorum. * `clusterName` — The Elasticsearch cluster name. Used internally by nodes to discover each other. * `jvmHeap` — JVM heap size per node. Set to approximately 50% of `maxMemory`. Hard cap at `30g`. A minimum of 3 replicas is required for master quorum. With 3 nodes, the cluster can survive the loss of 1 node. Always use an odd number — an even number of nodes does not improve fault tolerance and can cause split-brain scenarios. ### Resources and Storage * `resources.minCpu` / `resources.minMemory` — Guaranteed minimum CPU and memory per node. * `resources.maxCpu` / `resources.maxMemory` — Maximum CPU and memory per node. * `volumeset.capacity` — Initial volume size in GiB per node (minimum 10). * `volumeset.autoscaling.enabled` — Automatically expand volumes as data grows. * `volumeset.autoscaling.maxCapacity` — Maximum volume size in GiB. * `volumeset.autoscaling.minFreePercentage` — Triggers a scale-up when free space falls below this percentage. * `volumeset.autoscaling.scalingFactor` — Multiplier applied to current capacity on each scale-up. ### Multi-Zone When `multiZone.enabled: true`, Control Plane spreads Elasticsearch replicas across availability zones within the location. This improves durability — if a zone goes down, remaining nodes in other zones maintain quorum. Verify your selected location supports multiple availability zones before enabling. ### Internal Access Controls which workloads can reach Elasticsearch and Kibana. Both have independent `internal_access` settings. | Type | Description | | --------------- | --------------------------------------------------------------- | | `same-gvc` | Allow access from all workloads in the same GVC (recommended) | | `same-org` | Allow access from all workloads in the org | | `workload-list` | Allow access only from specific workloads listed in `workloads` | ### Kibana Kibana is enabled by default and provides a web UI for exploring data, managing indices, building dashboards, and monitoring cluster health. * `kibana.enabled` — Deploy the Kibana workload. * `kibana.resources.cpu` / `kibana.resources.memory` — CPU and memory for the Kibana container. * `kibana.internal_access` — Controls which workloads can reach Kibana (same options as `internal_access`). ### Connecting Both Elasticsearch and Kibana are accessible internally from within the same GVC. External access is blocked by default — use `cpln port-forward` to reach them from your local machine. | Service | Internal hostname | Port | | ------------- | ---------------------------------------------------- | ------ | | Elasticsearch | `{release-name}-elasticsearch.{gvc-name}.cpln.local` | `9200` | | Kibana | `{release-name}-kibana.{gvc-name}.cpln.local` | `5601` | **Port-forward to Kibana:** ```bash theme={null} cpln workload port-forward --gvc GVC_NAME RELEASE_NAME-kibana --port 5601 # Then open http://localhost:5601 in your browser ``` **Port-forward to Elasticsearch:** ```bash theme={null} cpln workload port-forward --gvc GVC_NAME RELEASE_NAME-elasticsearch --port 9200 ``` ### Scaling **Scaling up** — Increase `replicas` to the next odd number and run `cpln helm upgrade`. New nodes join the cluster automatically and Elasticsearch begins rebalancing shards. **Scale down is destructive.** Elasticsearch does not automatically move shards off nodes that are about to be removed. Always take a manual snapshot before scaling down, and verify shard allocation with `GET /_cat/shards?v` before removing nodes to ensure no primary shards are stranded on the nodes being removed. ## Backup Elasticsearch backups are **incremental snapshots** stored directly in S3 or GCS via the built-in repository-s3 and repository-gcs plugins — no separate backup image is required. The Snapshot Lifecycle Management (SLM) feature handles scheduling and retention automatically. When backup is enabled, a one-time **Backup Setup Workload** runs at install time. It waits for the cluster to be healthy, then calls the Elasticsearch API to register the snapshot repository and create the SLM policy. Once it completes successfully, it can be removed to save resources: ```yaml theme={null} backup: enabled: true remove_setup_workload: true ``` The backup schedule uses **Quartz cron format** with 6 fields (seconds, minutes, hours, day-of-month, month, day-of-week) — not the standard 5-field cron format. For example, `"0 0 2 * * ?"` runs daily at 2am UTC. ### AWS S3 Prerequisites 1. Create an S3 bucket. Set `backup.aws.bucket` to its name and `backup.aws.region` to its region. 2. If you do not have a Cloud Account set up, refer to the docs to [Create a Cloud Account](/guides/create-cloud-account). Set `backup.aws.cloudAccountName` to its name. 3. Create an IAM policy with the following JSON, replacing `YOUR_BUCKET_NAME`: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket", "s3:GetObjectVersion", "s3:DeleteObjectVersion" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET_NAME", "arn:aws:s3:::YOUR_BUCKET_NAME/*" ] } ] } ``` 4. Set `backup.aws.policyName` to the name of the policy created in step 3. ### GCS Prerequisites 1. Create a GCS bucket. Set `backup.gcp.bucket` to its name. 2. If you do not have a Cloud Account set up, refer to the docs to [Create a Cloud Account](/guides/create-cloud-account). Set `backup.gcp.cloudAccountName` to its name. 3. Add the **Storage Admin** role to the GCP service account associated with the Cloud Account. ### Manual Snapshots Exec into any Elasticsearch container to trigger a snapshot immediately or inspect status: ```bash theme={null} # Trigger a snapshot now via SLM curl -X PUT 'http://localhost:9200/_slm/policy/automated-snapshots/_execute' # List all snapshots in the repository curl 'http://localhost:9200/_snapshot/backup-repo/_all?pretty' # Check a snapshot currently in progress curl 'http://localhost:9200/_snapshot/backup-repo/_current?pretty' # View the SLM policy and last execution result curl 'http://localhost:9200/_slm/policy/automated-snapshots?pretty' ``` ## Restoring a Snapshot Exec into any Elasticsearch node in the cluster to run restore commands. The snapshot repository (`backup-repo`) is already registered. **List available snapshots:** ```bash theme={null} curl 'http://localhost:9200/_snapshot/backup-repo/_all?pretty' ``` ### Scenario 1 — Disaster Recovery (Fresh Cluster) Deploy a new cluster from this template with backup enabled and pointing at the same bucket. Once the Backup Setup Workload completes (re-registering the same repository), restore all indices: ```bash theme={null} curl -X POST 'http://localhost:9200/_snapshot/backup-repo/SNAPSHOT_NAME/_restore' \ -H 'Content-Type: application/json' \ -d '{ "indices": ["*", "-.internal.*", "-.slo-observability.*.temp", "-.ds-ilm-history*"], "ignore_unavailable": true, "include_global_state": false }' ``` ### Scenario 2 — Restore to Existing Cluster When the target indices already exist, close them before restoring or the restore will be rejected: ```bash theme={null} # Close the index first curl -X POST 'http://localhost:9200/MY_INDEX/_close' # Restore from snapshot curl -X POST 'http://localhost:9200/_snapshot/backup-repo/SNAPSHOT_NAME/_restore' \ -H 'Content-Type: application/json' \ -d '{ "indices": "MY_INDEX", "ignore_unavailable": true, "include_global_state": false }' ``` ### Scenario 3 — Restore Specific Indices ```bash theme={null} curl -X POST 'http://localhost:9200/_snapshot/backup-repo/SNAPSHOT_NAME/_restore' \ -H 'Content-Type: application/json' \ -d '{ "indices": "my-index,my-other-index-2026.05*", "ignore_unavailable": true, "include_global_state": false }' ``` **Monitor restore progress:** ```bash theme={null} # View active recovery operations curl 'http://localhost:9200/_cat/recovery?v&active_only=true' # Check overall cluster health curl 'http://localhost:9200/_cluster/health?pretty' ``` ## Important Notes * **Replica count must be odd** — Elasticsearch requires an odd number of master-eligible nodes for quorum. Even numbers do not improve fault tolerance and can cause split-brain. * **JVM heap** — Set `jvmHeap` to approximately 50% of `maxMemory`, with a hard cap of `30g`. Elasticsearch relies heavily on off-heap memory for the filesystem cache. * **Backup schedule format** — SLM uses Quartz cron format (6 fields), not the standard 5-field cron. The `?` wildcard is required in the day-of-week field when day-of-month is set. * **Scale down carefully** — Always snapshot before reducing replicas. Verify no primary shards exist on nodes being removed before running the upgrade. ## External References Official Elasticsearch reference documentation Official Kibana guide Elasticsearch snapshot and restore guide Create, monitor, and delete snapshots with SLM View the source files, default values, and chart definition # External Secret Syncer Source: https://docs.controlplane.com/template-catalog/templates/ess Deploy External Secret Syncer on Control Plane using the Template Catalog. Covers configuration, scaling, and syncing secrets from AWS, Vault, GCP Secret Manager, 1Password, Doppler, and Infisical. ## Overview The External Secret Syncer (ESS) continuously syncs secrets and parameters from external providers into Control Plane secrets. This template deploys ESS as a workload that polls your configured providers on a set interval and creates or updates Control Plane secrets to match. ### How It Works ESS runs as a workload on Control Plane. Your provider configuration and secrets list are stored in a Control Plane secret and mounted into the workload as `sync.yaml`. On startup, ESS schedules a polling loop for each configured secret. At each interval, it fetches the latest value from the external provider and creates or updates the corresponding Control Plane secret via the API. ESS tags every secret it manages with `syncer.cpln.io/source` (set to the workload path). This prevents two ESS instances from accidentally overwriting each other's secrets. ESS only creates and updates secrets — it never deletes them, so removing a secret from `sync.yaml` leaves the existing Control Plane secret in place. ESS watches its config file and automatically restarts when changes are detected (every \~5 seconds). No workload restart is needed after updating the config secret. ### Supported Providers * [AWS Systems Manager Parameter Store](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html) * [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/) * [HashiCorp Vault](https://www.hashicorp.com/en/products/vault) * [GCP Secret Manager](https://cloud.google.com/security/products/secret-manager) * [1Password](https://1password.com/) * [1Password Connect](https://developer.1password.com/docs/connect/) * [Doppler](https://www.doppler.com/) * [Infisical](https://infisical.com/) ### What Gets Created * **Standard ESS Workload** — An ESS container with a readiness probe on `/about`. * **Identity & Policy** — An identity bound to the workload with `manage` permissions on all secrets, allowing ESS to create and update Control Plane secrets. * **Secret** — An opaque secret containing the sync configuration (`sync.yaml`) with providers and secret mappings. This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites 1. A secret or parameter stored in one of the [supported providers](#supported-providers). 2. Credentials with read access to the desired secret (API token, IAM keys, etc.). Alternatively, you can use a cloud access [identity](/reference/identity) instead of supplying keys directly. ### Installation To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} image: ghcr.io/controlplane-com/cpln-build/external-secret-syncer:v1.3.4 resources: cpu: 200m memory: 256Mi port: 3004 allowedIp: - 1.2.3.4 # Replace with your IP essConfig: providers: - name: my-vault vault: address: https://my-vault.com:8200 token: syncInterval: 1m - name: my-aws-ssm awsParameterStore: region: us-east-1 accessKeyId: # alternatively configure identity to natively use AWS permissions secretAccessKey: # alternatively configure identity to natively use AWS permissions # - name: my-aws-secrets-manager # awsSecretsManager: # region: us-east-1 # accessKeyId: # secretAccessKey: # - name: my-1password # onePassword: # serviceAccountToken: # integrationName: my-ess # integrationVersion: 1.0.0 # - name: my-1password-connect # onePasswordConnect: # serverURL: https://my-connect-server.example.com # token: # - name: my-doppler # doppler: # accessToken: # - name: my-gcp # gcpSecretManager: # projectId: 123456789876 # credentials: # clientEmail: # privateKey: # - name: my-infisical # infisical: # clientId: # from an Infisical machine identity # clientSecret: # projectId: secrets: - name: auth provider: my-vault syncInterval: 20s dictionary: PORT: path: /v1/secret/data/app parse: data.port default: 5432 PASSWORD: path: /v1/secret/data/app parse: data.password USERNAME: default: "no username" path: /v1/secret/data/app parse: data.username - name: ssm provider: my-aws syncInterval: 20s opaque: /example/app # - name: secrets-manager # provider: my-aws-secrets-manager # dictionary: # PASSWORD: # path: /example/app # parse: password # - name: doppler-secret # provider: my-doppler # opaque: /project/config/SECRET_NAME # - name: doppler-project # provider: my-doppler # dictionaryFromProject: # path: project/config # syncs all secrets from a Doppler project+config # - name: gcp # provider: my-gcp # opaque: database-password # - name: gcp-project # provider: my-gcp # dictionaryFromProject: true # combines every accessible secret from the GCP project into one dictionary # - name: gcp-discover # provider: my-gcp # discoverAllSecrets: true # GCP only — creates one Control Plane secret per project secret; # # each secret's "cpln-type" label (opaque|dictionary) sets the type # - name: infisical-secret # provider: my-infisical # opaque: dev/DATABASE_URL # format: "/" # - name: json-config # provider: my-aws-secrets-manager # dictionaryFromJson: /example/app/config # fetches a JSON object and flattens it into a dictionary ``` ### Top-Level Fields * `image` — The ESS container image. Do not change unless upgrading. * `resources.cpu` / `resources.memory` — Resource limits for the workload container. * `port` — Port for the ESS HTTP admin API (default: `3004`). Used for health checks and manual sync triggers. * `allowedIp` — List of CIDRs allowed to reach the ESS admin API externally. Replace the placeholder with your IP, or use `0.0.0.0/0` to allow all. * `essConfig` — The full sync configuration — providers and secrets (see below). ### Providers Each entry in `essConfig.providers` defines a connection to an external secret store. Every provider must have a unique `name`. An optional `syncInterval` sets the default polling interval for all secrets using that provider. | Provider | Required Fields | | ------------------- | --------------------------------------------------------------------- | | HashiCorp Vault | `vault.address`, `vault.token` | | AWS Parameter Store | `awsParameterStore.region` | | AWS Secrets Manager | `awsSecretsManager.region` | | GCP Secret Manager | `gcpSecretManager.projectId` | | 1Password | `onePassword.serviceAccountToken` | | 1Password Connect | `onePasswordConnect.serverURL`, `onePasswordConnect.token` | | Doppler | `doppler.accessToken` | | Infisical | `infisical.clientId`, `infisical.clientSecret`, `infisical.projectId` | AWS providers optionally accept `accessKeyId` and `secretAccessKey`. If omitted, ESS falls back to credentials provided through the workload's cloud access [identity](/reference/identity). GCP Secret Manager optionally accepts `credentials.clientEmail` and `credentials.privateKey`. If omitted, ESS uses Application Default Credentials. **Provider examples:** ```yaml theme={null} # HashiCorp Vault - name: my-vault vault: address: https://my-vault.com:8200 token: syncInterval: 1m # AWS Parameter Store - name: my-aws-ssm awsParameterStore: region: us-east-1 accessKeyId: # optional if using an IAM-linked identity secretAccessKey: # optional if using an IAM-linked identity # AWS Secrets Manager - name: my-aws-secrets-manager awsSecretsManager: region: us-east-1 accessKeyId: secretAccessKey: # GCP Secret Manager - name: my-gcp gcpSecretManager: projectId: 123456789876 credentials: # optional — omit to use Application Default Credentials clientEmail: privateKey: # 1Password - name: my-1password onePassword: serviceAccountToken: integrationName: my-ess # optional integrationVersion: 1.0.0 # optional # 1Password Connect - name: my-1password-connect onePasswordConnect: serverURL: https://my-connect-server.example.com token: # Doppler - name: my-doppler doppler: accessToken: # use a Doppler service token (dp.st....) # Infisical - name: my-infisical infisical: clientId: example-client-id # create an Infisical machine identity for the ESS clientSecret: example-client-secret projectId: example-project-id ``` ### Secrets Each entry in `essConfig.secrets` maps an external secret to a Control Plane secret. Each secret must specify a `name`, a `provider`, and exactly one sync type. #### `opaque` — Single value Creates a Control Plane `opaque` secret from a single fetched value. Shorthand (path only): ```yaml theme={null} - name: my-secret provider: my-vault opaque: /v1/secret/data/myapp ``` With options: ```yaml theme={null} - name: my-secret provider: my-vault opaque: path: /v1/secret/data/myapp # path to fetch parse: data.password # optional — extract a key from a JSON/YAML response default: fallback-value # optional — used if fetch fails encoding: base64 # optional — base64-decode the fetched value before storing ``` Vault KV engine secrets are nested under a `data` key. When using `parse`, start with `data` to access the secret content (e.g., `data.password`). If you use the shorthand form with no `default`, a fetch failure causes the sync to fail with no fallback. #### `dictionary` — Multiple values Creates a Control Plane `dictionary` secret. Each key is fetched independently and supports `path`, `parse`, `default`, and `encoding`. ```yaml theme={null} - name: auth provider: my-vault dictionary: PORT: path: /v1/secret/data/app parse: data.port default: 5432 PASSWORD: path: /v1/secret/data/app parse: data.password USERNAME: path: /v1/secret/data/app parse: data.username default: "no username" ``` A failure on one key does not block the others. #### `dictionaryFromProject` — Sync an entire project Syncs all secrets from a provider project in one operation, stored as a Control Plane `dictionary` secret. Only valid with a Doppler or GCP Secret Manager provider. The expected shape differs per provider. **Doppler** — specify a `project/config` path: ```yaml theme={null} - name: my-doppler-config provider: my-doppler dictionaryFromProject: path: my-project/dev # format: "project/config" — exactly two segments ``` **GCP Secret Manager** — set to `true` to pull every accessible secret from the project configured on the provider. Each fetched secret's latest version becomes one key in the resulting dictionary. Secrets with no accessible latest version (no versions, disabled, or destroyed) are skipped. ```yaml theme={null} - name: my-gcp-config provider: my-gcp dictionaryFromProject: true ``` Doppler requires the `{ path: ... }` object form; GCP Secret Manager requires the `true` form. Mixing them — or using either with another provider — causes ESS to exit at startup. #### `dictionaryFromJson` — Flatten a JSON object Fetches a single value that contains a JSON object and flattens it into a Control Plane `dictionary` secret. Set it to the path of the value to fetch. Valid with any provider. ```yaml theme={null} - name: json-config provider: my-aws-secrets-manager dictionaryFromJson: /example/app/config # path to a value holding a JSON object ``` Each leaf in the JSON object becomes one dictionary key: * **Nested objects** are flattened using dot notation (`{ "db": { "host": "x" } }` → key `db.host`). * **Arrays** are JSON-stringified into the value (`{ "tags": ["a", "b"] }` → key `tags` with value `["a","b"]`). * **`null`** is stored as the string `"null"`; numbers and booleans are stored as their string form. For example, the JSON object `{"db":{"host":"db.internal","port":5432},"tags":["a","b"]}` produces a dictionary with keys `db.host` (`db.internal`), `db.port` (`5432`), and `tags` (`["a","b"]`). If the fetched value is not a valid JSON object — a raw string, number, array, or malformed JSON — ESS stores the raw value under a single `__raw` key and logs a warning, so the secret stays usable as a dictionary type. #### `discoverAllSecrets` — Mirror an entire GCP project (one secret each) Discovers every accessible secret in the GCP project configured on the provider and creates a **separate** Control Plane secret for each one. This differs from `dictionaryFromProject: true`, which combines every secret into a single dictionary. GCP Secret Manager only, and must be set to `true`. ```yaml theme={null} - name: gcp-discover # identifier only — created secrets are named after the GCP secrets provider: my-gcp discoverAllSecrets: true ``` The type of each created secret is controlled by a `cpln-type` **label** on the GCP secret: * `cpln-type: dictionary` — the value is parsed as JSON and flattened into a Control Plane `dictionary` secret (same flattening rules as `dictionaryFromJson`, including the `__raw` fallback for non-JSON values). * `cpln-type: opaque` (or no label) — stored as a Control Plane `opaque` secret. Each Control Plane secret is named after its GCP secret, normalized to a valid Control Plane name (lowercased, with unsupported characters such as `_` replaced by `-` — e.g. `MY_API_KEY` → `my-api-key`). If two names normalize to the same value, the last one wins and a warning is logged. Created secrets carry a `syncer.cpln.io/discoveredBy` tag set to this entry's `name`. Secrets with no accessible latest version (no versions, disabled, or destroyed) are skipped. `discoverAllSecrets` is only valid with a GCP Secret Manager provider. ESS does not delete a discovered Control Plane secret when its source GCP secret is later removed. ### Sync Interval Intervals use the format `hms`. All parts are optional but at least one is required. Examples: `10s`, `5m`, `1h`, `1h30m`, `1h30m10s`. Priority (highest wins): 1. Secret-level `syncInterval` 2. Provider-level `syncInterval` 3. Global default (`300s`) ### Doppler Path Formats | Sync type | Path format | Example | | ---------------------------- | ---------------------------- | -------------------------------- | | `opaque` or `dictionary` key | `project/config/SECRET_NAME` | `my-app/production/DATABASE_URL` | | `dictionaryFromProject` | `project/config` | `my-app/production` | ### Infisical Path Formats The Infisical project is set on the provider (`infisical.projectId`). Secret paths are scoped to an environment within that project. | Sync type | Path format | Example | | ---------------------------- | -------------------------- | ------------------ | | `opaque` or `dictionary` key | `/` | `dev/DATABASE_URL` | ## Synced Secret Output A secret created by ESS will look like: ```yaml theme={null} kind: secret name: hello description: hello tags: syncer.cpln.io/lastError: '' # populated if ESS encounters an error syncer.cpln.io/source: //gvc//workload/ type: dictionary data: PORT: '1234' PASSWORD: 'no pass' # default used if the key was not found ``` The `syncer.cpln.io/lastError` tag is empty on success. If ESS encounters an error syncing a secret, the tag is populated with the error message. ## Important Notes * **Conflict protection** — If a Control Plane secret already exists and is managed by a different ESS instance, the sync for that secret will fail. Two ESS instances cannot manage the same secret. * **Secret type changes** — Changing a secret from `opaque` to `dictionary` (or vice versa) causes ESS to delete the existing secret and recreate it. There is a brief window where the secret does not exist. * **No automatic deletion** — ESS only creates and updates secrets; it never deletes them. Removing a secret from `sync.yaml`, or deleting a source secret upstream, leaves the existing Control Plane secret in place — delete unwanted secrets manually. ESS-managed secrets carry the `syncer.cpln.io/source` tag (and discovered secrets additionally carry `syncer.cpln.io/discoveredBy`) to help identify them. * **Doppler `parse`** — The `parse` field only works when the Doppler secret's value is JSON or YAML. Using `parse` on a plain string secret throws an error. * **Hot reload** — ESS watches its config file and automatically restarts when changes are detected (every \~5 seconds). No workload restart is needed after updating the config secret. ## External References AWS Systems Manager Parameter Store documentation AWS Secrets Manager documentation HashiCorp Vault secret management Google Cloud Secret Manager documentation 1Password secret management Doppler secrets platform Infisical secret management platform Source code for the External Secret Syncer image View the source files, default values, and chart definition # etcd Source: https://docs.controlplane.com/template-catalog/templates/etcd Deploy etcd on Control Plane using the Template Catalog. Covers configuration, volumes, scaling, and distributed key-value store cluster setup with peer discovery. ## Overview etcd is a distributed, reliable key-value store designed for the most critical data of a distributed system. It provides consistent coordination, service discovery, and configuration management across distributed systems, making it a common foundation for cluster health and orchestration. This template deploys an etcd cluster as a stateful workload with configurable replica count, persistent storage, and auto-compaction configured so the backend does not grow without bound. ### What Gets Created * **Stateful Workload** — An etcd cluster with a configurable number of replicas (default: 3). * **Volume Set** — Persistent storage for etcd data. * **Secret** — An opaque startup script secret that handles cluster initialization, peer URL configuration, and replica setup. * **Identity & Policy** — An identity bound to the workload with `reveal` access to the startup script secret. This template does not create a GVC. You must deploy it into an existing GVC. ## Installation This template has no external prerequisites. To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} replicas: 3 # minimum 3, must be odd resources: cpu: 1 memory: 2Gi multiZone: false # etcd keeps every superseded revision until told otherwise, so compaction is # required rather than optional and cannot be switched off here. tuning: autoCompactionMode: periodic # periodic (retention is a duration) or revision (a revision count) autoCompactionRetention: 1h # periodic needs an explicit unit (1h, 30m, 24h); revision takes a count quotaBackendBytes: 0 # backend size limit in bytes; 0 = etcd's own default of 2 GiB volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) internal_access: type: same-gvc # options: same-gvc, same-org, workload-list workloads: # Note: can only be used if type is same-gvc or workload-list #- //gvc/GVC_NAME/workload/WORKLOAD_NAME ``` ### Replicas * `replicas` — Number of etcd instances in the cluster (minimum 3). The replica count must always be an odd number. etcd uses the Raft consensus algorithm, which requires a majority quorum — an odd number of members ensures a quorum can always be reached and avoids split-brain scenarios. ### Resources * `resources.cpu` / `resources.memory` — CPU and memory allocated to each etcd instance (default: 1 CPU, 2Gi). These can be lowered for lighter workloads. See the [etcd hardware recommendations](https://etcd.io/docs/v3.6/op-guide/hardware/) for guidance. ### Storage * `volumeset.capacity` — Persistent volume size in GiB for etcd data (minimum 10). ### Compaction and Backend Growth etcd never discards a superseded revision on its own. Every write creates a new revision and the old one is kept until a **compaction** removes it, so the backend grows with **time alone** whenever a client writes on a timer — Patroni, for example, renews its leader lease roughly every 10 seconds, which produces new revisions whether or not any application data changes. Measured on an otherwise idle cluster: about **151,000 revisions and 19 MB per day**, which reaches etcd's default 2 GiB backend quota in roughly **110 days**. When the quota is reached, etcd raises a cluster-wide `NOSPACE` alarm and **every member goes read-only** until an operator intervenes. Auto-compaction is what prevents that, and this template configures it by default. ```yaml theme={null} tuning: autoCompactionMode: periodic # periodic (retention is a duration) or revision (a revision count) autoCompactionRetention: 1h # periodic needs an explicit unit (1h, 30m, 24h); revision takes a count quotaBackendBytes: 0 # backend size limit in bytes; 0 = etcd's own default of 2 GiB ``` * `tuning.autoCompactionMode` — How `autoCompactionRetention` is read. `periodic` treats it as a time window; `revision` treats it as a number of revisions to keep. There is no "off" — an unrecognized mode is rejected at render time. * `tuning.autoCompactionRetention` — How much history is kept before old revisions are discarded. The default `1h` keeps the backend flat. Raising it to `24h` or more buys a longer history window at the cost of a larger backend. * `tuning.quotaBackendBytes` — Backend size ceiling in bytes. `0` (the default) means the flag is not passed at all and etcd applies its own 2 GiB limit. Leave it at `0` unless the keyspace genuinely outgrows that; 8 GiB (`8589934592`) is etcd's own suggested maximum, above which it warns at startup. **In `periodic` mode a retention value without a unit means hours.** etcd reads a bare `30` as thirty *hours*, not thirty minutes. This template rejects an unsuffixed value at render time rather than letting it become a silently wrong window — write `30m`, `1h` or `24h`. **`quotaBackendBytes` takes a plain byte count, not a size suffix.** etcd itself refuses `--quota-backend-bytes 2Gi` at boot and the cluster would crash-loop, so a `2Gi`-style value is rejected at render time instead. Write `2147483648`. Compaction is deliberately not switchable off: a retention of `0`, an unrecognized mode and a negative quota (which etcd reads as "no quota at all") are each rejected at render time. Each one produces a chart that installs cleanly and fails weeks later. **Compaction frees pages for reuse inside the backend file; it does not shrink the file.** The reported `dbSize` therefore plateaus rather than dropping, while `dbSizeInUse` falls back to the size of the live keyspace. That is the expected behavior and is sufficient to stay under the quota, because reclaimed pages are reused for new writes. Only `etcdctl defrag` returns space to the filesystem, and this template does not automate it — defragmentation blocks the member it runs on. Testing measured the mechanism directly on a shortened retention window: the compacted revision advanced from 1 to 4001, `dbSizeInUse` collapsed from 5.76 MB to 20,480 bytes while `dbSize` stayed at about 5.8 MB, and reads of pre-compaction revisions then failed with `required revision has been compacted`. The multi-day `dbSize` plateau is **derived from that mechanism rather than observed** over days. `etcdctl endpoint status` reports `QUOTA | 0 B` when `quotaBackendBytes` is `0`, because that column reflects the flag rather than the effective limit. The effective quota in that case is still etcd's 2 GiB default, which etcd logs at startup as `enabled backend quota with default value`. ### Multi-Zone * `multiZone` — When `true`, distributes replicas equally across available zones for higher availability. Not all locations support multi-zone deployments. Confirm that your target location supports multi-zone before enabling this option. ### Internal Access The `internal_access` section controls which workloads can reach the etcd cluster: | Type | Description | | --------------- | --------------------------------------------------------------- | | `same-gvc` | Allow access from all workloads in the same GVC | | `same-org` | Allow access from all workloads in the same organization | | `workload-list` | Allow access only from specific workloads listed in `workloads` | ## If the Backend Quota Is Already Full Auto-compaction is configured from template version `1.4.2` onward. Earlier versions passed no compaction flags at all, so a cluster installed from one of them retains every revision it has ever written and grows until it hits the quota. **Upgrading turns compaction on, but it cannot rescue a cluster that has already filled its backend.** Compaction stops further growth; it never shrinks an existing backend file. Once etcd has raised a `NOSPACE` alarm, writes stay rejected until an operator compacts, defragments each member and disarms the alarm — an upgrade does none of that. The symptom usually surfaces in the client rather than in etcd. A Patroni replica that cannot renew its lease exits *cleanly*, so it restart-loops with `exitCode: 0` and `reason: Completed` and a climbing restart count — which reads as healthy and gets misdiagnosed as a database fault. Check etcd first. Both inspection commands are read-only and safe to run on a live cluster: ```bash theme={null} cpln workload exec RELEASE_NAME-etcd --gvc GVC_NAME --container etcd -- etcdctl endpoint status --cluster -w table cpln workload exec RELEASE_NAME-etcd --gvc GVC_NAME --container etcd -- etcdctl alarm list ``` A `DB SIZE` close to 2.1 GB on every member, plus `NOSPACE` in the alarm list, confirms it. A healthy cluster prints nothing at all for `alarm list`. Recovery from there — compacting to a revision, defragmenting each member, then disarming the alarm — is an operator procedure this template deliberately does not perform, because each step is disruptive and the order matters. Follow etcd's [maintenance guide](https://etcd.io/docs/v3.6/op-guide/maintenance/) and plan it as a maintenance window. ## External References Official etcd documentation Compaction, defragmentation, and clearing a NOSPACE alarm etcd hardware and resource sizing guidelines View the source files, default values, and chart definition # etcd Multi-Location Source: https://docs.controlplane.com/template-catalog/templates/etcd-multi-location Deploy etcd Multi-Location on Control Plane using the Template Catalog. Covers configuration, volumes, quorum sizing, and a stretched key-value cluster with exactly one member per location. ## Overview etcd is a strongly consistent key-value store used as the coordination layer for leader election, distributed locking, and service configuration. This template deploys a **single stretched etcd cluster with exactly one member per Control Plane location**, sharing one raft quorum across regions, with raft timers tuned for cross-region round trips and auto-compaction enabled. It is also the consensus store used by the [`postgres-multi-location`](/template-catalog/templates/postgres-multi-location) template, and is independently useful as a cross-region coordination store for your own services. This template creates a new GVC and requires at least 2 locations. For a single-location cluster, use the [etcd](/template-catalog/templates/etcd) template instead. ### What Gets Created * **GVC** — A new GVC pinned to the configured locations via static placement. The GVC is what pins the deployment's locations, so it is always created. * **Stateful etcd Workload** — (`RELEASE_NAME-etcd`): one member in each configured location, using `replicaDirect` addressing so every member is individually reachable. Client API on `2379`, raft peer traffic on `2380`. * **Volume Set** — (`RELEASE_NAME-etcd-vs`): persistent storage per member at `/var/lib/etcd` for the raft write-ahead log and snapshots. ext4, general-purpose-ssd, final snapshot on delete, 7-day snapshot retention. * **Secret** — (`RELEASE_NAME-etcd-startup`): an opaque startup script that computes each member's name, peer URL, and the full cluster list at container start from the location it is running in. * **Identity & Policy** — An identity bound to the workload with `reveal` access to the startup script secret and nothing else. Members find each other over per-replica internal DNS (`replica-0.RELEASE_NAME-etcd.LOCATION.GVC_NAME.cpln.local:2380`). There is no operator, no discovery service, and no join step — every member receives the full cluster list up front and they elect a leader among themselves. ## Installation This template has no external prerequisites. To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} # ─── GVC and locations ──────────────────────────────────────────────────────── # Lives under `global` so a parent chart (postgres-multi-location) sets it once # and Helm propagates it here. See README before changing. global: gvc: name: etcd-multi-location-gvc # the GVC this chart creates # One etcd member per location. Minimum 2; 3 survives losing one location, # 5 survives losing two. See the quorum table in the README. locations: - name: aws-us-east-1 - name: aws-eu-central-1 - name: aws-us-west-2 # ─── etcd ───────────────────────────────────────────────────────────────────── image: controlplanecorporation/etcd:0.1 resources: cpu: 500m memory: 512Mi # Raft timers, tuned for cross-region round trips (measured 63-140 ms). # Raise both if your locations are more than ~250 ms apart. tuning: heartbeatIntervalMs: 250 # ~0.5-1.5x the worst round trip between locations electionTimeoutMs: 5000 # must be >= 10x heartbeatIntervalMs; maximum 50000 # etcd keeps every superseded revision until told otherwise, so compaction is # required rather than optional and cannot be switched off here. autoCompactionMode: periodic # periodic (retention is a duration) or revision (a revision count) autoCompactionRetention: 1h # periodic needs an explicit unit (1h, 30m, 24h); revision takes a count quotaBackendBytes: 0 # backend size limit in bytes; 0 = etcd's own default of 2 GiB volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) internalAccess: type: same-gvc # options: same-gvc, same-org, workload-list workloads: [] # only used when type is workload-list #- //gvc/GVC_NAME/workload/WORKLOAD_NAME # ─── Disaster recovery ──────────────────────────────────────────────────────── recovery: # EMERGENCY ONLY. Set to the surviving location's name to restart that member # as a new single-member cluster after permanently losing quorum. Read the # "Recovering from a lost location" section of the README first. forceNewClusterInLocation: "" ``` ### Locations and Quorum * `global.gvc.name` — Name of the GVC this template creates. It must **not** name a GVC that already exists. * `global.gvc.locations` — The member map: one etcd member is deployed in each listed location. At least 2 locations are required. Helm owns the GVC this template creates. If `global.gvc.name` matches a GVC that already exists, Helm adopts it and a later uninstall deletes that GVC along with everything inside it. Always pick a name no other release uses. etcd commits a write only when a **majority** of members accept it. Because there is exactly one member per location, the location is the failure domain: | Locations | Majority needed | Location losses survived | What that means | | --------- | --------------- | ------------------------ | ---------------------------------------------------------------------------------------- | | 2 | 2 | **0** | Losing either one stops writes. The survivor holds current data, but recovery is manual. | | 3 | 2 | **1** | Automatic failover. The recommended shape. | | 4 | 3 | **1** | No better than 3, and costs more. | | 5 | 3 | **2** | Survives losing two locations. | With N locations you survive `floor((N-1)/2)` losses, so an even count never buys anything over the odd count below it. Two locations is permitted as a deliberate warm-standby topology, but it survives nothing automatically. The `global.gvc` block lives under `global` so that a parent chart — `postgres-multi-location` consumes this template as a subchart — sets the GVC and location list once and Helm propagates it. Each location runs exactly one member; any other replica count fails at render time. Changing `global.gvc.locations` reprovisions the cluster: every member restarts with a new cluster list. This is not etcd's graceful `member add` / `member remove` path, so plan it as a maintenance window. ### Image and Resources * `image` — The etcd image to run (etcd 3.6.5). * `resources.cpu` / `resources.memory` — CPU and memory allocated to each member (default: `500m`, `512Mi`). ### Raft Tuning * `tuning.heartbeatIntervalMs` — Leader heartbeat interval, roughly 0.5–1.5× the worst round trip between your locations. * `tuning.electionTimeoutMs` — How long a follower waits before campaigning. Must be at least 10× the heartbeat interval, and at most `50000`. Both bounds are enforced at render time. The defaults detect a dead leader in about 5 seconds across an AWS us-east ↔ eu-central ↔ us-west triangle. Locations further apart (US ↔ Asia-Pacific is 350–400 ms) need both values raised in proportion. ### Storage * `volumeset.capacity` — Persistent volume size in GiB per member for the raft write-ahead log and snapshots (minimum 10). ### Compaction and Backend Growth etcd never discards a superseded revision on its own. Every write creates a new revision and the old one is kept until a **compaction** removes it, so the backend grows with **time alone** whenever a client writes on a timer — Patroni, for example, renews its leader lease roughly every 10 seconds, which produces new revisions whether or not any application data changes. Measured on an otherwise idle cluster: about **151,000 revisions and 19 MB per day**, which reaches etcd's default 2 GiB backend quota in roughly **110 days**. When the quota is reached, etcd raises a cluster-wide `NOSPACE` alarm and **every member goes read-only** until an operator intervenes. Auto-compaction is what prevents that. It has always been enabled in this template; since version `1.0.2` it is also configurable, alongside the backend quota: ```yaml theme={null} tuning: autoCompactionMode: periodic # periodic (retention is a duration) or revision (a revision count) autoCompactionRetention: 1h # periodic needs an explicit unit (1h, 30m, 24h); revision takes a count quotaBackendBytes: 0 # backend size limit in bytes; 0 = etcd's own default of 2 GiB ``` * `tuning.autoCompactionMode` — How `autoCompactionRetention` is read. `periodic` treats it as a time window; `revision` treats it as a number of revisions to keep. There is no "off" — an unrecognized mode is rejected at render time. * `tuning.autoCompactionRetention` — How much history is kept before old revisions are discarded. The default `1h` keeps the backend flat and is the value this template is tested at. Raising it to `24h` or more buys a longer history window at the cost of a larger backend. * `tuning.quotaBackendBytes` — Backend size ceiling in bytes. `0` (the default) means the flag is not passed at all and etcd applies its own 2 GiB limit. Leave it at `0` unless the keyspace genuinely outgrows that; 8 GiB (`8589934592`) is etcd's own suggested maximum, above which it warns at startup. **In `periodic` mode a retention value without a unit means hours.** etcd reads a bare `30` as thirty *hours*, not thirty minutes. This template rejects an unsuffixed value at render time rather than letting it become a silently wrong window — write `30m`, `1h` or `24h`. **`quotaBackendBytes` takes a plain byte count, not a size suffix.** etcd itself refuses `--quota-backend-bytes 2Gi` at boot and the cluster would crash-loop, so a `2Gi`-style value is rejected at render time instead. Write `2147483648`. Compaction is deliberately not switchable off: a retention of `0`, an unrecognized mode and a negative quota (which etcd reads as "no quota at all") are each rejected at render time. Each one produces a chart that installs cleanly and fails weeks later. **Compaction frees pages for reuse inside the backend file; it does not shrink the file.** The reported `dbSize` therefore plateaus rather than dropping, while `dbSizeInUse` falls back to the size of the live keyspace. That is the expected behavior and is sufficient to stay under the quota, because reclaimed pages are reused for new writes. Only `etcdctl defrag` returns space to the filesystem, and this template does not automate it — defragmentation blocks the member it runs on. Testing measured the mechanism directly on a shortened retention window: the compacted revision advanced from 1 to 4001, `dbSizeInUse` collapsed from 5.76 MB to 20,480 bytes while `dbSize` stayed at about 5.8 MB, and reads of pre-compaction revisions then failed with `required revision has been compacted`. On this template's own three-location run at the default `1h`, compaction fired at the one-hour mark, freed 569 KB, and `dbSize` stayed flat afterwards under load. The multi-day `dbSize` plateau is **derived from that mechanism rather than observed** over days. `etcdctl endpoint status` reports `QUOTA | 0 B` when `quotaBackendBytes` is `0`, because that column reflects the flag rather than the effective limit. The effective quota in that case is still etcd's 2 GiB default, which etcd logs at startup as `enabled backend quota with default value`. ### Internal Access The `internalAccess` section controls which workloads can reach the client API on `2379`. Cross-location traffic inside one GVC is same-GVC traffic, so the default covers a stretched cluster with no extra rule. | Type | Description | | --------------- | ---------------------------------------------------------------------------------- | | `same-gvc` | Allow access from all workloads in the same GVC (recommended) | | `same-org` | Allow access from all workloads in the same organization | | `workload-list` | Allow access only from the specific workloads listed in `internalAccess.workloads` | There is deliberately no public access: etcd in this template runs without TLS and without authentication. Anything permitted by `internalAccess` has full read/write access to the entire keyspace. Use `workload-list` if the GVC contains workloads that should not have it. Firewall changes take up to roughly two and a half minutes to take effect. Re-test after waiting rather than concluding the setting was ignored. ### Connecting to etcd | Target | Address | | ------------------------------------------ | --------------------------------------------------------------- | | Client API, load-balanced across locations | `RELEASE_NAME-etcd.GVC_NAME.cpln.local:2379` | | A specific location's member | `replica-0.RELEASE_NAME-etcd.LOCATION.GVC_NAME.cpln.local:2379` | | Raft peer traffic (members only) | Port `2380` | Point clients at one endpoint per location so they can fail over. From inside the cluster: ```bash theme={null} cpln workload exec RELEASE_NAME-etcd --gvc GVC_NAME --container etcd -- etcdctl member list -w table cpln workload exec RELEASE_NAME-etcd --gvc GVC_NAME --container etcd -- etcdctl endpoint status --cluster -w table cpln workload exec RELEASE_NAME-etcd --gvc GVC_NAME --container etcd -- etcdctl put /demo/key hello cpln workload exec RELEASE_NAME-etcd --gvc GVC_NAME --container etcd -- etcdctl --command-timeout=5s get /demo/key ``` Every member is named `RELEASE_NAME-etcd-LOCATION`, so `member list` maps one-to-one onto your location list. Allow about two minutes of convergence after a cold install before concluding that a member is unreachable — cross-region service discovery can lag a workload reporting ready by well over a minute. ### Ports | Port | Protocol | Description | | ------ | -------- | --------------------------------- | | `2379` | TCP | etcd client API (internal only) | | `2380` | TCP | Raft peer traffic between members | ## Failover Behavior Measured on a three-location cluster (`aws-us-east-1`, `aws-eu-central-1`, `aws-us-west-2`) with the default 5 s election timeout: | Event | Result | | -------------------- | ------------------------------------------------------------------------------------------------- | | Idle stability | 10.8 hours with zero spontaneous elections | | Leader crash | New leader elected in **5.59 s**, with 0 failed writes on a client pinned to a surviving follower | | Planned replica stop | Leadership handed over in **502 ms**, 0 failed writes across 570 samples | Write latency is bounded by one cross-region round trip, because the leader needs a follower acknowledgement before it can commit. Measured with the leader in `aws-eu-central-1`: | Client location | Write latency (p50) | | ----------------------------------------------- | ------------------- | | `aws-eu-central-1` (co-located with the leader) | 95.7 ms | | `aws-us-east-1` | 185.7 ms | | `aws-us-west-2` (furthest from the leader) | 236.4 ms | A client co-located with the leader does not get local-write latency — one cross-region round trip is the floor for any stretched quorum. ## Upgrades A `helm upgrade` takes the whole cluster down for about **66 seconds** (measured on three locations). Members in every location restart together, quorum is lost, and writes time out until it returns. The cluster recovers on its own, but every configuration change — including one that only changes a firewall rule — costs that window, so plan upgrades as a short planned outage. Nothing serializes the restart: the field that would limit it (`rolloutOptions.maxUnavailableReplicas`) is not retained by the platform, so the chart deliberately does not set it. Running more members per location has not been tested and should not be assumed to help. ## Behavior Under Quorum Loss When a majority of members is unreachable, the cluster stops committing writes. What that looks like from a client is easy to misread: * **Writes hang rather than failing fast.** They block until they time out, so always give clients a short `--command-timeout` (or the client-library equivalent) — otherwise connections pile up against a cluster that cannot commit. * **Serializable reads keep succeeding against stale data.** A read issued with `--consistency=s` is served from the local member's own store and never notices the loss of quorum. * **`IS LEADER: true` is not proof of leadership.** An isolated survivor keeps reporting itself as leader for about 6 seconds while unable to commit anything. Health-check with a linearizable read (`etcdctl get KEY`, without `--consistency=s`), never with `endpoint status`. Never suspend a location for this workload. Suspending and resuming a location permanently withdraws that location's endpoints from the other locations' service discovery while every status surface still reports healthy. Add or remove locations by editing `global.gvc.locations` instead. ## Recovering from a Lost Location `recovery.forceNewClusterInLocation` is for the case where quorum is **permanently** gone — with two locations, that is the loss of either one. It is not needed for a location that is coming back: a member that returns with its volume intact rejoins on its own in well under a minute. `etcdctl member remove` cannot help here, because removing a member itself requires quorum. Set `recovery.forceNewClusterInLocation` to the **surviving** location's name and upgrade the release. That member restarts as a single-member cluster rebuilt from its own write-ahead log and serves writes again immediately. Set `recovery.forceNewClusterInLocation` back to `""` and upgrade again. Leaving it set means the flag fires on every future restart of that member. The evicted members still hold the old cluster ID and refuse to start until their data directory is cleared. Uninstall and reinstall, or delete that volume, so the member bootstraps fresh. Never set `recovery.forceNewClusterInLocation` to more than one location, and never leave it set. Two members both forcing a new cluster produce two divergent single-member clusters that cannot be merged. ## If the Backend Quota Fills Every version of this template compacts, so a default install does not accumulate revisions in the first place. A backend can still reach the quota if the keyspace itself is genuinely large, or if `autoCompactionRetention` has been raised far enough that the retained history outgrows 2 GiB. **Turning compaction on, or tightening it, cannot rescue a cluster that has already filled its backend.** Compaction stops further growth; it never shrinks an existing backend file. Once etcd has raised a `NOSPACE` alarm, writes stay rejected until an operator compacts, defragments each member and disarms the alarm — a `helm upgrade` does none of that. The symptom usually surfaces in the client rather than in etcd. A Patroni replica that cannot renew its lease exits *cleanly*, so it restart-loops with `exitCode: 0` and `reason: Completed` and a climbing restart count — which reads as healthy and gets misdiagnosed as a database fault. Check etcd first. Both inspection commands are read-only and safe to run on a live cluster: ```bash theme={null} cpln workload exec RELEASE_NAME-etcd --gvc GVC_NAME --container etcd -- etcdctl endpoint status --cluster -w table cpln workload exec RELEASE_NAME-etcd --gvc GVC_NAME --container etcd -- etcdctl alarm list ``` Add `--location LOCATION` to target a specific location's member; without it the CLI picks one for you and names it in its output. `endpoint status --cluster` reports every member from whichever one you reach. A `DB SIZE` close to 2.1 GB on every member, plus `NOSPACE` in the alarm list, confirms it. A healthy cluster prints nothing at all for `alarm list`. Recovery from there — compacting to a revision, defragmenting each member, then disarming the alarm — is an operator procedure this template deliberately does not perform, because each step is disruptive and the order matters. Follow etcd's [maintenance guide](https://etcd.io/docs/v3.6/op-guide/maintenance/) and plan it as a maintenance window. ## External References Official etcd documentation Compaction, defragmentation, and clearing a NOSPACE alarm Heartbeat and election timeout guidance for cross-region clusters Recovering an etcd cluster that has lost quorum View the source files, default values, and chart definition # FusionAuth Source: https://docs.controlplane.com/template-catalog/templates/fusionauth Deploy FusionAuth on Control Plane using the Template Catalog. Covers configuration, volumes, scaling, and identity management with PostgreSQL and optional backups. ## Overview FusionAuth is a modern, self-hosted identity and access management platform that provides user authentication, authorization, and secure single sign-on. It supports OAuth2, OpenID Connect, and SAML. This template deploys FusionAuth with a PostgreSQL database that is automatically configured and connected — no manual database setup required. ### What Gets Created * **FusionAuth Workload** — The main identity platform with configurable CPU and memory. * **PostgreSQL Workload** — The backing database, automatically connected to FusionAuth on startup. * **Volume Set** — Persistent storage for PostgreSQL data, with optional autoscaling. * **Backup Cron Job** (optional) — A scheduled job that dumps the PostgreSQL database to an S3 or GCS bucket when `postgres.backup.enabled` is `true`. * **Secrets** — An opaque PostgreSQL credentials secret, and a startup script secret that waits for database readiness before launching FusionAuth. * **Identity & Policy** — An identity bound to the workloads with `reveal` access to the PostgreSQL credentials and startup script secrets. When backup is enabled, the identity also provides the backup cron job with access to the configured object storage bucket. This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites Prerequisites are only required if you plan to enable PostgreSQL backups (`postgres.backup.enabled: true`). Skip this section if backups are not needed. ### AWS S3 1. Create an S3 bucket. Set `postgres.backup.aws.bucket` and `postgres.backup.aws.region` in your values file. 2. If you do not have a Control Plane Cloud Account set up, follow the [Create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) guide. Set `postgres.backup.aws.cloudAccountName` to the name of your Cloud Account. 3. Create an IAM policy with the following JSON, replacing `YOUR_BUCKET_NAME`: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket", "s3:GetObjectVersion", "s3:DeleteObjectVersion" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET_NAME", "arn:aws:s3:::YOUR_BUCKET_NAME/*" ] } ] } ``` 4. Set `postgres.backup.aws.policyName` to the name of the policy created in step 3. ### GCS 1. Create a GCS bucket. Set `postgres.backup.gcp.bucket` in your values file. 2. If you do not have a Control Plane Cloud Account set up, follow the [Create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) guide. Set `postgres.backup.gcp.cloudAccountName` to the name of your Cloud Account. 3. Add the **Storage Admin** role to the GCP service account associated with the Cloud Account. ## Installation To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} resources: cpu: 512m memory: 1024Mi firewall: external: inboundAllowCIDR: - 0.0.0.0/0 outboundAllowCIDR: [] # Set to 0.0.0.0/0 to allow outbound traffic if communicating with an IdP internal: type: same-gvc # options: same-gvc, same-org, workload-list workloads: # Note: can only be used if type is same-gvc or workload-list #- //gvc/GVC_NAME/workload/WORKLOAD_NAME # Database configuration postgres: image: postgres:18 config: username: username password: password database: test resources: minCpu: 200m minMemory: 128Mi maxCpu: 500m maxMemory: 256Mi volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) autoscaling: enabled: false maxCapacity: 100 # Maximum capacity in GiB minFreePercentage: 10 # Trigger scaling when free space drops below this percentage scalingFactor: 1.2 # Multiply current capacity by this factor when scaling up internalAccess: type: same-gvc # options: none, same-gvc, same-org, workload-list workloads: # Note: can only be used if type is same-gvc or workload-list #- //gvc/GVC_NAME/workload/WORKLOAD_NAME # Postgres backup configuration (compatible with Postgres 17+) backup: enabled: false image: controlplanecorporation/pg-backup:18.1.0 # tag 18.1.0 = Postgres 18, 17.1.0 = Postgres 17 schedule: "0 2 * * *" # daily at 2am UTC resources: cpu: 100m memory: 128Mi provider: aws # Options: aws or gcp aws: bucket: my-backup-bucket region: us-east-1 cloudAccountName: my-backup-cloudaccount policyName: my-backup-policy prefix: postgres/backups # Folder path within the bucket gcp: bucket: my-backup-bucket cloudAccountName: my-backup-cloudaccount prefix: postgres/backups ``` ### FusionAuth Resources * `resources.cpu` / `resources.memory` — CPU and memory allocated to the FusionAuth workload. ### Firewall * `firewall.external.inboundAllowCIDR` — CIDR ranges allowed to reach FusionAuth from the internet (default: `0.0.0.0/0`). * `firewall.external.outboundAllowCIDR` — CIDR ranges FusionAuth is allowed to reach externally. Empty by default. Set to `0.0.0.0/0` (or a specific CIDR) if FusionAuth needs to communicate with an external Identity Provider such as Google OAuth. * `firewall.internal.type` — Controls which internal workloads can reach FusionAuth (`same-gvc`, `same-org`, or `workload-list`). ### PostgreSQL * `postgres.config.username` / `postgres.config.password` — Database credentials. **Change before deploying to production.** * `postgres.config.database` — Name of the database created on startup. These values are only applied on first startup when the data directory is empty. Updating them after the initial deployment will have no effect on the running database. To change credentials or the database name on an existing instance, use PostgreSQL's native commands (e.g. `ALTER USER`, `ALTER DATABASE`). * `postgres.resources` — Min/max CPU and memory bounds for the PostgreSQL workload. * `postgres.internalAccess.type` — Controls which workloads can reach PostgreSQL. Set to `none` to isolate replicas from each other, or use `same-gvc`, `same-org`, or `workload-list`. ### PostgreSQL Storage * `postgres.volumeset.capacity` — Initial volume size in GiB (minimum 10). * `postgres.volumeset.autoscaling.enabled` — Automatically expand the volume as it fills. When enabled: * `maxCapacity` — Maximum volume size in GiB. * `minFreePercentage` — Trigger a scale-up when free space drops below this percentage. * `scalingFactor` — Multiply the current capacity by this factor when scaling up. ### PostgreSQL Backup Set `postgres.backup.enabled: true` to enable scheduled database dumps to object storage. The backup image tag corresponds to the PostgreSQL version (`18.1.0` for Postgres 18, `17.1.0` for Postgres 17). Set `postgres.backup.provider` to `aws` or `gcp` and fill in the corresponding section. The `prefix` field controls the folder path within the bucket where backups are stored. ### Restoring a Backup To restore from a backup, run the following from a client with access to the bucket: **AWS S3** ```bash theme={null} export PGPASSWORD="PASSWORD" aws s3 cp "s3://BUCKET_NAME/PREFIX/BACKUP_FILE.sql.gz" - \ | gunzip \ | psql \ --host=WORKLOAD_NAME \ --port=5432 \ --username=USERNAME \ --dbname=postgres unset PGPASSWORD ``` **GCS** ```bash theme={null} export PGPASSWORD="PASSWORD" gsutil cp "gs://BUCKET_NAME/PREFIX/BACKUP_FILE.sql.gz" - \ | gunzip \ | psql \ --host=WORKLOAD_NAME \ --port=5432 \ --username=USERNAME \ --dbname=postgres unset PGPASSWORD ``` ### Post-Deployment Setup After deployment, open the FusionAuth admin panel to complete setup: 1. Use the setup wizard to create your application. 2. Configure your application with the `origin`, `redirect`, and `logout` URLs for your code. 3. Set the tenant issuer to match your deployment URL (e.g., `my-fusionauth-app.io`). 4. If using an external Identity Provider, configure it in the admin panel and ensure `firewall.external.outboundAllowCIDR` allows traffic to the IdP. ## External References Official FusionAuth documentation FusionAuth OAuth2 and OpenID Connect guide Create a Control Plane Cloud Account for backup storage access Source code for the PostgreSQL backup container image View the source files, default values, and chart definition # Ghost Source: https://docs.controlplane.com/template-catalog/templates/ghost Deploy Ghost, the open-source publishing platform for blogs, newsletters, and paid memberships, on Control Plane. Covers the bundled MySQL 8 database, persistent content storage, public HTTPS access, SMTP email, and optional scheduled database backups. ## Overview Ghost is the open-source publishing platform for professional blogs, newsletters, and paid memberships, with a first-class editor and REST Content/Admin APIs. This template deploys a single stateful Ghost workload backed by a bundled MySQL 8 database, with durable content storage, an HTTPS public site, optional SMTP email, and optional scheduled database backups to object storage. ### Architecture * **Ghost** — Stateful, single-replica workload serving the site, editor, and Content/Admin APIs on port `2368`. Boots through a startup script that sets the public `url` from the canonical endpoint. * **MySQL 8** — Backing database provisioned from the [mysql](/template-catalog/templates/mysql) template as a subchart (image pinned to `mysql:8` — the only database Ghost supports) and connected to Ghost automatically. ### What Gets Created * **Stateful Ghost Workload** — The Ghost server with configurable CPU and memory. * **Stateful MySQL Workload** — Single-replica MySQL 8, automatically connected to Ghost. * **Volume Sets** — A persistent volume set for Ghost content (`/var/lib/ghost/content`: uploaded images, themes, logs, adapters) and one for MySQL data. * **Secrets** — An opaque secret holding the startup script and the MySQL credentials secret. * **Identity & Policy** — An identity bound to the Ghost workload with `reveal` access scoped to exactly the secrets it mounts (plus your SMTP secret when mail is enabled). * **Cron Backup Workload** *(optional)* — Scheduled MySQL dumps to object storage, created only when `mysql.backup.enabled` is `true`. This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites None for a default install — it deploys with a working MySQL 8 backend and an auto-assigned HTTPS endpoint. Two optional features need setup before installing: * **SMTP email** — for member sign-in links and newsletters, create a dictionary secret with keys `user` and `password` first (see [Mail](#mail-smtp)). * **Database backups** — an AWS S3 or GCS bucket plus a Control Plane Cloud Account (see [Backing Up](#backing-up)). Install using your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration Key configuration values (see the template's `values.yaml` for the complete set): ```yaml theme={null} image: ghost:6.54.1-alpine # official Docker Hub image (library/ghost) resources: # Ghost (Node) app — single instance cpu: 500m memory: 1024Mi minCpu: 250m minMemory: 512Mi # Persistent content: uploaded images, themes, logs, adapters volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) # Public URL Ghost uses to build links and emails. Leave empty to auto-derive # the canonical *.cpln.app endpoint. Set to your custom domain once attached. publicUrl: "" # e.g. https://blog.example.com # Mail (SMTP) — optional. DISABLED while secretName is empty (email off). mail: secretName: "" # e.g. my-ghost-smtp (dictionary secret with keys: user, password) host: "" # e.g. smtp.mailgun.org port: 587 # 465 = SSL, 587 = STARTTLS secure: false # true for port 465 from: "" # e.g. "Ghost " publicAccess: enabled: true # HTTPS site via the canonical *.cpln.app endpoint internalAccess: type: same-gvc # options: none, same-gvc, same-org, workload-list workloads: [] # only used when type is same-gvc or workload-list # Backing database: MySQL 8 (bundled via the mysql template) mysql: image: mysql:8 # Ghost supports ONLY MySQL 8 — do not change to 9 or MariaDB enablePhpMyAdmin: false config: db: ghost user: ghost password: change-me-ghost-db # change before installing rootPassword: change-me-mysql-root # change before installing resources: minCpu: 150m maxCpu: 500m minMemory: 256Mi maxMemory: 1024Mi volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) internalAccess: type: same-gvc backup: enabled: false # scheduled DB dumps to object storage — see Backing Up schedule: "0 2 * * *" # daily at 2am UTC provider: aws # aws or gcp aws: bucket: my-backup-bucket region: us-east-1 cloudAccountName: my-backup-cloudaccount policyName: my-backup-policy prefix: ghost/backups gcp: bucket: my-backup-bucket cloudAccountName: my-backup-cloudaccount prefix: ghost/backups ``` ### Ghost Application * `image` — The official Ghost image from Docker Hub (`library/ghost`, Alpine variant). * `resources` — CPU and memory bounds for the Ghost workload. * `volumeset.capacity` — Initial content volume size in GiB (minimum 10). Holds uploaded images, themes, logs, and adapters at `/var/lib/ghost/content`. ### Site URL * `publicUrl` — The public URL Ghost uses to build page links and email links. Leave empty (default) to auto-derive the canonical `*.cpln.app` endpoint at boot. Set it to your custom domain (e.g. `https://blog.example.com`) once you attach one, so links and emails point at the right host. ### Mail (SMTP) Email powers member sign-in links, staff invitations, and newsletters. It is **off by default** — the entire mail configuration is gated on `mail.secretName` being non-empty. To enable it, create a dictionary secret with your SMTP credentials **before** installing: ```bash theme={null} cpln secret create --name my-ghost-smtp --type dictionary \ --data 'user=smtp-username,password=smtp-password' ``` Then set: * `mail.secretName` — The name of the dictionary secret (keys `user` and `password`). The template grants the Ghost identity `reveal` on exactly this secret. * `mail.host` — Your SMTP server hostname (e.g. `smtp.mailgun.org`). * `mail.port` / `mail.secure` — `587` with `secure: false` for STARTTLS (default), or `465` with `secure: true` for SSL. * `mail.from` — The From address for outgoing mail (e.g. `"Ghost "`). ### Access * `publicAccess.enabled` — Serve the site and admin over HTTPS on the auto-assigned `*.cpln.app` canonical endpoint (default). Set to `false` for an internal-only instance — Ghost then derives its URL from the internal DNS name. * `internalAccess.type` — Controls which workloads can reach Ghost over the internal network: | Type | Description | | --------------- | ---------------------------------------------------------------------- | | `none` | No internal access. | | `same-gvc` | Allow access from all workloads in the same GVC (default). | | `same-org` | Allow access from all workloads in the same organization. | | `workload-list` | Allow access only from workloads listed in `internalAccess.workloads`. | ### Backing Database * `mysql.image` — Pinned to `mysql:8`. Ghost supports **only MySQL 8** — not MySQL 9 and not MariaDB. Do not change this. * `mysql.config.db` / `user` / `password` / `rootPassword` — Credentials for the bundled MySQL, applied on first startup. **Change both passwords before installing.** * `mysql.resources` / `mysql.volumeset.capacity` — CPU/memory bounds and initial volume size for the MySQL workload. * `mysql.internalAccess.type` — Controls which workloads can reach MySQL directly. * `mysql.enablePhpMyAdmin` — Off by default to keep the footprint to Ghost plus its database. MySQL credentials are only applied on first startup when the data directory is empty. Changing them in values after the initial deployment has no effect on the running database. To fully reset, `helm uninstall` (which deletes the volume sets) and reinstall. ## Connecting | Access | Endpoint | Notes | | ----------------- | ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Public site | `https://.cpln.app` | Auto-assigned when `publicAccess.enabled`. Find it under `status.canonicalEndpoint` (`cpln workload get -ghost -o yaml`). | | Admin panel | `https://.cpln.app/ghost` | The Ghost editor and settings. | | Owner account | First visit to `/ghost` | Created via the setup wizard on first visit — there are no bootstrap credentials. | | Internal (in-GVC) | `http://-ghost..cpln.local:2368` | Reachable from other workloads per `internalAccess.type`. Send an `X-Forwarded-Proto: https` header — Ghost redirects plain-HTTP requests to its configured URL. | | Database | `-mysql..cpln.local:3306` | Credentials live in the `-mysql-config` secret. | After deploy, open `https://.cpln.app/ghost` in a browser to create the owner account, then start publishing. Until the owner exists, the site serves the default theme with no admin user. ## Backing Up Database backups are optional and disabled by default. When enabled, a cron workload runs a scheduled `mysqldump` of the Ghost database and uploads it (gzipped) to your bucket under the configured prefix. Enable with `mysql.backup.enabled: true`, pick a `mysql.backup.provider`, and complete the storage setup below **before** installing. Create an S3 bucket. Set `mysql.backup.aws.bucket` and `mysql.backup.aws.region` to match. If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) for your AWS account. Set `mysql.backup.aws.cloudAccountName` to its name. Create an IAM policy granting the required S3 actions on the bucket, and set `mysql.backup.aws.policyName` to its name: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["s3:ListBucket"], "Resource": "arn:aws:s3:::my-backup-bucket" }, { "Effect": "Allow", "Action": ["s3:PutObject", "s3:GetObject", "s3:DeleteObject"], "Resource": "arn:aws:s3:::my-backup-bucket/*" } ] } ``` Create a GCS bucket. Set `mysql.backup.gcp.bucket` to its name. If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) for your GCP project and grant its service account **Storage Object Admin** (`roles/storage.objectAdmin`) on the bucket. Set `mysql.backup.gcp.cloudAccountName` to its name. ### Restoring a Backup Backups are standard gzipped `mysqldump` archives named `mysql-.sql.gz`. To restore, download the archive from your bucket and load it into a MySQL 8 instance: ```bash theme={null} gunzip -c mysql-.sql.gz | mysql -u root -p ``` The dump recreates the full `ghost` database — posts, members, settings, and users. Uploaded images and themes live on the Ghost content volume set, not in the database, so a database restore covers content and configuration but not media files. ## Important Notes * **Single replica by design.** Ghost has no upstream clustering support, so there is no `replicas` knob. Durability comes from the MySQL backend, the content volume set, and optional scheduled backups. Put a CDN in front of a busy public site to absorb the brief blip during a restart or upgrade. * **MySQL 8 only.** Ghost does not support MySQL 9 or MariaDB — keep `mysql.image: mysql:8`. * **Create the owner account first.** After deploy, visit `/ghost` to run the setup wizard. Until then the site is public with the default theme and no admin exists. * **Change both database passwords before installing.** They seed the database on first boot and cannot be changed by editing values afterward — uninstalling (which deletes the volume sets) and reinstalling is the reset path. * **SMTP is a prerequisite secret, not a value.** Create the dictionary secret (keys `user`, `password`) before install and reference it via `mail.secretName`. Leaving it empty keeps email fully off. * **Set `publicUrl` when using a custom domain** so page links and emails point at the right host. Empty auto-derives the canonical `*.cpln.app` endpoint. * **Content survives redeploys** under the same release name — posts and settings in MySQL, media on the content volume set. Uninstalling deletes both volume sets and all data. ## External References Official Ghost product documentation Full reference of Ghost configuration options Why Ghost requires MySQL 8 The library/ghost image on Docker Hub View the source files, default values, and chart definition # Gitea Source: https://docs.controlplane.com/template-catalog/templates/gitea Deploy Gitea, a lightweight self-hosted Git service, on Control Plane. Covers repositories, pull requests, issues, the package registry, PostgreSQL backing, public HTTPS access, and optional Git-over-SSH. ## Overview Gitea is a lightweight, self-hosted Git service — repositories, pull requests, issues, and a built-in package registry — backed by PostgreSQL. This template deploys a single Gitea server with persistent storage, an automatically wired PostgreSQL database, a public HTTPS web UI with Git-over-HTTPS, and an optional Git-over-SSH endpoint. ### Architecture * **Gitea** — Stateful, single-replica workload running the rootless image. Serves the web UI, Git-over-HTTPS, and the package registry on port 3000, plus the built-in SSH server on 2222. * **PostgreSQL** — Backing database provisioned from the `postgres` template as a subchart and connected to Gitea on startup. ### What Gets Created * **Stateful Gitea Workload** — The Gitea server with configurable CPU and memory, bootstrapped with an admin account on first boot. * **Stateful PostgreSQL Workload** — Single-replica Postgres, automatically connected to Gitea. * **Volume Sets** — A persistent volume set for Gitea (`/var/lib/gitea`: repositories, LFS objects, attachments, and SSH host keys) and one for PostgreSQL data, both with optional autoscaling. * **Secrets** — A dictionary secret holding the stable app secrets and admin credentials, an opaque secret holding the admin-bootstrap script, and the PostgreSQL credentials secret. * **Identity & Policy** — An identity bound to the Gitea workload with `reveal` access scoped to exactly its two secrets plus the PostgreSQL config secret. * **Direct Load Balancer** *(optional)* — A raw-TCP port for Git-over-SSH, created only when `ssh.enabled` is `true`. This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites None for a default install. Before installing, change the admin password and generate the three security values (see [Important Notes](#important-notes)). Install using your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration Key configuration values (see the template's `values.yaml` for the complete set): ```yaml theme={null} image: gitea/gitea:1.27.1-rootless # rootless variant: runs as UID 1000, built-in SSH server on 2222 resources: minCpu: 250m minMemory: 512Mi maxCpu: 1000m maxMemory: 1024Mi # Storage (repos, LFS, attachments, SSH host keys) volumeset: capacity: 20 # initial capacity in GiB (minimum is 10) autoscaling: enabled: false maxCapacity: 200 minFreePercentage: 10 scalingFactor: 1.2 # Gitea admin + app secrets (template-scoped — CHANGE THESE) gitea: admin: username: gitea_admin password: change-me-admin-pass # CHANGE before install — the first admin login email: admin@example.com security: # Stable per install. CHANGE THESE. Generate each with: # gitea generate secret SECRET_KEY | INTERNAL_TOKEN | JWT_SECRET # Do NOT rotate after install — changing SECRET_KEY makes existing encrypted data unreadable. secretKey: REPLACE_WITH_gitea_generate_secret_SECRET_KEY internalToken: REPLACE_WITH_gitea_generate_secret_INTERNAL_TOKEN jwtSecret: REPLACE_WITH_gitea_generate_secret_JWT_SECRET disableRegistration: true # true = admin-invite only; false = allow open self-registration # Access publicAccess: enabled: true # HTTPS web UI + Git-over-HTTPS on the auto *.cpln.app endpoint # Git-over-SSH (optional, OFF by default — see Important Notes) ssh: enabled: false # false = Git-over-HTTPS only (public web UI stays up); true = public SSH takes the endpoint externalPort: 22 # public port clients connect to (also advertised in SSH clone URLs) domain: "" # advertised SSH host in clone URLs; empty = use the web domain internalAccess: type: same-gvc # options: none, same-gvc, same-org, workload-list workloads: # only used when type is same-gvc or workload-list #- //gvc/GVC_NAME/workload/WORKLOAD_NAME # Backing database (postgres subchart) postgres: image: postgres:18 config: username: gitea password: change-me-db-pass database: gitea resources: minCpu: 200m minMemory: 256Mi maxCpu: 500m maxMemory: 512Mi volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) internalAccess: type: same-gvc ``` ### Image and Resources * `image` — The Gitea image. The template uses the **rootless** variant, which runs as UID 1000 and serves SSH on port 2222 inside the container. * `resources` — Min/max CPU and memory bounds for the Gitea workload. ### Storage * `volumeset.capacity` — Initial Gitea volume size in GiB (minimum 10). Holds repositories, LFS objects, attachments, and SSH host keys. * `volumeset.autoscaling.enabled` — Automatically expand the volume as it fills. When enabled: * `maxCapacity` — Maximum volume size in GiB. * `minFreePercentage` — Trigger a scale-up when free space drops below this percentage. * `scalingFactor` — Multiply the current capacity by this factor when scaling up. ### Admin and Security * `gitea.admin.username` / `gitea.admin.password` / `gitea.admin.email` — The site administrator account, bootstrapped on first boot. **Change the password before installing.** * `gitea.security.secretKey` / `internalToken` / `jwtSecret` — Stable per-install app secrets. Generate each with `gitea generate secret SECRET_KEY` (and `INTERNAL_TOKEN`, `JWT_SECRET`). These are values (not auto-generated) so they stay stable across upgrades. **Never rotate `secretKey` after install** — doing so makes all encrypted data (2FA secrets, tokens, mirror credentials) permanently unreadable. * `gitea.disableRegistration` — `true` (default) restricts new accounts to admin invites; `false` allows open self-registration. ### Access * `publicAccess.enabled` — Exposes the HTTPS web UI, Git-over-HTTPS, and the package registry on the auto-assigned `*.cpln.app` canonical endpoint. * `ssh.enabled` — Exposes Git-over-SSH via a direct TCP load balancer. **OFF by default** — see [Important Notes](#important-notes) for the trade-off with the public web UI. * `ssh.externalPort` — The public SSH port clients connect to; also advertised in SSH clone URLs (default `22`). * `ssh.domain` — The SSH host advertised in clone URLs. Empty uses the web domain. * `internalAccess.type` — Controls which workloads can reach Gitea over the internal network (`none`, `same-gvc`, `same-org`, or `workload-list`). ### Backing Database * `postgres.config.username` / `password` / `database` — Credentials for the bundled PostgreSQL, applied on first startup. **Change the password before deploying to production.** * `postgres.resources` — Min/max CPU and memory bounds for the PostgreSQL workload. * `postgres.volumeset.capacity` — Initial Postgres volume size in GiB (minimum 10). * `postgres.internalAccess.type` — Controls which workloads can reach PostgreSQL. PostgreSQL credentials are only applied on first startup when the data directory is empty. Changing them after the initial deployment has no effect on the running database — use PostgreSQL's native commands (e.g. `ALTER USER`) instead. ## Connecting | Access | Endpoint | Notes | | ---------------------------------- | --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | | Web UI + Git-over-HTTPS + registry | `https://.cpln.app` | Auto-assigned when `publicAccess.enabled`. Find it under `status.canonicalEndpoint` (`cpln workload get -gitea -o yaml`). | | Git-over-SSH | direct-LB address on `ssh.externalPort` | Only when `ssh.enabled`. The reachable host is the `loadBalancer.direct` address from the workload status. | | Internal (in-GVC) | `-gitea..cpln.local:3000` | Reachable from other workloads per `internalAccess.type`. | | Credentials | admin login | `gitea.admin.username` / `gitea.admin.password`. | Open the canonical endpoint in a browser and sign in with the admin credentials to create repositories, users, and organizations. Clone and push over HTTPS using the same endpoint. ## The SSH / HTTPS Endpoint Trade-off A Control Plane workload has exactly **one** `*.cpln.app` canonical endpoint, and it can serve **either** the HTTPS web UI (web UI + Git-over-HTTPS on port 443) **or** a raw-TCP load balancer for SSH (port 22) — **not both**. Enabling `ssh.enabled` repoints that single endpoint to SSH, which takes the public web UI on 443 offline. Because of this, **`ssh.enabled` is `false` by default**, which keeps the public web UI and Git-over-HTTPS working out of the box — all most users need. Git-over-HTTPS supports full clone, push, and pull, so SSH is not required for normal use. Enable `ssh.enabled: true` only if you either (a) serve the web UI through a **custom domain** (so the canonical endpoint is free for SSH), or (b) only need Git-over-SSH. Turning it on repoints the public `*.cpln.app` endpoint to SSH on port 22 and makes the public HTTPS web UI on 443 unreachable. ## Important Notes * **Change `gitea.admin.password` and the three `gitea.security.*` values before installing.** The shipped defaults are illustrative placeholders and are insecure as-is. Generate each security value with `gitea generate secret SECRET_KEY` (and `INTERNAL_TOKEN`, `JWT_SECRET`). * **Never rotate `gitea.security.secretKey` after install.** Changing it makes all encrypted data (2FA secrets, tokens, mirror credentials) permanently unreadable. Because it is a value rather than auto-generated, `helm upgrade` keeps it stable. * **Public SSH and the public web UI cannot share one endpoint** — SSH is off by default. See [The SSH / HTTPS Endpoint Trade-off](#the-ssh-%2F-https-endpoint-trade-off) above. * **Single replica only.** A rolling restart or upgrade incurs brief downtime. Do not raise the workload scale above 1 — replicas would each get separate repo volumes and corrupt state. * **Data lives on the volume set** and survives redeploys under the same release name. Change admin credentials after first boot in the Gitea UI, not via values — the data directory keeps the original account. To fully reset, `helm uninstall` (which deletes the volume set) then reinstall. ## External References Official Gitea product documentation Full reference of Gitea configuration options Using Gitea's built-in package registry View the source files, default values, and chart definition # GlitchTip Source: https://docs.controlplane.com/template-catalog/templates/glitchtip Deploy GlitchTip on Control Plane using the Template Catalog. Open-source, Sentry-compatible error tracking with a stateless web tier, a background worker, Redis/Sentinel task queue, and a highly available PostgreSQL backend. Covers database and queue modes, the optional SMTP prerequisite, closed registration, and backups. ## Overview GlitchTip is open-source, Sentry-API-compatible error tracking — applications report crashes and exceptions with standard Sentry SDKs pointed at a GlitchTip DSN, and GlitchTip groups them into issues with alerting. It is fully MIT-licensed with nothing feature-gated; there is no paid tier. This template deploys GlitchTip backed by a highly available PostgreSQL cluster and a Redis/Sentinel queue by default. The web UI and SDK event-ingest endpoint are served on one public HTTPS endpoint, and the admin account is created automatically on first boot — so there is never an unauthenticated setup page and self-signup is closed by default. ### Architecture * **GlitchTip web** — A stateless standard workload (default 1 replica, `replicas` knob for more) serving the UI, API, and SDK event ingest on port `8000`. * **GlitchTip worker** — A single-replica standard workload running the background task worker and scheduler; it also runs database migrations and the superuser bootstrap at boot. It is a fixed singleton and is not scaled. * **PostgreSQL (HA, default)** — The [postgres-highly-available](/template-catalog/templates/postgres-highly-available) template as a subchart: 3× Patroni PostgreSQL, 3× etcd, and a HAProxy leader-routing endpoint GlitchTip connects through. Holds all issue and event data. * **PostgreSQL (dev/lightweight, optional)** — The single-instance [postgres](/template-catalog/templates/postgres) template instead, for lighter non-HA deployments. * **Redis + Sentinel (default, optional)** — The [redis](/template-catalog/templates/redis) template in Sentinel mode: the task queue, cache, and sessions. Disable it to run those on PostgreSQL instead (a lighter dev shape). ### What Gets Created * **Standard GlitchTip Web Workload** — The stateless web tier serving the UI, API, and SDK event ingest on port `8000` (`replicas` controls its scale). * **Standard GlitchTip Worker Workload** — A single-replica worker + scheduler that runs migrations and the admin bootstrap at boot. * **Database Workloads** — HA mode: a stateful Patroni PostgreSQL workload, a stateful etcd workload, and a standard HAProxy leader-routing workload. Single mode: one stateful PostgreSQL workload. * **Redis Workloads** *(default)* — A master-replica Redis deployment with Sentinel for failover. Omitted when `redis.enabled` is `false`. * **Volume Sets** — The database subchart's persistent volumes (10 GiB per replica by default), plus Redis persistence when enabled. The GlitchTip web and worker tiers have no volumes of their own. * **Secrets** — The Django `SECRET_KEY`, admin bootstrap credentials, two start scripts, and the database and Redis credentials from the subcharts. * **Identity & Policy** — A shared identity for the web and worker workloads and a least-privilege policy granting it `reveal` on exactly the secrets it uses (including the optional email secret when configured). This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites * **None for a default install.** * **Optional — outbound email** (member invites, alerts, password resets): create an [opaque secret](/guides/create-secret/opaque) in your org, with encoding `plain`, whose payload is a full email URL, for example `smtp://user:password@smtp.example.com:587`. Set its name in `email.secretName` and create it **before** installing. Leave `email.secretName` empty to run without email. * **Optional — database backups**: a bucket and access setup for one of the supported providers — see [Backing Up](#backing-up). Change `django.secretKey`, `admin.password`, the database password (`postgresHA.postgres.password` or `postgres.config.password`), and the Redis password (`redis.redis.auth.password.value`) from their placeholder defaults before installing. If you want outbound email, create the opaque secret described above and set `email.secretName` to its name before installing. Install the template using your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Choosing a Database Mode Exactly one of the two database modes must be enabled — the chart enforces this at render and fails the install with a clear message otherwise. | | `postgresHA` (default) | `postgres` | | -------------------- | ----------------------------------------------------------------------- | -------------------------------------- | | What runs | 3× Patroni PostgreSQL, 3× etcd, HAProxy leader endpoint | One single-replica PostgreSQL workload | | Database failover | Automatic (Patroni leader election) | None | | First-boot readiness | \~4–6 minutes (worker waits for the HA proxy to open before migrations) | Under a minute | | Best for | Production | Development and lightweight installs | ## Choosing a Queue Mode By default the [redis](/template-catalog/templates/redis) template is deployed and GlitchTip runs its task queue, cache, and sessions through Redis Sentinel. Set `redis.enabled: false` to run those on PostgreSQL instead — a lighter "lean" shape with no Redis workloads, at lower throughput. Both modes are supported upstream and are proven end to end. ## Configuration The default `values.yaml` for this template: ```yaml theme={null} image: glitchtip/glitchtip:6.2.2 replicas: 1 # web tier — stateless; set 2+ for high availability (state lives in PostgreSQL/Redis) resources: # web workload cpu: 1000m memory: 1Gi minCpu: 250m minMemory: 512Mi worker: # background worker + scheduler; runs migrations and admin bootstrap at boot (single replica) resources: cpu: 1000m memory: 1Gi minCpu: 250m minMemory: 512Mi concurrency: 20 # async tasks processed in parallel (VTASKS_CONCURRENCY) django: secretKey: change-me-glitchtip-secret-key # session/token signing key — change before installing; rotating later logs out all users admin: # superuser seeded on first boot only — log in with these, then manage in the UI email: admin@example.com password: change-me-glitchtip-admin # change before installing registration: enabled: false # open self-signup on the endpoint; admin-created users and invites work regardless email: secretName: "" # name of a pre-created opaque secret whose payload is an EMAIL_URL (create BEFORE install; empty = outbound email off) fromAddress: glitchtip@example.com # DEFAULT_FROM_EMAIL — used only when secretName is set domain: "" # full URL used in DSNs and email links (e.g. https://errors.example.com); empty = canonical *.cpln.app endpoint publicAccess: enabled: true # UI + SDK event ingest (DSN) on the canonical *.cpln.app HTTPS endpoint internalAccess: # internal firewall scope (in-GVC SDK callers) type: same-gvc # options: none, same-gvc, same-org, workload-list workloads: [] # used with workload-list redis: # task queue / cache / sessions (default) enabled: true # false = PostgreSQL carries the queue, cache, and sessions (lighter dev shape) redis: replicas: 2 auth: password: enabled: true value: change-me-glitchtip-redis # change before installing (any characters OK — the boot script percent-encodes it) firewall: internal_inboundAllowType: same-gvc persistence: enabled: true sentinel: replicas: 3 firewall: internal_inboundAllowType: same-gvc persistence: enabled: true postgresHA: # default: highly available PostgreSQL enabled: true postgres: username: glitchtip password: change-me-glitchtip-db-password # change before installing database: glitchtip replicas: 3 volumeset: capacity: 10 # initial capacity in GiB per replica (minimum is 10) backup: enabled: false # optional — see Backing Up mode: logical # logical or wal-g resources: cpu: 100m memory: 128Mi logical: image: ghcr.io/controlplane-com/backup-images/postgres-backup:17.1.0 schedule: "0 2 * * *" walg: intervalSeconds: 21600 provider: aws # options: aws, gcp, minio aws: bucket: glitchtip-pg-backup-bucket region: us-east-1 cloudAccountName: my-s3-cloud-account policyName: glitchtip-pg-backup-policy prefix: postgres/backups gcp: bucket: glitchtip-pg-backup-bucket cloudAccountName: my-gcs-cloud-account prefix: postgres/backups minio: endpoint: http://my-minio-workload:9000 bucket: glitchtip-pg-backup-bucket accessKey: my-minio-username secretKey: my-minio-password prefix: postgres/backups postgres: # dev/lightweight: single-instance PostgreSQL (disable postgresHA first) enabled: false config: username: glitchtip password: change-me-glitchtip-db-password # change before installing database: glitchtip volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) backup: enabled: false # optional — see Backing Up image: ghcr.io/controlplane-com/backup-images/postgres-backup:18.1.0 schedule: "0 2 * * *" resources: cpu: 100m memory: 128Mi provider: aws # options: aws, gcp, minio aws: bucket: glitchtip-pg-backup-bucket region: us-east-1 cloudAccountName: my-s3-cloud-account policyName: glitchtip-pg-backup-policy prefix: postgres/backups gcp: bucket: glitchtip-pg-backup-bucket cloudAccountName: my-gcs-cloud-account prefix: postgres/backups minio: endpoint: http://my-minio-workload:9000 bucket: glitchtip-pg-backup-bucket accessKey: my-minio-username secretKey: my-minio-password prefix: postgres/backups ``` ### GlitchTip * `image` — The GlitchTip container image (used by both the web and worker workloads). * `replicas` — Web-tier replica count. The web tier is stateless (all state is in PostgreSQL and Redis), so scaling to `2` or more gives high availability. The default `1` renders the proven single-replica shape. * `resources` — CPU and memory for the web container. * `worker.resources` — CPU and memory for the worker container. * `worker.concurrency` — Number of async tasks the worker processes in parallel (`VTASKS_CONCURRENCY`). * `django.secretKey` — Session and token signing key. **Change it before installing.** Rotating it later logs out all users but corrupts nothing. It is stored as a secret reference, never in plaintext. * `admin.email` / `admin.password` — The superuser account, seeded on first boot only. **Change `admin.password` before installing.** Log in with these, then manage users in the UI; changing the values later does not modify the existing account. * `registration.enabled` — Open self-signup on the public endpoint. Default `false` (closed) — admin-created users and invites still work either way. See [Onboarding Users](#onboarding-users). ### Email * `email.secretName` — Name of your pre-created opaque secret whose payload is an `EMAIL_URL` (see [Prerequisites](#prerequisites)). Empty (default) turns outbound email off. Member invites, alert notifications, and password-reset mail all require this to be set. * `email.fromAddress` — The `From` address used on outbound mail; applied only when `email.secretName` is set. ### Access * `publicAccess.enabled` — Serve the UI and SDK event ingest (the DSN endpoint) on the canonical `*.cpln.app` HTTPS endpoint (default). Because Sentry SDKs typically run outside the GVC, public access is on by default. Set to `false` for an internal-only instance (external requests are blocked at the edge; in-GVC callers still reach it per `internalAccess`). * `internalAccess.type` — Internal firewall scope of the web workload: | Type | Description | | --------------- | ---------------------------------------------------------------------- | | `none` | No internal access. | | `same-gvc` | Allow access from all workloads in the same GVC (default). | | `same-org` | Allow access from all workloads in the same organization. | | `workload-list` | Allow access only from workloads listed in `internalAccess.workloads`. | * `domain` — Full URL (for example `https://errors.example.com`) embedded in DSNs and email links. Empty (default) uses the canonical `*.cpln.app` endpoint, derived automatically at boot. ### Queue / Cache: Redis * `redis.enabled` — Deploy the Redis subchart in Sentinel mode for the task queue, cache, and sessions (default). Set to `false` to carry those on PostgreSQL instead — see [Choosing a Queue Mode](#choosing-a-queue-mode). * `redis.redis.replicas` — Redis master-replica count. * `redis.redis.auth.password.value` — The Redis password. Required when Redis is enabled (the chart enforces it). **Change it before installing** — any characters are fine, the boot script percent-encodes it into the connection URL. * `redis.sentinel.replicas` — Number of Sentinel instances. Sentinel authentication must stay disabled — GlitchTip cannot send a Sentinel password; the same-GVC firewall is the boundary there. * `redis.redis.persistence.enabled` / `redis.sentinel.persistence.enabled` — Persistent storage for Redis and Sentinel. ### Database Enable exactly one of `postgresHA` (production, default) or `postgres` (dev/lightweight) — see [Choosing a Database Mode](#choosing-a-database-mode). In both modes, **change the database password before installing** (`postgresHA.postgres.password` / `postgres.config.password`). GlitchTip is wired to the active database automatically — the HAProxy leader endpoint in HA mode, or the single instance directly in dev mode. **Template version `1.0.0` did not compact the etcd cluster inside the bundled highly available database**, so etcd's backend grows with time alone and goes read-only once it reaches its 2 GiB quota — after roughly 110 days — taking PostgreSQL failover with it. Only installs running the HA database are affected (`postgresHA.enabled`, the default here); see [etcd History Compaction](/template-catalog/templates/postgres-highly-available#etcd-history-compaction) for the mechanism and the symptoms. Upgrade to `1.0.1` or later to turn compaction on: that stops further growth but cannot shrink a backend that has already grown, and a cluster that has already raised a `NOSPACE` alarm needs operator recovery rather than an upgrade. ## Onboarding Users Registration is closed by default (`registration.enabled: false`) — an override of GlitchTip's upstream open-signup default. With registration closed, **invites only work for accounts that already exist**: create teammate accounts first through the Django admin at `https://.cpln.app/admin/` (log in with the superuser account), then invite them to your organization. Invite, alert, and password-reset emails require `email.secretName` to be set. To allow open self-signup instead, set `registration.enabled: true`. ## Connecting | What | Value | | ---------------------------------- | ---------------------------------------------------------------------------------------------------------- | | UI (public) | `https://.cpln.app` — `status.canonicalEndpoint` of `{release}-glitchtip` | | SDK DSN | Copy from the UI: project → Settings → DSN (it embeds the public endpoint) | | Internal (same GVC) | `http://{release}-glitchtip.{gvc}.cpln.local:8000` | | Login | `admin.email` / `admin.password` | | Django admin (user management) | `https://.cpln.app/admin/` | | PostgreSQL (internal, HA mode) | `{release}-postgres-ha-proxy.{gvc}.cpln.local:5432`, credentials in the `{release}-postgres-config` secret | | PostgreSQL (internal, single mode) | `{release}-postgres.{gvc}.cpln.local:5432`, credentials in the `{release}-pg-config` secret | ### Reporting Errors Point any Sentry SDK at your project's DSN. The DSN is issued in the UI under **project → Settings → DSN** and embeds the public endpoint, so no extra configuration is needed. Existing `@sentry/*` SDK setups keep working — only the DSN changes. SDKs running inside the same GVC can send events to the internal endpoint per `internalAccess`. ## Backing Up Database backups are optional and disabled by default. They cover the PostgreSQL database — the issues, events, and users that make up your GlitchTip instance. Enable them with `postgresHA.backup.enabled` or `postgres.backup.enabled` (matching your database mode), and complete the storage setup for your provider **before** installing. The values below are shown under `backup.*` — set them within the enabled database block. Create an S3 bucket. Set `backup.aws.bucket` and `backup.aws.region` to match. If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) for your AWS account. Set `backup.aws.cloudAccountName` to its name. Create an AWS IAM policy with the JSON below (replace `YOUR_BUCKET`), then set `backup.aws.policyName` to the policy's name: ```json theme={null} { "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Action": [ "s3:ListBucket", "s3:GetBucketLocation", "s3:GetObject", "s3:GetObjectVersion", "s3:PutObject", "s3:DeleteObject", "s3:DeleteObjectVersion", "s3:AbortMultipartUpload" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET", "arn:aws:s3:::YOUR_BUCKET/*" ] }] } ``` Create a GCS bucket. Set `backup.gcp.bucket` to its name. If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) for your GCP project. Set `backup.gcp.cloudAccountName` to its name — access is keyless (no stored credentials). Grant the `Storage Admin` role (or `roles/storage.objectAdmin` scoped to the bucket) to the GCP service account created for the Cloud Account. Create your bucket on the server. Set `backup.minio.bucket` to its name. Set `backup.minio.endpoint` to the S3 API address including port. For the `minio` marketplace template deployed in the same GVC, this is `http://WORKLOAD_NAME:9000`. Set `backup.minio.accessKey` and `backup.minio.secretKey` to credentials with access to the bucket. In HA mode, `backup.mode` selects `logical` (scheduled `pg_dump` via a cron workload) or `wal-g` (continuous WAL archiving). The single-instance mode takes scheduled logical dumps. ## Important Notes * **Change `django.secretKey`, `admin.password`, the database password, and the Redis password before installing.** * **Do not scale the worker** — it is a fixed singleton (scheduler plus boot-time migrations). Web `replicas` is the scaling knob; a worker outage pauses processing but ingest keeps accepting events and catches up when the worker returns. * **First boot: the web tier stays not-ready until the worker finishes migrations** — roughly 4–6 minutes in HA mode while the worker waits for the PostgreSQL HA proxy to open, then self-heals. If it seems stuck, check the worker logs first, not the web logs. * **With registration closed (default), invites only work for accounts that already exist** — create teammate accounts first at `/admin/`, then invite them. Invite and alert emails require `email.secretName`. See [Onboarding Users](#onboarding-users). * **DSNs embed the endpoint URL** — if you add a custom domain later, set `domain`, run a Helm upgrade, and update the DSNs in your apps. * **Source-map / artifact uploads are ephemeral** (local disk) — lost on restart and not shared across web replicas. Error ingest itself is unaffected; events go to PostgreSQL. * **Uninstall deletes the database volume sets** — all issues, events, and users. Enable backups if the data matters. * **This template ships the MIT-licensed open-source image** — GlitchTip is a single edition with nothing feature-gated. ## External References Official GlitchTip documentation Installation and configuration reference Client setup for the Sentry SDKs GlitchTip is compatible with What changed in the GlitchTip 6 release line Upstream GlitchTip backend source View the source files, default values, and chart definition # Grafana Source: https://docs.controlplane.com/template-catalog/templates/grafana Deploy Grafana OSS on Control Plane using the Template Catalog. Dashboards and alerting over your own datasources, backed by a highly available PostgreSQL app database, with datasource provisioning as code and multi-replica alerting HA coordinated by Redis Sentinel. ## Overview Grafana is the open-source (AGPL-3.0) dashboarding and alerting front end for time-series and SQL data. This template deploys Grafana OSS as a stateless app tier backed by a highly available PostgreSQL app database — every dashboard, user, alert rule, and saved datasource lives in that database, so the Grafana workload itself holds no state and can run multiple replicas. This template exists to visualize **data you own**: the catalog's [Prometheus](/template-catalog/templates/prometheus), [Thanos](/template-catalog/templates/thanos), and [Grafana Mimir](/template-catalog/templates/mimir) installs, your own SQL databases, and external systems. It is not an observability stack in itself — it ships no collectors, no exporters, and no data. **This template ships zero dashboards and zero datasources.** A fresh install has an empty dashboard list and an empty datasource list — you connect your own datasources (in the UI or [as code](#provisioning-datasources)) and build or import your own dashboards. Control Plane's console already provides built-in workload-metrics dashboards for every workload; this template is for the data those dashboards do not cover. ### Architecture * **Grafana** — A stateless standard workload serving the UI and HTTP API on port `3000`. The `replicas` knob controls how many instances run; all replicas share the same PostgreSQL app database. * **PostgreSQL (HA, default)** — The [postgres-highly-available](/template-catalog/templates/postgres-highly-available) template as a subchart: 3× Patroni PostgreSQL, 3× etcd, and a HAProxy leader-routing endpoint Grafana connects through. * **PostgreSQL (dev/lightweight, optional)** — The single-instance [postgres](/template-catalog/templates/postgres) template instead, for lighter non-HA deployments. * **Redis Sentinel (optional)** — The [redis](/template-catalog/templates/redis) template as a subchart in Sentinel mode. Grafana's alerting HA uses it to coordinate alert evaluation across replicas, so exactly one notification is sent per alert. Required when `replicas` is 2 or more. * **Hardened defaults** — User signup, anonymous access, and upstream telemetry are all disabled. When public access is on, Grafana's `root_url` is derived automatically from the canonical endpoint. ### What Gets Created * **Standard Grafana Workload** — `replicas` stateless instances serving the UI and API on port `3000`. * **Database Workloads** — HA mode: a stateful Patroni PostgreSQL workload, a stateful etcd workload, and a standard HAProxy leader-routing workload. Single mode: one stateful PostgreSQL workload. * **Redis Workloads** *(optional)* — A Redis workload and a Sentinel workload (3 replicas each by default) when `redis.enabled` is true. * **Volume Sets** — The database subchart's persistent volumes (10 GiB per replica by default), plus the etcd volumes in HA mode. Grafana itself has none. * **Secrets** — The database credentials created by the database subchart, and — only when `datasources.definitions` is set — an opaque secret holding the rendered datasource provisioning file. The admin password and the datasource-encryption key are **prerequisite secrets you create yourself**; the chart references them by name and never creates, modifies, or deletes them. * **Identity & Policy** — A least-privilege policy granting the Grafana identity `reveal` on exactly the secrets it uses, including the prerequisite admin-password, encryption-key, datasource-credential, and SMTP secrets you name. * **Cron Backup Workload** *(optional)* — When database backups are enabled. This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites **Two [opaque secrets](/guides/create-secret/opaque) must exist before you install.** Grafana's admin account is a human-facing login, and `publicAccess.enabled` defaults to `true`, so that account is reachable from the internet — a value in `values.yaml` would sit in the Helm release for the life of the install, so neither credential is a value. You create both secrets with encoding `plain` and name them in `admin.passwordSecretName` and `admin.secretKeySecretName`. Choose your own strong password for the `admin` login and store it as the secret's payload: ```bash theme={null} printf '%s' 'YOUR-STRONG-PASSWORD' | cpln secret create-opaque --name my-grafana-admin-password --encoding plain -f - ``` Set `admin.passwordSecretName` to the name you used. Generate a random key — Grafana encrypts every stored datasource credential with it — and back the key up somewhere safe outside Control Plane: ```bash theme={null} printf '%s' "$(openssl rand -hex 32)" | cpln secret create-opaque --name my-grafana-secret-key --encoding plain -f - ``` Set `admin.secretKeySecretName` to the name you used. **A missing prerequisite secret wedges the install rather than failing it.** `cpln helm install` still exits 0 and reports success, but the Grafana workload never starts: it sits at zero replicas with the message `The secret no longer exists. Workload updates are paused until the secret is added or the reference to the secret removed.` Create both secrets first, and after installing confirm with `cpln workload get-deployments {release}-grafana --gvc {gvc}` rather than trusting the Helm output. The two secrets have **different lifecycles** — see [Admin Credentials](#admin-credentials) before you delete or rotate either one. Optional features each need a secret or a bucket created **before** you install: | Feature | What you must create first | | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------ | | Credentialed provisioned datasources | One [dictionary secret](/guides/create-secret/dictionary) per entry in `datasources.credentialSecrets`, holding the credential keys | | Authenticated SMTP | An [opaque secret](/guides/create-secret/opaque) with encoding `plain` holding the SMTP password, named in `smtp.passwordSecretName` | | Database backups | A bucket and access setup on AWS S3, Google Cloud Storage, or an S3-compatible server — see [Backing Up](#backing-up) | Change the app database password (`postgresHA.postgres.password` or `postgres.config.password`) before installing as well — it ships with a `change-me` placeholder default. Once both secrets exist, install the template using your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Choosing a Database Mode Exactly one of the two database modes must be enabled — the chart enforces this at render and fails the install with a clear message otherwise. | | `postgresHA` (default) | `postgres` | | ----------------- | ------------------------------------------------------- | -------------------------------------- | | What runs | 3× Patroni PostgreSQL, 3× etcd, HAProxy leader endpoint | One single-replica PostgreSQL workload | | Database failover | Automatic (Patroni leader election) | None | | Footprint | 8 replicas across 3 workloads | 1 workload | | Best for | Production | Development and lightweight installs | In HA mode the full stack converges in roughly 6 minutes on a fresh install: etcd comes up first, then the PostgreSQL cluster and its HAProxy endpoint, then Grafana runs its schema migrations. Single mode is faster. ## Configuration The default `values.yaml` for this template: ```yaml theme={null} image: grafana/grafana:13.1.1 replicas: 1 # 1 = single instance; >=2 = HA tier over shared Postgres (requires redis.enabled: true) resources: maxCpu: 1000m maxMemory: 1Gi minCpu: 500m minMemory: 512Mi # BOTH secrets must EXIST BEFORE INSTALL — opaque, encoding: plain. # The admin login is on the public internet when publicAccess.enabled is true, # so its password never transits values or the Helm release. admin: user: admin # initial admin login name (not sensitive) # The password applies only when the admin account is FIRST created; on later # boots Grafana ignores it (change it in the UI instead). applyPassword: true # Set to false after your first login to stop referencing the password secret, which can then be deleted passwordSecretName: my-grafana-admin-password # opaque secret holding the first-boot admin password; read only while applyPassword is true # DIFFERENT LIFECYCLE — this one is permanent. It is read on EVERY boot to # decrypt datasource credentials stored in the DB (AES-256), so it has no # applyPassword-style toggle: never delete it, and never rotate it (that # breaks every saved datasource secret). secretKeySecretName: my-grafana-secret-key # opaque secret holding the encryption key # Grafana datasource provisioning entries, passed through verbatim. Point them at # YOUR datasources (prometheus/thanos/mimir templates, your DBs, external APIs). datasources: definitions: [] # - name: Prometheus # type: prometheus # access: proxy # url: http://RELEASE-prometheus.GVC.cpln.local:9095 # prometheus template # isDefault: true # - name: Thanos # type: prometheus # access: proxy # url: http://RELEASE-thanos.GVC.cpln.local:10902 # thanos Query template # - name: AppDB # type: postgres # url: my-db-host:5432 # user: grafana_reader # jsonData: { database: appdb, sslmode: disable } # secureJsonData: # password: $PG_PASSWORD # interpolated from credentialSecrets below # The credentials Grafana authenticates TO each datasource with — not user # access to Grafana. `definitions` renders into a plaintext provisioning file, # so put the password in a pre-created dictionary secret and write $KEY above. # Every $KEY needs an entry here, and the secrets MUST EXIST BEFORE INSTALL. credentialSecrets: [] # - name: my-grafana-ds-credentials # keys: [PG_PASSWORD] smtp: # alert notification emails enabled: false host: smtp.example.com:587 # host:port user: "" # empty = unauthenticated SMTP passwordSecretName: "" # opaque secret (encoding: plain) with the SMTP password; create BEFORE install fromAddress: grafana@example.com fromName: Grafana publicAccess: enabled: true # UI on the canonical *.cpln.app HTTPS endpoint internalAccess: type: same-gvc # options: none, same-gvc, same-org, workload-list workloads: [] # used with workload-list # workloads: # - //gvc/GVC_NAME/workload/WORKLOAD_NAME postgresHA: # default: highly available PostgreSQL app database enabled: true postgres: username: grafana password: change-me-grafana-db-password # change before installing database: grafana replicas: 3 volumeset: capacity: 10 # initial capacity in GiB per replica (minimum is 10) backup: # optional database backups — see Backing Up enabled: false mode: logical # logical or wal-g resources: cpu: 100m memory: 128Mi logical: image: ghcr.io/controlplane-com/backup-images/postgres-backup:17.1.0 schedule: "0 2 * * *" walg: intervalSeconds: 21600 provider: aws # options: aws, gcp, minio aws: bucket: grafana-pg-backup-bucket region: us-east-1 cloudAccountName: my-s3-cloud-account policyName: grafana-pg-backup-policy prefix: postgres/backups gcp: bucket: grafana-pg-backup-bucket cloudAccountName: my-gcs-cloud-account prefix: postgres/backups minio: endpoint: http://my-minio-workload:9000 bucket: grafana-pg-backup-bucket accessKey: my-minio-username secretKey: my-minio-password prefix: postgres/backups postgres: # dev/lightweight: single-instance PostgreSQL (disable postgresHA first) enabled: false config: username: grafana password: change-me-grafana-db-password # change before installing database: grafana volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) backup: # optional database backups — see Backing Up enabled: false image: ghcr.io/controlplane-com/backup-images/postgres-backup:18.1.0 schedule: "0 2 * * *" resources: cpu: 100m memory: 128Mi provider: aws # options: aws, gcp, minio aws: bucket: grafana-pg-backup-bucket region: us-east-1 cloudAccountName: my-s3-cloud-account policyName: grafana-pg-backup-policy prefix: postgres/backups gcp: bucket: grafana-pg-backup-bucket cloudAccountName: my-gcs-cloud-account prefix: postgres/backups minio: endpoint: http://my-minio-workload:9000 bucket: grafana-pg-backup-bucket accessKey: my-minio-username secretKey: my-minio-password prefix: postgres/backups redis: # Sentinel-mode Redis — alerting-HA coordination; required when replicas >= 2 enabled: false redis: image: redis:7.4 replicas: 3 resources: cpu: 200m memory: 256Mi minCpu: 80m minMemory: 128Mi sentinel: image: redis:7.4 replicas: 3 resources: cpu: 200m memory: 256Mi minCpu: 80m minMemory: 128Mi ``` ### Grafana Instance * `image` — The Grafana OSS container image. * `replicas` — Number of Grafana instances. `1` is a single instance; `2` or more forms an HA tier over the shared app database and **requires `redis.enabled: true`** — see [High Availability](#high-availability). * `resources` — CPU and memory for the Grafana container: `minCpu`/`minMemory` are the reservation, `maxCpu`/`maxMemory` the limit. * `admin.user` — The admin login name. Not sensitive, so it stays a plain value. Applied when the account is first created. * `admin.applyPassword` / `admin.passwordSecretName` / `admin.secretKeySecretName` — The two prerequisite secrets and the toggle that retires one of them. See [Admin Credentials](#admin-credentials). Signup (`GF_USERS_ALLOW_SIGN_UP`), anonymous access, and upstream analytics/update checks are disabled by the template and are not configurable. ### Admin Credentials Both credentials live in [opaque secrets](/guides/create-secret/opaque) you create before installing (see [Prerequisites](#prerequisites)) — neither ever passes through Helm values. **They sit next to each other in `values.yaml` but their lifecycles are opposites**, and deleting the wrong one is destructive: | | `admin.passwordSecretName` | `admin.secretKeySecretName` | | ---------------------------- | --------------------------------------------------------- | ------------------------------------------------------------------- | | What it holds | The `admin` login password | The key encrypting stored datasource credentials | | When Grafana reads it | Only when the admin account is **first created** | On **every boot** | | Environment variable | `GF_SECURITY_ADMIN_PASSWORD` | `GF_SECURITY_SECRET_KEY` | | Can you stop referencing it? | Yes — set `admin.applyPassword: false` | **No.** There is no toggle | | Can you delete the secret? | Yes, once `applyPassword` is `false` | **Never** | | Can you rotate it? | Yes — but change the password in the Grafana UI, not here | **Never** — every saved datasource credential becomes undecryptable | The encryption-key secret is permanent. Deleting it wedges the workload, and changing its payload makes every datasource credential already saved in the app database undecryptable. Back the key up outside Control Plane instead of rotating it. #### Retiring the Admin Password Secret After the account exists, the password secret is inert — Grafana ignores `GF_SECURITY_ADMIN_PASSWORD` on later boots, and the password can only be changed in the UI (or with `grafana-cli admin reset-admin-password`). So you can stop referencing it entirely: Sign in as `admin.user` with the password from the secret and confirm you can reach the UI. With `admin.applyPassword: false`, the chart drops the password secret from both places it appeared — the container environment and the `reveal` policy grant. The encryption-key reference is retained. Nothing references it any more, so the secret can be deleted. Your existing admin login keeps working; the account lives in the app database. **After that upgrade, expect logins to return `401` and `/api/health` to return `503` for a minute or two** while the bundled app database restarts alongside the change. This is the database being briefly unreachable, not the toggle breaking authentication or resetting your password — do not roll back, and do not recreate the password secret. Wait for `/api/health` to report `200` and log in again. ### Access * `publicAccess.enabled` — Serve the UI and API on the canonical `*.cpln.app` HTTPS endpoint (default). Everything behind the endpoint is gated by Grafana's own login, and Grafana's `root_url` is set to that endpoint automatically. Set to `false` for an internal-only instance — external requests are then refused at the edge while in-GVC callers still reach it per `internalAccess`. * `internalAccess.type` — Internal firewall scope of the Grafana workload: | Type | Description | | --------------- | ---------------------------------------------------------------------- | | `none` | No internal access. | | `same-gvc` | Allow access from all workloads in the same GVC (default). | | `same-org` | Allow access from all workloads in the same organization. | | `workload-list` | Allow access only from workloads listed in `internalAccess.workloads`. | Firewall changes applied by an upgrade take up to about 30 seconds to propagate. ### App Database Enable exactly one of `postgresHA` (production, default) or `postgres` (dev/lightweight) — see [Choosing a Database Mode](#choosing-a-database-mode). In both modes, **change the database password before installing** (`postgresHA.postgres.password` / `postgres.config.password`). Grafana is wired to the active database automatically: the HAProxy leader endpoint in HA mode, or the single instance directly in dev mode. In HA mode `postgresHA.proxy.enabled` must stay `true` — that HAProxy endpoint is Grafana's stable database address, and disabling it is rejected at render. **Template versions before `1.2.1` did not compact the etcd cluster inside the bundled highly available database**, so etcd's backend grows with time alone and goes read-only once it reaches its 2 GiB quota — after roughly 110 days — taking PostgreSQL failover with it. Only installs running the HA database are affected (`postgresHA.enabled`, the default here); see [etcd History Compaction](/template-catalog/templates/postgres-highly-available#etcd-history-compaction) for the mechanism and the symptoms. Upgrade to `1.2.1` or later to turn compaction on: that stops further growth but cannot shrink a backend that has already grown, and a cluster that has already raised a `NOSPACE` alarm needs operator recovery rather than an upgrade. ### High Availability Grafana holds no local state, so scaling the app tier is a matter of raising `replicas`. Alert evaluation is the one thing that must be coordinated: without coordination every replica would evaluate the same rule and send its own notification. ```yaml theme={null} replicas: 2 # any value >= 2 redis: enabled: true # required when replicas >= 2 ``` * Coordination uses Grafana's **Redis-Sentinel alerting HA mode** — the Sentinel workload deployed by the `redis` subchart, not gossip-based peer clustering. Each replica registers itself under its own peer name, and exactly one notification is delivered per firing alert. * The chart refuses to render `replicas >= 2` without `redis.enabled: true`. * Redis and Sentinel ship without authentication — the same-GVC internal firewall is the boundary. Enabling Redis or Sentinel auth is rejected at render in this version. * Multi-replica behavior verified in testing: a full rolling restart and a killed replica both served every request without a single non-200 response, and a killed replica was replaced in about 98 seconds. ### SMTP Grafana sends alert notification emails through the SMTP server you configure. Leave `smtp.enabled: false` if you use webhook, chat, or other contact points instead. ```yaml theme={null} smtp: enabled: true host: smtp.example.com:587 # host:port user: mailer # omit for unauthenticated SMTP passwordSecretName: my-smtp-password # pre-created opaque secret (encoding: plain) fromAddress: grafana@example.com fromName: Grafana ``` `smtp.passwordSecretName` is required whenever `smtp.user` is set. The password stays in the [opaque secret](/guides/create-secret/opaque) — it never passes through Helm values — and the chart grants the Grafana identity `reveal` on exactly that secret. ## Provisioning Datasources You can add datasources in the UI at any time. `datasources.definitions` additionally lets you declare them as code: each entry is a standard [Grafana datasource provisioning](https://grafana.com/docs/grafana/latest/administration/provisioning/#data-sources) entry, rendered verbatim into a provisioning file that the workload mounts at startup. Provisioned datasources are read-only in the Grafana UI. To change one, change the value and upgrade the release. For catalog services running in the same GVC, use their internal endpoints: ```yaml theme={null} datasources: definitions: - name: Prometheus type: prometheus access: proxy url: http://RELEASE_NAME-prometheus.GVC_NAME.cpln.local:9095 # prometheus template isDefault: true - name: Thanos type: prometheus access: proxy url: http://RELEASE_NAME-thanos.GVC_NAME.cpln.local:10902 # thanos Query template ``` ### Datasource Credentials Credentials for provisioned datasources never go into Helm values. Put them in a [dictionary secret](/guides/create-secret/dictionary) you create before installing, list the secret under `datasources.credentialSecrets`, and reference each key as `$KEY` in the provisioning entry. Every listed key is exposed to the container as an environment variable, Grafana interpolates it when it loads the provisioning file, and the chart grants the workload `reveal` on exactly that secret. Create a [dictionary secret](/guides/create-secret/dictionary) — for example `my-grafana-ds-credentials` with the key `PG_PASSWORD` — **before** installing. Add the secret name and the keys you want exposed to `datasources.credentialSecrets`. Use `$PG_PASSWORD` wherever the credential belongs in the provisioning entry. ```yaml theme={null} datasources: definitions: - name: AppDB type: postgres url: my-db-host:5432 user: grafana_reader jsonData: { database: appdb, sslmode: disable } secureJsonData: password: $PG_PASSWORD credentialSecrets: - name: my-grafana-ds-credentials keys: [PG_PASSWORD] ``` The provisioning file itself only ever contains the literal `$PG_PASSWORD` placeholder — the value stays in the secret. ### Control Plane Metrics as a Datasource Control Plane's own metrics store is Prometheus-compatible and holds more than the console's built-in dashboards display, including [custom metrics](/reference/workload/custom-metrics) your workloads expose, cost-relevant series such as `egress`, `cross_zone_traffic`, and `volume_set_*`, and cron and stability counters. Adding it as a datasource complements the built-in workload dashboards rather than replacing them — reach for it when you need custom application metrics, cost views, alerting you own and route yourself, or a single pane that mixes platform metrics with your other datasources. Follow the [centralized metrics management guide](/guides/centralized-metrics-management) for the endpoint details, then wire the token in as a datasource credential: Create a service account granted the `readMetrics` permission on the org and generate a key for it. Put the key in a [dictionary secret](/guides/create-secret/dictionary) — for example under the key `CPLN_METRICS_TOKEN` — and list the secret under `datasources.credentialSecrets`. Point a `prometheus`-type datasource at `https://metrics.cpln.io/metrics/org/YOUR_ORG` and send the token as an `Authorization` header. ```yaml theme={null} datasources: definitions: - name: Control Plane metrics type: prometheus access: proxy url: https://metrics.cpln.io/metrics/org/YOUR_ORG jsonData: httpHeaderName1: Authorization secureJsonData: httpHeaderValue1: Bearer $CPLN_METRICS_TOKEN credentialSecrets: - name: my-grafana-ds-credentials keys: [CPLN_METRICS_TOKEN] ``` A workload's built-in `CPLN_TOKEN` does **not** authenticate against the metrics endpoint. Use a service-account key with the `readMetrics` permission. ## Connecting | What | Value | | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- | | UI / API (public) | `https://.cpln.app` — `status.canonicalEndpoint` of `{release}-grafana` | | Internal (same GVC) | `http://{release}-grafana.{gvc}.cpln.local:3000` | | Login | `admin.user` / the payload of the `admin.passwordSecretName` secret — or whatever you have since changed the password to in the UI | | App database (internal, HA mode) | `{release}-postgres-ha-proxy.{gvc}.cpln.local:5432`, credentials in the `{release}-postgres-config` secret | | App database (internal, single mode) | `{release}-postgres.{gvc}.cpln.local:5432`, credentials in the `{release}-pg-config` secret | Health and readiness are served at `/api/health`, which reports the Grafana version and the app-database status: ```bash theme={null} curl -s https://.cpln.app/api/health ``` The [Grafana HTTP API](https://grafana.com/docs/grafana/latest/developers/http_api/) is available on the same endpoint for scripted dashboard, datasource, and alert-rule management. ## Backing Up Database backups are optional and disabled by default. They cover the app database — the dashboards, users, alert rules, and saved datasources that make up your Grafana instance. Enable them with `postgresHA.backup.enabled` or `postgres.backup.enabled` (matching your database mode), and complete the storage setup for your provider **before** installing. The values below are shown under `backup.*` — set them within the enabled database block. Create an S3 bucket. Set `backup.aws.bucket` and `backup.aws.region` to match. If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) for your AWS account. Set `backup.aws.cloudAccountName` to its name. Create an AWS IAM policy with the JSON below (replace `YOUR_BUCKET`), then set `backup.aws.policyName` to the policy's name: ```json theme={null} { "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Action": [ "s3:ListBucket", "s3:GetBucketLocation", "s3:GetObject", "s3:GetObjectVersion", "s3:PutObject", "s3:DeleteObject", "s3:DeleteObjectVersion", "s3:AbortMultipartUpload" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET", "arn:aws:s3:::YOUR_BUCKET/*" ] }] } ``` Create a GCS bucket. Set `backup.gcp.bucket` to its name. If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) for your GCP project. Set `backup.gcp.cloudAccountName` to its name — the backup identity reaches the bucket keylessly, with no stored credentials. Grant the `Storage Admin` role to the GCP service account created for the Cloud Account (`roles/storage.objectAdmin` scoped to the bucket also works). Create your bucket on the server. Set `backup.minio.bucket` to its name. Set `backup.minio.endpoint` to the S3 API address including port. For the [MinIO](/template-catalog/templates/minio) template deployed in the same GVC, this is `http://WORKLOAD_NAME:9000`. Set `backup.minio.accessKey` and `backup.minio.secretKey` to credentials with access to the bucket. In HA mode, `backup.mode` selects `logical` (scheduled `pg_dump` via a cron workload) or `wal-g` (continuous WAL archiving). The single-instance mode takes scheduled logical dumps. ## Important Notes * **Create the admin-password and encryption-key secrets before installing**, and change the database password. A missing prerequisite secret does not fail the install — `helm install` reports success and the workload then sits at zero replicas waiting on the secret. * **The encryption-key secret is permanent: never delete it, never rotate it.** It is read on every boot to decrypt stored datasource credentials; changing its payload makes every saved datasource credential undecryptable. Back the key up outside Control Plane. * **After your first login you can set `admin.applyPassword: false` and delete the password secret** — see [Retiring the Admin Password Secret](#retiring-the-admin-password-secret). Expect `401` logins and a `503` from `/api/health` for a minute or two after that upgrade while the app database restarts; this is not the toggle breaking authentication. * **This template ships no dashboards and no datasources.** It is the pane for data you own; Control Plane's console already provides built-in workload-metrics dashboards, so there is no reason to recreate those here. * **Scaling requires Redis** — set `replicas >= 2` together with `redis.enabled: true`. The chart refuses to render multi-replica without it, because Sentinel-coordinated alerting is what keeps one firing alert from producing one notification per replica. * **Dashboards, users, and alert rules live in the app database** and survive Grafana restarts, redeployments, and replica replacement. **Uninstall deletes the database volume sets** — enable backups if the data matters. * **Prerequisite secrets you create are not owned by the release** — the admin-password, encryption-key, datasource-credential, and SMTP secrets all survive `helm uninstall` and must be deleted manually if you no longer need them. * **With `publicAccess.enabled: false`, Grafana's `root_url` is not derived** from a canonical endpoint, so absolute links in alert emails from an internal-only instance point at localhost. Keep public access on if you rely on links in email notifications. * **Grafana Live push updates are per-instance** in multi-replica mode; dashboard auto-refresh and alerting are unaffected. * **This template ships Grafana OSS only** — Enterprise features such as fine-grained RBAC, reporting, and query caching are not available. ## External References Official Grafana documentation Reference for the datasource provisioning entries used by this template How Grafana coordinates alert evaluation across replicas Every Grafana setting and its environment-variable name Manage dashboards, datasources, and alert rules programmatically View the source files, default values, and chart definition # Grafana Multi-Location Source: https://docs.controlplane.com/template-catalog/templates/grafana-multi-location Deploy Grafana OSS across Control Plane locations behind one endpoint, with dashboards, users, sessions and datasource credentials shared through a stretched Patroni PostgreSQL cluster, and exactly one instance sending each firing alert's notification — a pinned evaluator or Redis-coordinated HA. ## Overview Grafana Multi-Location deploys **one logical Grafana** whose UI/API instances run in every location of a chart-created multi-location GVC, behind a single georouted `*.cpln.app` endpoint. Every dashboard, user, org, session, alert rule and saved datasource lives in a [`postgres-multi-location`](/template-catalog/templates/postgres-multi-location) cluster stretched across the same locations, so Grafana itself holds no state: there is no volume, no session affinity and nothing to hand over when an instance is replaced. **Exactly one instance sends each firing alert's notification**, in one of two shapes you choose with `alerting.highAvailability.enabled`: * **Off (the default)** — a **separate single-replica workload** runs in one location with rule execution enabled, and it is disabled on every UI instance. Exactly-once evaluation is a property of the topology: nothing is elected at runtime, and no value of `replicas` can produce a second evaluator. Losing that location stops evaluation. * **On** — that workload is not created at all. Every UI instance in every location evaluates every rule, and a stretched Redis tier coordinates the peers so exactly one **notification** is sent. Alert evaluation then survives the loss of a location, at the cost of multiplying data-source query load. See [Alert Evaluation](#alert-evaluation) before choosing. For a Grafana inside a single location, use the [Grafana](/template-catalog/templates/grafana) template instead. ### How Many Locations You Need The bundled database's consensus store ([`etcd-multi-location`](/template-catalog/templates/etcd-multi-location)) commits a write only when a **majority** of its members agree, and it runs one member per location. That arithmetic decides what survives — the Grafana tier itself has no quorum of its own. | Locations | Location losses survived | What a user sees when one location is lost | | --------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **2** | **0** | Dashboards still render at times, but logins, saves and alert-state writes fail. The survivor holds current data and stays read-only until it is promoted by hand. | | **3** | **1** | Automatic database failover; the endpoint keeps serving from the two surviving locations (derived from the quorum arithmetic — a true region outage cannot be simulated here). | | **5** | **2** | Survives losing **two** locations. | With N locations you survive `floor((N-1)/2)` losses, so an even count buys nothing over the odd count below it. Losing the location named in `alerting.location` is a separate matter — see [Alert Evaluation](#alert-evaluation). **`alerting.highAvailability.enabled: true` requires at least 3 locations**, and the chart refuses to render below that. Its Redis tier elects a master by a majority of locations (one Sentinel each), so at 2 locations losing either one leaves no quorum — the exact event the knob exists to survive. ### What Gets Created * **GVC** — A new GVC pinned to the configured locations. This template always creates it. * **Standard Grafana UI Workload** — `{release}-grafana`, `replicas` instances **per location**, serving the UI and HTTP API on port `3000`. Public by default. Alert rule execution is disabled here by default, and enabled on every instance when alerting HA is on. * **Standard Alert Evaluator Workload** *(optional)* — `{release}-grafana-alerting`, the same image with **exactly one replica**, running only in `alerting.location` and never reachable from the internet. Not created when `alerting.highAvailability.enabled` is `true`, or when `alerting.enabled` is `false`. * **App Database Workloads** — The `postgres-multi-location` subchart: a stateful Patroni PostgreSQL workload with one primary and asynchronous replicas, a stateful etcd workload for consensus, and an HAProxy leader-routing tier in every location. * **Alerting Coordination Workloads** *(optional)* — The `redis-multi-location` subchart: `{release}-redis` and `{release}-sentinel`, one of each **per location**, coordinating exactly-once notification delivery. Created **only** when `alerting.highAvailability.enabled` is `true`. * **Volume Sets** — `{release}-postgres-vs` for the PostgreSQL data directory and the etcd cluster's own volume set, plus `{release}-redis-vs` and `{release}-sentinel-vs` with alerting HA on. Grafana itself has none. * **Secrets** — The subchart startup scripts and Redis configuration, and — only when `datasources.definitions` is set — `{release}-grafana-datasources`, the rendered datasource provisioning file mounted by every Grafana workload. The admin password, encryption key and database credentials are **prerequisite secrets you create yourself**; the chart references them by name and never creates, modifies or deletes them. * **Identity & Policy** — One identity shared by every Grafana workload, with `reveal` on exactly the secrets they mount and nothing else. Each subchart tier gets its own identity and policy. This template creates its own GVC. You do not need to create one before installing. ## Prerequisites The GVC named in `global.gvc.name` **must not already exist**. Helm adopts a GVC that does, and `helm uninstall` then deletes it along with every unrelated workload in it. Always point this template at a fresh GVC name. **Three secrets must exist before you install.** The admin account is a human-facing login and `publicAccess.enabled` defaults to `true`, so its password never passes through Helm values; the encryption key and the database credentials are shared by every instance in every location. An [opaque secret](/guides/create-secret/opaque) with encoding `plain`: ```bash theme={null} printf '%s' "$(openssl rand -hex 24)" \ | cpln secret create-opaque --name my-grafana-admin-password --encoding plain -f - ``` Set `admin.passwordSecretName` to the name you used. An [opaque secret](/guides/create-secret/opaque) with encoding `plain`. Every instance reads it on every boot to decrypt datasource credentials stored in the shared database — back it up outside Control Plane and never rotate it: ```bash theme={null} printf '%s' "$(openssl rand -hex 32)" \ | cpln secret create-opaque --name my-grafana-secret-key --encoding plain -f - ``` Set `admin.secretKeySecretName` to the name you used. A [dictionary secret](/guides/create-secret/dictionary) holding exactly `username`, `password` and `database`: ```bash theme={null} cpln secret create-dictionary --name my-grafana-db-credentials \ --entry username=grafana \ --entry password="$(openssl rand -hex 24)" \ --entry database=grafana ``` Use plain identifiers for `username` and `database` — they are used unquoted when the database is created. Set `postgresML.postgres.credentialsSecretName` to the secret's name. ```bash theme={null} cpln secret reveal my-grafana-db-credentials -o json ``` Without `-o json` the command prints a table containing no secret data. **A missing prerequisite secret wedges the install rather than failing it.** `cpln helm install` still reports success while the affected workload sits at zero replicas waiting on a secret reference that never resolves, which looks like a broken install. Create all three first, and confirm with `cpln workload get-deployments {release}-grafana --gvc {gvc}` rather than trusting the Helm output. Optional features each need something created **before** you install: | Feature | What you must create first | | ----------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Credentialed provisioned datasources | One [dictionary secret](/guides/create-secret/dictionary) per entry in `datasources.credentialSecrets`, holding the credential keys | | Authenticated SMTP | An [opaque secret](/guides/create-secret/opaque) with encoding `plain` holding the SMTP password, named in `smtp.passwordSecretName` | | Database backups | A bucket and access setup on AWS S3, Google Cloud Storage, or an S3-compatible server — see [Backing Up](#backing-up) | | Authenticating the alerting-HA Redis tier | One [opaque secret](/guides/create-secret/opaque) with encoding `plain` per password — see [Authenticating the Redis Tier](#authenticating-the-redis-tier). Optional hardening: alerting HA itself needs **no** extra secrets | Once the secrets exist, install the template using your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} # ─── GVC and locations ──────────────────────────────────────────────────────── # Lives under `global` so the postgres-multi-location subchart — its own etcd # subchart, two levels down — and the optional redis-multi-location subchart get # the same GVC and locations automatically. Never maintain two location lists. global: gvc: # This chart CREATES this GVC. It must NOT already exist: Helm adopts a GVC # that does, and `helm uninstall` then DELETES it and everything in it. name: grafana-multi-location-gvc # Minimum 2 locations, and minimum 3 with alerting HA enabled below. The # database tier needs 3 for automatic failover and 5 to survive losing two — # see the survival table in the README. # `replicas` here is DATABASE members per location. Grafana's own count per # location is the top-level `replicas` below; the optional Redis tier has its # own count (redisML.redis.replicasPerLocation) and ignores this number. locations: - name: aws-us-east-1 replicas: 1 - name: aws-eu-central-1 replicas: 1 - name: aws-us-west-2 replicas: 1 # ─── Grafana UI tier ────────────────────────────────────────────────────────── image: grafana/grafana:13.1.3 replicas: 1 # Grafana UI instances PER LOCATION resources: maxCpu: 1000m maxMemory: 1Gi minCpu: 500m minMemory: 512Mi database: maxOpenConn: 10 # per instance; see the README budget # ─── Alerting ───────────────────────────────────────────────────────────────── # enabled: false turns rule evaluation off entirely — rules can still be created # and viewed, nothing ever evaluates them. # # highAvailability.enabled: false (default) — a SEPARATE single-replica workload # in `location` evaluates every rule, and exactly-once follows from there being # only one of it. Restarting it re-notifies whatever is firing at the time, and # losing that location stops evaluation with no sign of it in the UI. # # highAvailability.enabled: true — no separate workload; every UI instance # evaluates and a stretched Redis (redisML below) picks exactly one to SEND, so # alerting survives losing a location. Needs 3+ locations. The costs: datasource # query load multiplies by (locations × replicas) because Redis dedupes the # notification and not the query; a large rule set may need resources.maxCpu # raised; and if Redis is unhealthy alerting DUPLICATES rather than going silent. alerting: enabled: true highAvailability: enabled: false # Dedicated-evaluator mode ONLY — where the one evaluator runs. Must be one of # global.gvc.locations. IGNORED when highAvailability.enabled is true. location: aws-us-east-1 # Dedicated-evaluator mode ONLY — in HA mode the UI tier's `resources` apply. resources: maxCpu: 1000m maxMemory: 1Gi minCpu: 500m minMemory: 512Mi # ─── Admin Bootstrap & Encryption (prerequisite secrets) ────────────────────── # BOTH secrets must EXIST BEFORE INSTALL — opaque, encoding: plain (see README). # The admin login is on the public internet when publicAccess.enabled is true, # so its password never transits values or the Helm release. admin: user: admin # initial admin login name (not sensitive) # The password applies only when the admin account is FIRST created; on later # boots Grafana ignores it (change it in the UI instead). applyPassword: true # set false after your first login to stop referencing the password secret passwordSecretName: my-grafana-admin-password # opaque secret holding the first-boot admin password # DIFFERENT LIFECYCLE — permanent. Read on EVERY boot by EVERY instance to # decrypt datasource credentials stored in the shared database: never delete # it, never rotate it. secretKeySecretName: my-grafana-secret-key # opaque secret holding the encryption key # ─── Datasources as Code (optional) ─────────────────────────────────────────── # Grafana datasource provisioning entries, passed through verbatim. Every # instance of every workload applies the same file on boot. datasources: definitions: [] # - name: Prometheus # type: prometheus # access: proxy # # Substitute BOTH parts: the workload name AND the GVC. A leftover # # placeholder resolves to nothing and the panel shows only # # "upstream connect error ... connection timeout". # url: http://YOUR_WORKLOAD.YOUR_GVC.cpln.local:9095 # isDefault: true # - name: AppDB # type: postgres # url: my-db-host:5432 # user: grafana_reader # jsonData: { database: appdb, sslmode: disable } # secureJsonData: # password: $PG_PASSWORD # interpolated from credentialSecrets below # The credentials Grafana authenticates TO each datasource with. `definitions` # renders into a plaintext provisioning file, so put the password in a # pre-created dictionary secret and write $KEY above. Every $KEY needs an entry # here, and the secrets MUST EXIST BEFORE INSTALL. credentialSecrets: [] # - name: my-grafana-ds-credentials # keys: [PG_PASSWORD] # ─── SMTP for Alert Emails (optional) ───────────────────────────────────────── # Whichever instance evaluates is what actually sends: the dedicated evaluator # workload by default, or the coordinating UI instance when alerting HA is on. smtp: enabled: false host: smtp.example.com:587 # host:port user: "" # empty = unauthenticated SMTP. IF YOU SET THIS, the relay MUST offer # STARTTLS or TLS: Grafana refuses to send credentials over an unencrypted # connection ("failed to send email: unencrypted connection") and every # notification is then lost, with the only signal a log line in the # sending workload. Hosted relays (SES, SendGrid, Mailgun, M365, Gmail) # are fine; a plain in-GVC relay is not — leave this empty for those. passwordSecretName: "" # opaque secret (encoding: plain) with the SMTP password; create BEFORE install fromAddress: grafana@example.com fromName: Grafana # ─── Access ─────────────────────────────────────────────────────────────────── # Applies to the UI tier. The dedicated evaluator workload is NEVER public; it # honours internalAccess only, so you can reach it in-GVC to manage silences. publicAccess: enabled: true # UI on the canonical *.cpln.app HTTPS endpoint internalAccess: type: same-gvc # options: none, same-gvc, same-org, workload-list workloads: [] # used with workload-list # workloads: # - //gvc/GVC_NAME/workload/WORKLOAD_NAME # ─── App Database (subchart: postgres-multi-location) ───────────────────────── # One Patroni cluster stretched across the same locations: a single primary, # async replicas elsewhere, automatic promotion in a surviving location. # Grafana has no read/write splitting, so EVERY query goes to the primary — # put it where most of your users are. postgresML: # The stretched etcd cluster behind Patroni. Compaction is on by default and # should stay on: without it etcd's backend grows with time alone — Patroni # renews its lease every ~10s and every renewal is a revision — until it hits # the quota and etcd goes READ-ONLY, taking the database's failover with it. etcd: tuning: autoCompactionMode: periodic # periodic (retention is a duration) or revision (a revision count) autoCompactionRetention: 1h # periodic needs an explicit unit (1h, 30m, 24h) quotaBackendBytes: 0 # backend size limit in bytes; 0 = etcd's own default of 2 GiB postgres: # REQUIRED PREREQUISITE SECRET — CREATE IT BEFORE YOU INSTALL. # A `dictionary` secret holding exactly `username`, `password` and # `database`. If it does not exist the deployment wedges waiting on it. credentialsSecretName: my-grafana-db-credentials # Preferred location for the database primary. Keep it aligned with # alerting.location so the hot path has no cross-region hop. With alerting HA # on, this also decides which Grafana location runs the schema migrations. primaryLocation: aws-us-east-1 resources: minCpu: 500m minMemory: 1Gi maxCpu: 1 maxMemory: 2Gi volumeset: capacity: 10 # initial capacity in GiB per member (minimum is 10) proxy: minReplicas: 2 # HAProxy leader-routing tier, per location maxReplicas: 2 backup: # optional database backups — see Storage setup in the README enabled: false mode: logical # logical or wal-g location: aws-us-east-1 # logical mode only: the ONE location the nightly job runs in resources: cpu: 100m # 512Mi, matching postgres-multi-location's own default. At 128Mi the GCP # path OOMs with NO log output: logical jobs merely report `failed` and the # wal-g sidecar loops on OOMKilled while WAL archives with no base backup. # Do not lower this without re-testing the GCS path. memory: 512Mi logical: image: ghcr.io/controlplane-com/backup-images/postgres-backup:17.1.0 schedule: "0 2 * * *" walg: intervalSeconds: 21600 provider: aws # options: aws, gcp, minio aws: bucket: my-grafana-bucket region: us-east-1 cloudAccountName: my-s3-cloud-account policyName: my-grafana-backup-policy prefix: grafana/backups gcp: bucket: my-grafana-bucket cloudAccountName: my-gcs-cloud-account prefix: grafana/backups minio: endpoint: http://my-minio-workload:9000 bucket: my-grafana-bucket credentialsSecretName: my-grafana-minio-credentials prefix: grafana/backups # ─── Alerting-HA coordination (subchart: redis-multi-location) ──────────────── # Rendered ONLY when alerting.highAvailability.enabled is true. One Redis and one # Sentinel per location; failover needs a majority of them, which is why alerting # HA requires 3+ locations. redisML: redis: # OPTIONAL hardening, off by default. Opaque secret (encoding: plain) whose # payload is the password; it MUST EXIST BEFORE INSTALL, and Grafana reads the # same one, so a misspelt name stops the UI tier too. "" leans on the same-GVC # firewall instead — fine for a GVC this chart owns. passwordSecretName: "" image: redis:7.4 replicasPerLocation: 1 # Redis members per location; 1 is plenty for alert coordination resources: cpu: 200m memory: 256Mi volumeset: initialCapacity: 10 # GiB per member (platform minimum); coordination data is tiny sentinel: # Same shape and same failure mode as redis.passwordSecretName, independent of # it — Sentinel is what Grafana asks for the current master. passwordSecretName: "" image: redis:7.4 resources: cpu: 200m memory: 256Mi ``` ### GVC and Locations The GVC name and location list live under `global.gvc` so that Helm passes the same values to the bundled database, through it to etcd, and to the optional Redis tier. Never configure the location lists separately. * `global.gvc.name` — Name of the GVC this chart creates. It must not already exist. * `global.gvc.locations[].name` — A Control Plane location (e.g. `aws-us-east-1`). At least 2 are required, at least 3 with `alerting.highAvailability.enabled: true`, and the chart refuses to render with fewer. * `global.gvc.locations[].replicas` — **Database** members in that location, not Grafana instances. It must be at least 1; removing a location from the list is the supported way to shrink the deployment. The Redis tier has its own count (`redisML.redis.replicasPerLocation`) and ignores this number. ### Grafana UI Tier * `image` — The Grafana OSS container image. It must provide `/bin/bash`: the chart overrides the entrypoint to run Grafana's `/run.sh` through a boot wrapper. The default Alpine-based official image does. * `replicas` — Grafana UI instances **per location**. It carries no alerting-related restriction in either mode — the only thing to watch is the connection budget below. * `resources` — `minCpu`/`minMemory` are the reservation, `maxCpu`/`maxMemory` the limit, applied per instance. * `database.maxOpenConn` — Maximum database connections **per instance**. `(replicas × locations + 1) × database.maxOpenConn` must stay at **80 or less** — the bundled cluster's `max_connections` is 100, and the remainder is headroom for Patroni and administration. The `+ 1` is the dedicated alert evaluator, and it is dropped from the arithmetic when there isn't one, i.e. with alerting HA **on** or `alerting.enabled: false`. The chart enforces the budget at render time and refuses to install with the arithmetic spelled out, so raising `replicas` past that point means lowering `database.maxOpenConn` (or lowering `replicas`). With alerting HA on, rule evaluation runs on this tier rather than on a workload of its own, so a large rule set may need `resources.maxCpu` raised above the default — `alerting.resources` is unused in that mode. Signup, anonymous access and upstream analytics are disabled by the template and are not configurable. ### Admin Credentials and Encryption Key Both credentials live in [opaque secrets](/guides/create-secret/opaque) you create before installing (see [Prerequisites](#prerequisites)) — neither ever passes through Helm values. They sit next to each other in `values.yaml` but their lifecycles are opposites: | | `admin.passwordSecretName` | `admin.secretKeySecretName` | | ---------------------------- | -------------------------------------------------------------------- | ------------------------------------------------ | | What it holds | The `admin` login password | The key encrypting stored datasource credentials | | When Grafana reads it | Only when the admin account is **first created** | On **every boot**, on every instance | | Can you stop referencing it? | Yes — set `admin.applyPassword: false` | **No.** There is no toggle | | Can you rotate or delete it? | Yes, once `applyPassword` is `false` — change the password in the UI | **Never** | Every Grafana workload carries the admin bootstrap environment deliberately. Grafana's built-in default password is the literal string `admin`, so an instance without it that won the race for the empty database would create an `admin`/`admin` account on a publicly exposed UI. Testing confirms that does not happen: `admin`/`admin` is refused with a `401` on a fresh install. The encryption key is permanent. Every instance in every location decrypts saved datasource credentials with it, so changing its payload makes all of them unreadable everywhere — and alert rules that query those datasources then fail. Back the key up outside Control Plane instead of rotating it. ### Datasources as Code `datasources.definitions` entries are standard [Grafana datasource provisioning](https://grafana.com/docs/grafana/latest/administration/provisioning/#data-sources) entries, rendered verbatim into a provisioning file that every instance of every Grafana workload mounts at boot. Applying the same file concurrently from every instance is safe — testing at ten instances produced exactly one row per definition. Credentials never go into the provisioning file. Put them in a [dictionary secret](/guides/create-secret/dictionary) created before installing, list it under `datasources.credentialSecrets`, and reference each key as `$KEY`: ```yaml theme={null} datasources: definitions: - name: AppDB type: postgres url: my-db-host:5432 user: grafana_reader jsonData: { database: appdb, sslmode: disable } secureJsonData: password: $PG_PASSWORD credentialSecrets: - name: my-grafana-ds-credentials keys: [PG_PASSWORD] ``` Each listed key is exposed to every Grafana workload as an environment variable and interpolated when Grafana loads the file; the chart grants the identity `reveal` on exactly that secret. Provisioned datasources are read-only in the UI — change the value and upgrade to change one. **Substitute both parts of an in-GVC datasource URL — the workload name *and* the GVC name.** A leftover placeholder resolves to nothing, and the panel shows only `upstream connect error ... connection timeout` with no hint at the cause. A name that does not resolve times out rather than failing fast, so this looks like a network or firewall problem and is not one. ### SMTP **Authenticated SMTP requires a relay that offers STARTTLS or TLS.** Grafana refuses to send credentials over an unencrypted connection — it fails with `unencrypted connection` and **every notification is lost**, with the only signal a log line in whichever workload does the sending. Hosted relays (SES, SendGrid, Mailgun, Microsoft 365, Gmail) are unaffected; a plain in-GVC relay is not. Leave `smtp.user` empty to send unauthenticated against such a relay. Grafana sends alert notification emails through the SMTP server you configure, and **whichever instance evaluates is what actually sends them** — the dedicated evaluator by default, or the coordinating UI instance when alerting HA is on. `smtp.passwordSecretName` is required whenever `smtp.user` is set, and the password stays in a pre-created [opaque secret](/guides/create-secret/opaque) with encoding `plain`. Leave `smtp.enabled: false` if you use webhook or chat contact points instead. ### Access `publicAccess.enabled` applies to the **UI tier only**. When it is on, the tier is served on the canonical `*.cpln.app` HTTPS endpoint and Grafana's `root_url` is derived from it automatically, including for the evaluator, so links in delivered notifications point at the UI tier and open in a browser. The dedicated evaluator itself is never reachable from the internet — it gets a canonical endpoint but requests to it are refused with a `403`. With alerting HA on there is no evaluator workload, and the Redis tier is reachable from inside the GVC only. | `internalAccess.type` | Description | | --------------------- | ------------------------------------------------------------------------ | | `none` | No internal access. | | `same-gvc` | Allow access from all workloads in the same GVC (default). | | `same-org` | Allow access from all workloads in the same organization. | | `workload-list` | Allow access only from the workload links in `internalAccess.workloads`. | A firewall change is not instant. Turning public access off was measured at about 107 seconds after the new workload version was serving. Allow up to about four minutes and re-test before concluding a setting did not apply. ### App Database The `postgresML` block configures the bundled [`postgres-multi-location`](/template-catalog/templates/postgres-multi-location) cluster: one primary, asynchronous replicas in the other locations, an HAProxy leader-routing tier in each, and automatic promotion in a surviving location. Everything the database tier can do — pooling, restores, emergency quorum recovery, per-member addressing — is documented on that template's page. `postgresML.primaryLocation` is a **preferred** location for the primary: the members elsewhere wait up to 90 seconds for it to initialize the cluster before bootstrapping themselves, and it also biases later elections. Keep it aligned with `alerting.location` so the evaluator's queries have no cross-region hop. With alerting HA on it also decides which Grafana location runs the schema migrations first. Grafana has **no read/write splitting** — every query, including every dashboard load, goes to the single primary. Testing confirms the shape directly: with seven Grafana instances running, all application connections were on the primary and both standbys carried none. Every location except the primary's therefore pays one cross-region round trip per query, so set `primaryLocation` where most of your users are. #### etcd History Compaction `postgresML.etcd.tuning.autoCompactionMode`, `postgresML.etcd.tuning.autoCompactionRetention` and `postgresML.etcd.tuning.quotaBackendBytes` control how much revision history the database's consensus store keeps and how large its backend may grow. Compaction has been enabled in every version of the bundled etcd chart; since template version `1.1.1` the values are also configurable here. The defaults — `periodic`, `1h` and `0` (etcd's own 2 GiB limit) — are the right settings for a Patroni consensus store and should be left alone: an etcd cluster that fills its backend goes read-only, which takes the database's failover with it. See [Compaction and Backend Growth](/template-catalog/templates/etcd-multi-location#compaction-and-backend-growth) for the mechanism and the accepted value formats. ### Alerting-HA Coordination The `redisML` block configures the bundled [`redis-multi-location`](/template-catalog/templates/redis-multi-location) subchart — one Redis and one Sentinel per location, coordinating exactly-once notification delivery. It is rendered **only** when `alerting.highAvailability.enabled` is `true`, and ignored entirely otherwise. * `redisML.redis.replicasPerLocation` — Redis members per location. One is plenty: the data is a few kilobytes of coordination keys. * `redisML.redis.volumeset.initialCapacity` — GiB per member, at the platform minimum of `10`. * `redisML.{redis,sentinel}.image` / `.resources` — Passed through to the subchart. These blocks expose only a limit, so they use bare `cpu` and `memory`. * `redisML.{redis,sentinel}.passwordSecretName` — Optional authentication, empty by default. See below. #### Authenticating the Redis Tier **Authentication is off by default: enabling alerting HA is one flag and needs no extra secrets.** The tier is reachable only from inside the GVC this chart creates, and that same-GVC firewall is the boundary it relies on. Turn authentication on if you deploy anything else into that GVC. The firewall is a real boundary but not one the chart controls after install, and write access to an authless Redis is enough to **suppress your alert notifications** — a hostile or simply buggy neighbor can claim another peer already sent them. Alert coordination is precisely the thing you do not want failing quietly. Create either or both [opaque secrets](/guides/create-secret/opaque) with encoding `plain` **before installing**: ```bash theme={null} printf '%s' "$(openssl rand -hex 32)" | cpln secret create-opaque --name my-grafana-redis-password --encoding plain -f - printf '%s' "$(openssl rand -hex 32)" | cpln secret create-opaque --name my-grafana-sentinel-password --encoding plain -f - ``` Then name them: ```yaml theme={null} redisML: redis: passwordSecretName: my-grafana-redis-password sentinel: passwordSecretName: my-grafana-sentinel-password ``` The two are independent — setting one without the other is valid. Grafana authenticates with `ha_redis_password` and `ha_redis_sentinel_password`, reading the **same secrets** the Redis tier reads, so the two sides cannot drift apart. Testing confirmed both ports reject unauthenticated clients (`NOAUTH`) and a wrong password (`WRONGPASS`) on a fresh install with the passwords set from the first boot, and again after authentication is turned on for an already-running authless install. Rotating the Sentinel password was verified the same way — the new password is accepted and the old one rejected on every Sentinel — and turning a password back off also takes effect, after which that port accepts unauthenticated clients again. In the authenticated runs all six Grafana instances registered their peer keys inside the authenticated keyspace. The fresh-install result was measured through this template; enabling, rotating and removing a password afterwards was measured on the `redis-multi-location` subchart's own test run. **If you do set these names, a name that does not resolve stops the Grafana UI, not just alerting** — Grafana mounts the same secret. `cpln helm install` still reports **success**; the Redis, Sentinel and Grafana UI tiers then sit at **0 replicas** with no containers and therefore no logs. The explanation is on each workload's `status.versions[].message` — not the top-level `status.message`, which is empty — and reads `The secret no longer exists. Workload updates are paused until the secret is added or the reference to the secret removed.` Create the missing secret and it recovers on its own in about 5–6 minutes; no Helm action is needed. ## Alert Evaluation `alerting.enabled` is the parent switch, on by default. Setting it to `false` creates no evaluator workload and turns rule execution off on every instance: alert rules and contact points can still be created and viewed, and nothing ever evaluates them. Testing measured exactly that — two always-firing rules present, both reporting a null evaluation timestamp, and **zero notifications over 6 m 24 s** — while the UI, logins and dashboards were unaffected. Combining `alerting.enabled: false` with `alerting.highAvailability.enabled: true` is a contradiction and fails at render time. Everything below concerns the two shapes available while alerting is on. Grafana's memberlist alerting HA coordinates instances over a UDP gossip channel, which is not available between workloads on Control Plane; Grafana's **Redis-backed** coordination needs no peer port at all, and that is what `alerting.highAvailability.enabled` turns on. ### Choosing an Alerting Mode | | `highAvailability.enabled: false` (default) | `highAvailability.enabled: true` | | -------------------------------------------------------- | -------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | | Who evaluates rules | The dedicated `{release}-grafana-alerting` workload — 1 replica, 1 location | **Every UI instance**, in every location | | Exactly-once delivery | A property of the topology: there is only one evaluator | Redis-coordinated: peers order themselves and share a notification log | | Extra tier | None | 1 Redis + 1 Sentinel **per location** | | Losing a location | Alert evaluation **stops** until you repoint `alerting.location` and upgrade | Evaluation **continues** in the surviving locations (derived — see the note below; a true region outage cannot be simulated here) | | Minimum locations | 2 | **3** — the chart refuses to render below that | | **Data-source query load** | **1×** | **(locations × replicas)×** | | Silences | Expected not to propagate reliably — see the workaround below (not exercised in our testing) | Expected to propagate through the shared peer (upstream behaviour, not exercised in our testing) | | Where `alerting.location` and `alerting.resources` apply | Both | Neither — they are ignored | ### Read This Before Enabling HA **Turning HA on multiplies your data-source query load by the number of Grafana instances.** Every instance evaluates every rule against your Prometheus, Mimir or SQL server; Redis dedupes the **notification**, never the **query**. At the default 3 locations × 1 replica that is **3× the queries, permanently**. If your data source is already the bottleneck, that is a worse trade than losing alert evaluation when a region dies — leave the knob off. The other costs, at three locations and defaults: **+6 containers and +6 × 10 GiB volumes** for the Redis tier (minus the one evaluator container), constant cross-region heartbeat traffic to whichever location holds the Redis master, and rule evaluation landing on the UI tier's `resources` rather than on `alerting.resources`. ### Dedicated Evaluator Mode (the Default) Rule execution is disabled on the UI tier and enabled on a separate workload pinned to one replica in `alerting.location`, with zero replicas in every other location. Nothing is elected at runtime, and no value of `replicas` can produce a second evaluator. `alerting.location` is **required** in this mode and must name one of `global.gvc.locations`. Verified in testing at `replicas: 3` across three locations: all **9 UI instances** report rule execution disabled, the **single evaluator** reports it enabled, and the other two locations run no evaluator replica at all. In a later run at the default `replicas: 1`, an always-firing rule produced **41 notifications with 41 distinct request IDs over 26 minutes**, every one of them from the evaluator's own address. * **Losing the evaluator's replica self-heals, and re-notifies.** The platform reschedules it and evaluation resumes with no operator action — a gap of **21 seconds** between the last notification from the old replica and the first from its replacement (measured on 1.0.0, before the database gate was extended, so expect this to be a floor rather than a ceiling), with the UI tier unaffected throughout. Each replacement boots with an empty notification log and immediately re-sends whatever is currently firing, so a restart costs **duplicate** notifications rather than missed ones. * **Losing that whole location does not self-heal.** Alert evaluation stops until you run `helm upgrade` with `alerting.location` set to a surviving location, and the UI gives no sign of it — dashboards look perfectly healthy while nothing is being evaluated. This is the failure alerting HA exists to remove. * **Moving `alerting.location` does not open a two-evaluator window.** Only the evaluator workload is updated; the UI tier is untouched and does not restart. In the measured relocation the old evaluator's last notification preceded the new evaluator's readiness by **57 seconds** (measured on 1.0.0, same caveat), so the two never overlapped. Expect a gap of a few minutes with no evaluation while the new one boots. * **Silences are not expected to propagate between instances** (not exercised in our testing). Without gossip, a silence created against the UI tier is not guaranteed to be honored by the evaluator, so create silences against the evaluator directly: ```bash theme={null} curl -u admin:PASSWORD http://RELEASE_NAME-grafana-alerting.GVC_NAME.cpln.local:3000/api/alertmanager/grafana/api/v2/silences ``` The API requires credentials, so the `-u` flag is not optional. **The evaluator only answers from inside `alerting.location`.** Its internal name resolves to the GVC address from every location, but the other locations have no local upstream for it and return `503`. Run the silence command from a workload replica that is running in `alerting.location`. ### Redis-Coordinated Alerting HA With `alerting.highAvailability.enabled: true` no evaluator workload is created. Every UI instance evaluates every rule, registers itself as a peer in the stretched Redis tier, and the peers order themselves so that exactly one **sends** the notification. Each instance selects its own local Sentinel and all of them resolve the same Redis master, across regions. Configure the tier — including optional authentication — under [`redisML`](#alerting-ha-coordination). Measured across three locations at `replicas: 2` (six evaluating instances): | What was measured | Result | | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Exactly-once delivery | On the shipped authless default, **19 deliveries / 19 firing cycles / 19 distinct request IDs over 9 m 30 s**, against **114** if the six instances were uncoordinated. A run with the Redis tier authenticated measured **21 / 21 / 21 over 9 m 59 s** against **126** | | Notification links | `externalURL`, `generatorURL` and `silenceURL` are all the UI tier's canonical endpoint and return `200` when authenticated | | Redis unhealthy | Degrades to **duplicate** notifications — one per instance — and **never goes silent**; the longest gap during a five-minute Redis outage was 15 s, and it re-converged about **40 s** after the fault stopped, with no operator action | | A `helm upgrade`, including a no-op | **80–95 s** of duplicate notifications while the Redis tier is patched and peers churn (4 extra notifications over a 93 s window), then exactly-once resumes | **"We're getting every alert twice" almost always means the Redis tier is unhealthy, not that your alert rules are wrong.** Check the `{release}-redis` and `{release}-sentinel` workloads first. The failure direction is always duplicates, never silence. Two more properties to design around: * **Which location sends is not pinnable.** The sender is chosen by sorted peer name, and a standard workload's name carries a ReplicaSet hash that changes on every rollout — the elected sender moved from `aws-us-east-1` to `aws-us-west-2` between two installs of the same chart. Do not build routing, filtering or egress-IP allowlisting on the assumption that a particular location sends. * **After a location is lost, hand-off is not instant — and this one is derived, not timed.** Two things were measured directly: the Redis peer keys carry a **5-minute TTL**, so a dead location's peers keep their positions until it expires, and a location other than the previous sender was observed taking alerting over on its own after a rollout reordered the peers. The loss *event* itself has never been observed — no platform primitive simulates a true region outage — so "alerting continues from the survivors and only the sending instance changes" follows from those two measurements rather than from a timed failover. On the same basis, expect roughly **30 s** of extra delay before the first notification from a survivor, decaying to zero as the dead peer keys expire (derived from the 5-minute TTL and Grafana's peer-timeout default, not timed). ## Cross-Location Behavior Every instance shares one database, so there is no replication step between a write in one location and a read in another. Verified across three locations at `replicas: 2` — six UI instances plus the evaluator, each result attributed to a named individual instance: | What was tested | Result | | ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | | Dashboard created in one location | Read back **byte-identical** from all six UI instances and the evaluator, including the internal id | | Edit made in a second location | Authoritative everywhere, including the location it was created in — writes flow in both directions | | User and org created in one location | Log in successfully against every other instance (a wrong password returns `401` as the control) | | Session cookie issued in one location | Accepted as the only credential by every other instance, returning the correct identity — **no session affinity is needed** | | Two replicas in the same location | Each sees the other's writes | | Datasource credentials | Decrypt and connect successfully on **every** instance, because all of them share `GF_SECURITY_SECRET_KEY` | Cross-region visibility was bounded at roughly 1–3 seconds by the measurement resolution rather than by the system. **The public endpoint is proximity-routed, not round-robin.** In a 100-request tabulation, all 100 requests from one client were served by a single location — the one nearest the client. Several locations give you regional redundancy and locally served traffic, not a request spread across regions. ## Connecting Substitute your release name and the GVC name from `global.gvc.name`. | What | Where | | --------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | | Grafana UI / API (public) | `status.canonicalEndpoint` of `{release}-grafana` — `cpln workload get RELEASE_NAME-grafana --gvc GVC_NAME -o yaml` | | Grafana UI / API (internal) | `RELEASE_NAME-grafana.GVC_NAME.cpln.local:3000` | | Alert evaluator (internal, own location only) | `RELEASE_NAME-grafana-alerting.GVC_NAME.cpln.local:3000` — dedicated-evaluator mode only | | App database (always the current primary) | `RELEASE_NAME-postgres-proxy.GVC_NAME.cpln.local:5432` | | Alerting Redis (internal only) | `replica-0.RELEASE_NAME-redis.LOCATION.GVC_NAME.cpln.local:6379` — alerting HA only | | Alerting Sentinel (internal only) | `replica-0.RELEASE_NAME-sentinel.LOCATION.GVC_NAME.cpln.local:26379`, master name `mymaster` — alerting HA only | | Admin login | `admin.user`, with the password in the secret named by `admin.passwordSecretName` — `cpln secret reveal SECRET_NAME -o json` | Health and readiness are served at `/api/health`, which reports the Grafana version and the app-database status. The [Grafana HTTP API](https://grafana.com/docs/grafana/latest/developers/http_api/) is available on the same endpoint for scripted dashboard, datasource and alert-rule management. An in-GVC request to `RELEASE_NAME-grafana.GVC_NAME.cpln.local:3000` is always served by an instance in the **caller's own** location. That is why a single write-then-read from one client proves nothing about cross-location state. ## Availability and Planned Outages Measured on three locations (`aws-us-east-1`, `aws-eu-central-1`, `aws-us-west-2`). | Event | Measured impact | | --------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Cold install to all tiers ready, `replicas: 1` | **5 m 07 s** — measured at chart defaults with the primary in place. HAProxy and etcd first, then the Patroni cluster, then Grafana's 713 schema migrations (about 5 s with the primary local) | | Cold install at `replicas: 2` | About **5 m 30 s** for every replica to report ready — a shipped-code run at that setting measured 5 m 26 s with the primary in place | | Grafana-only change (for example `replicas: 1` → `3`) | **Zero downtime** — 1160 probe samples across four endpoints, no failures (measured on 1.0.0; the rollout is surge-based and unchanged since). **Does not apply to the first upgrade after an install**, which re-applies every tier once regardless of what changed | | Cold install with alerting HA on, `replicas: 2` | **4 m 22 s**, **4 m 44 s** and **6 m 11 s** in three separate runs, with the Redis and Sentinel tiers ready about a minute in | | Any `helm upgrade` that touches the database tier | The database reported unavailable for about **2 m 17 s**, and the Grafana tier took **5 m 1 s** to finish rolling every replica — measured on 1.1.1 with alerting HA on at `replicas: 2`. An earlier build whose primary bootstrapped outside `primaryLocation` was worse: about **4 minutes** to recover, one location out for **5–6 minutes**. Treat that as the worst case rather than the expected one | | Any `helm upgrade` with alerting HA on, including a no-op | **80–95 s** of duplicate alert notifications while the Redis tier is patched and the peers churn; never silence | | Loss of the evaluator's replica (HA off) | **21 s** without alert evaluation, then the replacement re-notifies whatever is firing; the UI tier is unaffected — measured on 1.0.0 before the database gate was extended, so treat it as a floor | **Treat every `helm upgrade` as a planned outage rather than a rolling one.** The bundled database members do not restart one at a time — the field that would serialize the rollout is not retained by the platform, so they go down together. Grafana's readiness probe is `/api/health`, which reports the database, so while the database is down the whole Grafana tier drops out of the load balancer and returns `503`. Changes confined to the Grafana workloads do not have this cost. **An upgrade that adds a new secret reference can pause the rollout for about 9–10 minutes while `helm upgrade` reports success.** Affected locations show `The identity ... is not allowed to reveal the secret ...` even though the grant is already in place and visible in `cpln secret access-report` — there is nothing to fix in the policy. It **clears itself with no action** (measured 9 m 0 s – 9 m 30 s), and no Helm action is required. Any change that introduces a secret reference can trigger it — for example setting `admin.applyPassword` back to `true`, enabling `smtp` with a password, adding `datasources.credentialSecrets`, or turning on `postgresML.backup` in `wal-g` mode, which wedged one location of the database tier for about 10 minutes in that subchart's own testing; because the wedged location held the primary on the pre-backup spec, **nothing was archived** for that window. It also means Helm reporting success is not evidence the new version is running — check `cpln workload get-deployments`. **A `Failed to lock database` line during a cold start is expected, and it clears itself.** Grafana runs its schema migrations under a non-blocking lock with no retry, so an instance that arrives while another holds the lock exits; the platform restarts it and the next attempt succeeds against the already-migrated schema. The chart staggers the UI instances so they do not arrive together, which removes these restarts at the default one replica per location. Two replicas in the **same** location still start together, so a few remain at `replicas: 2` — two three-location runs at that setting measured **2 and 3** container restarts, most of them this lock race. They are far more numerous when the database primary does not land in `postgresML.primaryLocation`, because the migrations then run cross-region and instances keep restarting while they finish — **15** at `replicas: 1`, against **0** at that same setting with the primary in place. Nothing needs doing either way. ## Backing Up Backups are disabled by default and cover the app database — the dashboards, users, alert rules and saved datasources that make up your Grafana. Enable them with `postgresML.backup.enabled: true`, choose `logical` (a scheduled `pg_dumpall` cron workload in the one location named by `postgresML.backup.location`) or `wal-g` (continuous archiving from whichever member is currently the primary), and complete the storage setup for your provider **before** installing. Backups come from the `postgres-multi-location` subchart and were not exercised in this template's own testing; the results below are from that template's test runs. **AWS S3** and **MinIO / S3-compatible** were exercised in both `logical` and `wal-g` modes at the shipped settings. **Google Cloud Storage** works but is memory-sensitive: it failed at `postgresML.backup.resources.memory: 128Mi` and passed at 256Mi (logical) and 512Mi (wal-g) — this template ships **512Mi**, at or above the values proven for both modes, which is why the values comment warns against lowering it. Both restores have been verified end to end there: a **wal-g restore** (base backup plus WAL replay into an empty data directory, checksum-identical to source) and a **logical restore** (`pg_dumpall` into a clean cluster, 0 errors, roles and sequences preserved). Rehearse your restore procedure before you rely on it. Create an S3 bucket. Set `postgresML.backup.aws.bucket` and `postgresML.backup.aws.region` to match. If you do not have one, [create a Cloud Account](/guides/create-cloud-account) for the AWS account holding the bucket. Set `postgresML.backup.aws.cloudAccountName` to its name. Create an IAM policy with the JSON below (replace `YOUR_BUCKET_NAME`), then set `postgresML.backup.aws.policyName` to the policy's name. This bucket-scoped policy is all the identity needs — no broad managed policy is required. ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket", "s3:GetObjectVersion", "s3:DeleteObjectVersion" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET_NAME", "arn:aws:s3:::YOUR_BUCKET_NAME/*" ] } ] } ``` Set `postgresML.backup.aws.prefix` to the folder path within the bucket. Create a GCS bucket. Set `postgresML.backup.gcp.bucket` to its name. If you do not have one, [create a Cloud Account](/guides/create-cloud-account) for the GCP project. Set `postgresML.backup.gcp.cloudAccountName` to its name. Add the **Storage Admin** (`roles/storage.admin`) role to the service account associated with the Cloud Account. The chart additionally binds `roles/storage.objectAdmin` on exactly the bucket named in `postgresML.backup.gcp.bucket`. Set `postgresML.backup.gcp.prefix` to the folder path. Create the bucket on the server. Set `postgresML.backup.minio.bucket` to its name. No Cloud Account is needed. Set `postgresML.backup.minio.endpoint` to the S3 API address including the port. For the [MinIO](/template-catalog/templates/minio) template in the same GVC, that is `http://WORKLOAD_NAME:9000`. **The endpoint must be reachable from every location.** In `wal-g` mode every member runs `restore_command`, so a MinIO workload that runs in only one location leaves the members in the other locations in a permanent restart loop — silently, because the leader stays healthy and writes keep succeeding. Run your S3-compatible endpoint in every location of the GVC, or use S3/GCS, which are global. Create a [dictionary secret](/guides/create-secret/dictionary) and set `postgresML.backup.minio.credentialsSecretName` to its name, then set `postgresML.backup.minio.prefix` to the folder path: ```bash theme={null} cpln secret create-dictionary --name my-grafana-minio-credentials \ --entry accessKey=MINIO_ACCESS_KEY \ --entry secretKey=MINIO_SECRET_KEY ``` ## Important Notes * **Create the admin password, encryption key and database credentials secrets before installing.** The chart creates none of them; without them the deployment waits on secrets that do not exist while `helm install` reports success. * **The GVC in `global.gvc.name` must not already exist.** Helm adopts an existing one and deletes it on uninstall, taking every unrelated workload with it. * **Never rotate or delete the encryption key.** Every instance in every location decrypts stored datasource credentials with it; changing it makes them all unreadable and alert rules that query them fail. * **Every `helm upgrade` that touches the database tier is a planned outage.** The database went unavailable for about 2 m 17 s and the Grafana tier took about 5 minutes to finish rolling every replica. A build whose primary bootstrapped outside `primaryLocation` was worse — about 4 minutes to recover, one location 5–6 minutes — so treat that as the worst case. The first upgrade after any install costs this even for a Grafana-only change. Changes confined to the Grafana workloads roll with zero downtime. * **An upgrade that adds a secret reference can pause the rollout for about 9–10 minutes** while Helm reports success. It clears itself with no action, and the grant is already in place — there is nothing to fix in the policy. Enabling `smtp`, `datasources.credentialSecrets` or `postgresML.backup` are examples that trigger it. * **Alert evaluation stops if you lose `alerting.location`, and the UI will not show it.** Repoint the knob and upgrade; that restarts only the evaluator. Set `alerting.highAvailability.enabled: true` to remove that failure — read the query-load cost in [Alert Evaluation](#alert-evaluation) first. * **Silences must be created against the evaluator, from a workload in `alerting.location`** — its internal address returns `503` from every other location. With alerting HA on there is no evaluator and this does not apply. * **`alerting.location` is required while alerting is on and HA is off**, and it is ignored with HA on. It is no longer a disable switch: use `alerting.enabled: false` to stop rule evaluation entirely, which also fails at render if combined with `alerting.highAvailability.enabled: true`. * **Turning alerting HA on or off changes which workloads exist.** Enabling it deletes the `{release}-grafana-alerting` workload and adds a Redis and a Sentinel workload per location; disabling it does the reverse. It is a normal `helm upgrade`, but treat it as a planned change, not a toggle to flip during an incident. * **Alerting HA multiplies data-source query load by (locations × replicas)** — 3× at the defaults — because every instance evaluates every rule and Redis dedupes the notification, not the query. * **Alerting HA is blocked below 3 locations, on purpose.** Sentinel elects a master by a majority of locations, so at 2 locations losing either one leaves no quorum. * **With alerting HA on, an unhealthy Redis means duplicate notifications, never silence.** Check the `{release}-redis` and `{release}-sentinel` workloads before suspecting your alert rules. * **With alerting HA on, a `helm upgrade` costs 80–95 s of duplicate notifications**, even an upgrade that changes nothing, and post-outage sender hand-off is not instant — the Redis peer keys' 5-minute TTL was measured, but the hand-off itself is derived from it rather than timed against a real outage. * **Which location sends notifications is not pinnable with alerting HA on.** The sender is chosen by sorted peer name and moved between regions across two installs of the same chart, so do not route, filter or egress-allowlist on it. * **The alerting-HA Redis tier is unauthenticated by default**, reachable only from inside this chart's GVC. If you deploy anything else into that GVC, authenticate it — write access to it is enough to suppress alert notifications. See [Authenticating the Redis Tier](#authenticating-the-redis-tier). * **If you do set `redisML.redis.passwordSecretName` or `redisML.sentinel.passwordSecretName`, a wrong name stops the Grafana UI too**, because Grafana reads the same secret — and `helm install` still reports success. If a tier sits at 0 replicas after install, read `status.versions[].message` on the workload. * **Use the canonical `*.cpln.app` endpoint, not a per-location hostname.** Grafana is configured with a single absolute `root_url` (the canonical endpoint). On a per-location hostname the UI and dashboard layout load, but the POST that fetches panel data is rejected — you get a dashboard with empty panels and no error shown. The canonical endpoint is georouted and already serves from the nearest location. * **A provisioned datasource reporting `upstream connect error ... connection timeout` is almost always an unsubstituted placeholder in its URL**, not a network or firewall problem. Check the GVC segment of `datasources.definitions[].url` first — a name that does not resolve times out rather than failing fast, and nothing in the Grafana UI names the cause. * **The public endpoint is proximity-routed.** More locations means regional redundancy and locally served traffic, not requests spread across regions. * **Every location except the database primary's pays a cross-region round trip per query**, because Grafana has no read/write splitting. Set `postgresML.primaryLocation` where most of your users are. * **Scaling `replicas` has no alerting-related restriction** — it applies to the UI tier only, in every location including `alerting.location`. Watch the connection budget instead. * **`replicas: 2` or higher lengthens a cold install** to roughly five and a half minutes. Nothing is wrong; there is simply more to schedule. * **Grafana Live has no HA engine here**, so a live-streamed message reaches only the browsers connected to the same instance — carried over from the single-location [Grafana](/template-catalog/templates/grafana) template's multi-replica behaviour, and not exercised in this template's own testing. Dashboard auto-refresh, queries, alerting, provisioning, login and the API are unaffected. * **With `publicAccess.enabled: false`, links in alert notifications point at the internal GVC address** and will not open from a browser outside the GVC. * **Never suspend a location.** Suspending and resuming one permanently withdraws its endpoints from the other locations' service discovery while every status surface still reports healthy. Remove a location from `global.gvc.locations` instead. * **Uninstall deletes every volume set the chart created** — the database, etcd and, with alerting HA on, Redis and Sentinel — along with the GVC. Enable backups if the data matters. Prerequisite secrets you created are not owned by the release and survive it. * **This template ships Grafana OSS only** — Enterprise features such as fine-grained RBAC, reporting and query caching are not available. ## External References Official Grafana documentation Alert rules, contact points, notification policies, and silences How Grafana coordinates alert evaluation across instances Reference for the datasource provisioning entries used by this template Every Grafana setting and its environment-variable name Manage dashboards, datasources, and alert rules programmatically View the source files, default values, and chart definition # Hermes Agent Source: https://docs.controlplane.com/template-catalog/templates/hermes-agent Deploy Hermes Agent by Nous Research on Control Plane using the Template Catalog. A self-hosted, model-agnostic AI agent wrapping any LLM with persistent memory, browser automation, an OpenAI-compatible gateway API, and a web dashboard. ## Overview Hermes Agent is a self-hosted AI agent from Nous Research. You bring the model — an API key for Anthropic, OpenAI, or any OpenAI-compatible endpoint — and the agent brings the memory, tools, and interfaces around it. This template deploys the supervised gateway with an OpenAI-compatible API for your clients and an internal-only web dashboard, with all agent state on a persistent volume. ### Architecture * **Hermes Agent** — A single-replica stateful workload running the supervised gateway. It serves the OpenAI-compatible API on port `8642` and the web dashboard on port `9119`. Headless Chromium for browser automation is baked into the image and launched on demand. * **Model** — External. Every completion is dispatched to your LLM provider using a key you supply; the workload holds no model weights. ### What Gets Created * **Stateful Hermes Agent Workload** — Single replica. Gateway API on port `8642` (bearer auth, optionally public); web dashboard on port `9119` (internal only, basic auth). * **Volume Set** — 10 GiB persistent storage at `/opt/data` for the SQLite memory database, sessions, learned skills, and agent config. Supports optional autoscaling. * **Identity & Policy** — An identity bound to the workload with `reveal` access to exactly the one prerequisite secret, and nothing else. This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites Hermes Agent needs an LLM API key and a dictionary secret that you create **before** installing. No credential is passed through values. Obtain a key from your provider — Anthropic, OpenAI, or any OpenAI-compatible endpoint (OpenRouter, Ollama, vLLM, LM Studio, a proxy) reached via `provider: custom`. Generate the bearer token your clients will present to the gateway API. It **must be at least 16 characters** — generate one with `openssl rand -hex 32`. Create a [dictionary secret](/guides/create-secret/dictionary) holding the three values below. **Name the keys however you like** — map them with `secret.keys` at install, so an existing secret works unchanged. Pass the secret's name as `secret.name`. | Value | Required | Maps to | | ---------------------------------------------------------------- | ----------------------------- | ------------------------------- | | LLM API key for your provider | Yes | `secret.keys.apiKey` | | Bearer token clients present to the gateway API (≥16 characters) | Yes | `secret.keys.apiServerKey` | | Dashboard basic-auth password | When the dashboard is enabled | `secret.keys.dashboardPassword` | Hermes rejects an API server key shorter than 16 characters. The gateway still starts, but the API never serves and the workload never becomes ready. Once your secret exists, install the template using your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} image: nousresearch/hermes-agent:v2026.7.7.2 model: provider: anthropic # anthropic | openai | custom (any other OpenAI-compatible endpoint) name: "" # model override (e.g. claude-opus-4.6, gpt-5); empty = provider default baseUrl: "" # OpenAI-compatible endpoint; required when provider is "custom" reasoningEffort: medium # none | low | medium | high — use "none" for non-reasoning models secret: # the dictionary secret you created (see Prerequisites) name: my-hermes-secret # name of your existing dictionary secret keys: # point each field at the key in YOUR secret that holds it apiKey: api-key # LLM API key for your provider apiServerKey: api-server-key # bearer token clients present to the gateway API dashboardPassword: dashboard-password # dashboard password (used when dashboard.enabled) dashboard: enabled: true # internal-only web UI on port 9119 username: admin # basic-auth username (password comes from the secret) resources: minCpu: 500m # idle floor — cpu:minCpu ratio must stay under 4:1 minMemory: 1Gi cpu: 2000m # burst ceiling (headroom for on-demand browser automation) memory: 4Gi volumeset: capacity: 10 # initial GiB (minimum 10) — memory DB, sessions, skills, config autoscaling: enabled: false # set true to auto-expand the volume as state grows maxCapacity: 100 # ceiling in GiB when autoscaling is enabled minFreePercentage: 10 # scale up when free space drops below this scalingFactor: 1.2 # multiplier applied on each scale-up publicAccess: enabled: false # expose the gateway API (8642) on the public canonical HTTPS endpoint internalAccess: type: same-gvc # none | same-gvc | same-org | workload-list workloads: [] # used with workload-list, e.g. //gvc/GVC/workload/NAME ``` ### Model * `image` — The Hermes Agent container image. * `model.provider` — The LLM provider: | Provider | Description | | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------- | | `anthropic` | Anthropic's API. Requires an Anthropic key. | | `openai` | OpenAI's API. Requires an OpenAI key. | | `custom` | **Any other OpenAI-compatible endpoint** — OpenRouter, Ollama, vLLM, LM Studio, or a proxy. Requires `model.baseUrl` and that service's key. | * `model.name` — Model override, e.g. `claude-opus-4.6`, `gpt-5`. Empty uses the provider default. Recommended for non-Anthropic providers. * `model.baseUrl` — The OpenAI-compatible endpoint. Required when `provider` is `custom`. * `model.reasoningEffort` — `none`, `low`, `medium`, or `high` (default `medium`). To reach OpenRouter or any other OpenAI-compatible service, use `provider: custom`: ```yaml theme={null} model: provider: custom baseUrl: https://openrouter.ai/api/v1 name: anthropic/claude-sonnet-4-5 ``` Hermes sends a reasoning effort with every request, and models that do not support reasoning reject it with `400: Unsupported parameter: 'reasoning.effort'`. Set `reasoningEffort: none` for those (e.g. `gpt-4o`); leave the default for reasoning-capable models (e.g. `gpt-5`, `claude-opus-4.6`). `model.baseUrl` has no effect with `provider: anthropic` — the Anthropic client ignores it. The template fails at render if you set both, so use `provider: custom` to route through a proxy. ### Secret * `secret.name` — Name of the dictionary secret you created. See [Prerequisites](#prerequisites). * `secret.keys.apiKey` / `secret.keys.apiServerKey` / `secret.keys.dashboardPassword` — The key names **within your secret** that hold each value. Override these to match an existing secret rather than renaming its keys. ### Dashboard * `dashboard.enabled` — Serve the web dashboard on port `9119`. Internal only — it is never published on the public endpoint. * `dashboard.username` — Basic-auth username. The password comes from your secret. ### Resources * `resources.minCpu` / `resources.minMemory` — The idle floor. * `resources.cpu` / `resources.memory` — The burst ceiling, giving headroom for the on-demand headless browser. The min→max spread is the elasticity: the workload idles at the floor and bursts toward the ceiling only while browser automation runs. Keep `cpu` under 4× `minCpu` — the platform rejects a wider ratio. ### Storage * `volumeset.capacity` — Initial volume size in GiB (minimum 10) for the memory database, sessions, skills, and config. * `volumeset.autoscaling.enabled` — Automatically expand the volume as state grows. When enabled: * `maxCapacity` — Maximum volume size in GiB. * `minFreePercentage` — Trigger a scale-up when free space drops below this percentage. * `scalingFactor` — Multiply the current capacity by this factor when scaling up. ### Access * `publicAccess.enabled` — Expose the gateway API on the public canonical HTTPS endpoint. Defaults to `false`. Read the security note below before enabling. * `internalAccess.type` — Internal firewall scope of the workload: | Type | Description | | --------------- | ---------------------------------------------------------------------- | | `none` | No internal access. | | `same-gvc` | Allow access from all workloads in the same GVC. | | `same-org` | Allow access from all workloads in the same organization. | | `workload-list` | Allow access only from workloads listed in `internalAccess.workloads`. | **`publicAccess.enabled: true` publishes a terminal-capable agent to the internet**, guarded only by your bearer token. The agent's terminal backend runs unsandboxed as the container user with full file access, so anyone holding the key can execute work inside the workload. It is off by default — before enabling it, use a long random API server key (`openssl rand -hex 32`) and prefer restricting reach via `internalAccess`. ## Connecting | Interface | Where | Auth | | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | | Gateway API (OpenAI-compatible) | From another workload: `{release}-hermes-agent.{gvc}.cpln.local:8642`. With `publicAccess.enabled: true`, also on the canonical HTTPS endpoint — read `status.canonicalEndpoint` from `cpln workload get {release}-hermes-agent -o yaml` | Bearer token (`secret.keys.apiServerKey`) | | Web dashboard | Internal only — `cpln workload port-forward {release}-hermes-agent --gvc {gvc} -p 9119:9119`, then `http://localhost:9119` | Basic auth (`dashboard.username` + the dashboard password from your secret) | Example request against the gateway API: ```bash theme={null} curl https://ENDPOINT/v1/chat/completions \ -H "Authorization: Bearer $API_SERVER_KEY" \ -H "Content-Type: application/json" \ -d '{"messages":[{"role":"user","content":"hello"}]}' ``` ## Messaging Platforms Hermes supports chat-platform gateways such as Telegram, Discord, and Slack. These are **configured after install** using Hermes's own interactive setup — not through this template's values: ```bash theme={null} cpln workload exec {release}-hermes-agent --gvc {gvc} --container hermes -- hermes gateway setup ``` Follow the prompts for your platform; the configuration is stored on the data volume. See the [Hermes Agent documentation](https://github.com/NousResearch/hermes-agent) for each platform's requirements, such as bot tokens. ## Important Notes * **The API server key must be at least 16 characters** — Hermes rejects anything shorter, and the workload will not become ready. * **The dashboard is internal-only** — it is never on the public endpoint; reach it via `cpln workload port-forward`. * **Single replica by design** — memory is a single-writer SQLite database and upstream forbids two gateways sharing one data directory. Do not scale up. State persists on the volume across restarts and the agent resumes; only in-flight work is lost. * **The model is external** — cost and rate limits are governed by your LLM provider, not this workload. * **Failed model calls return HTTP 200** with the error inside the body (`"finish_reason": "error"`, `"hermes": {"failed": true}`). A client that checks only the HTTP status will read a provider failure as success — inspect the body, or the agent log at `/opt/data/logs/agent.log`. * **Keep `cpu` under 4× `minCpu`** — the platform rejects a wider ratio; raise `minCpu` if you raise `cpu`. * **Resetting requires an uninstall** — changing the secret and redeploying does not wipe existing memory or config on the volume. `cpln helm uninstall` deletes the volume set. ## External References Official Hermes Agent documentation Source, issues, and release notes The team behind Hermes Agent View the source files, default values, and chart definition # Infisical Source: https://docs.controlplane.com/template-catalog/templates/infisical Deploy Infisical on Control Plane using the Template Catalog. An open-source secrets-management platform — store, version, scope, and serve application secrets through a web UI and REST API — backed by PostgreSQL and HA Redis. Covers the prerequisite root-of-trust secret, admin bootstrap, SMTP, scaling, and access. ## Overview Infisical is an open-source secrets-management platform: store, version, scope, and serve application secrets through a web UI and REST API, with projects, environments, and access controls. This template deploys a stateless Infisical server tier backed by PostgreSQL (the durable secret store) and HA Redis with Sentinel (cache and background-job queues). ### Architecture * **Infisical server** — A stateless `standard` workload serving the web UI and API on port `8080` from the standalone image. Runs its database migrations automatically on boot; scale with `infisical.replicas` (a startup lock ensures migrations run exactly once even when multiple replicas boot concurrently). * **PostgreSQL** ([postgres](/template-catalog/templates/postgres) subchart) — The durable store for all secrets, versions, projects, and users. * **Redis + Sentinel** ([redis](/template-catalog/templates/redis) subchart) — Cache and background-job queues, deployed HA (3 Redis + 3 Sentinel replicas, master `mymaster`). The server connects Sentinel-aware, so Redis failover is transparent. **Required** — Infisical will not boot without Redis. ### What Gets Created * **Standard Infisical Workload** — One or more stateless replicas serving the UI and API on port `8080`. * **Stateful PostgreSQL Workload** — Single-replica Postgres with a persistent volume set for all Infisical data. * **Redis Workloads** — A Redis master-replica set plus Redis Sentinel for cache and job queues. * **DB-credentials Secret** — A template-created dictionary secret used to assemble the server's `DB_CONNECTION_URI`. * **Identity & Policy** — An identity bound to the server with least-privilege `reveal` on exactly the secrets it mounts, including your prerequisite secret. This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites **You must create a dictionary secret before installing.** The server references it by name (`secrets.name`, default `my-infisical-secrets`) and the deployment wedges until the secret exists. Create it first as a **dictionary** secret holding Infisical's root-of-trust keys: * `ENCRYPTION_KEY` — a 16-byte hex key (`openssl rand -hex 16`) that encrypts every secret stored in Infisical. * `AUTH_SECRET` — a 32-byte base64 key (`openssl rand -base64 32`) that signs sessions and tokens. Both keys are **write-once — never rotate them**. Rotating `ENCRYPTION_KEY` corrupts every stored secret; rotating `AUTH_SECRET` invalidates all sessions. Create the secret with the CLI: ```bash theme={null} cpln secret create-dictionary --name my-infisical-secrets \ --entry ENCRYPTION_KEY=$(openssl rand -hex 16) \ --entry AUTH_SECRET=$(openssl rand -base64 32) ``` **Optional, only for authenticated SMTP:** a dictionary secret with `SMTP_USERNAME` and `SMTP_PASSWORD`, referenced via `smtp.auth.secretName`. Leave `smtp.auth.secretName` empty for unauthenticated relays and mail catchers. ```bash theme={null} cpln secret create-dictionary --name my-infisical-smtp \ --entry SMTP_USERNAME=apikey \ --entry SMTP_PASSWORD=your-smtp-password ``` Install the template using your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} infisical: # Standalone image: bundles the API + web UI on :8080 and runs DB migrations on boot. image: infisical/infisical:v0.162.14 replicas: 1 # 1 = proven single-replica shape; 2+ = HA tier (stateless app over shared pg+redis) siteUrl: "" # empty = derive from the canonical *.cpln.app endpoint; set (with https://) for a custom domain resources: minCpu: 250m maxCpu: 1000m minMemory: 512Mi maxMemory: 2Gi # Prerequisite dictionary secret — MUST EXIST BEFORE INSTALL (see Prerequisites). secrets: name: my-infisical-secrets # holds ENCRYPTION_KEY + AUTH_SECRET (both write-once) # SMTP / email (optional; off by default) smtp: enabled: false # true = enable email invites, verification, password reset host: smtp.example.com port: 587 fromAddress: no-reply@example.com fromName: Infisical requireTls: true # STARTTLS; set false for a plaintext mail catcher auth: secretName: "" # optional dictionary secret with SMTP_USERNAME + SMTP_PASSWORD; empty = no auth publicAccess: enabled: true # HTTPS UI + API on the auto *.cpln.app endpoint; false = internal-only internalAccess: type: same-gvc # options: none, same-gvc, same-org, workload-list workloads: [] # used only with workload-list # PostgreSQL — durable secret store postgres: image: postgres:18 config: username: infisical password: change-me-infisical-pg # change before installing database: infisical resources: minCpu: 250m maxCpu: 1000m minMemory: 512Mi maxMemory: 1Gi volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) # Redis + Sentinel (HA) — cache + job queues; REQUIRED redis: redis: image: redis:7.4 replicas: 3 auth: password: enabled: false # true = require AUTH; the password is wired into Infisical as REDIS_PASSWORD value: change-me-infisical-redis sentinel: image: redis:7.4 replicas: 3 ``` ### Infisical server * `infisical.image` — The standalone Infisical image (API + web UI in one container). Pin a concrete tag. * `infisical.replicas` — Number of stateless server replicas. `1` is the proven single-replica shape; `2` or more gives an HA tier over the shared PostgreSQL and Redis, with zero-downtime rolling restarts. Boot migrations are serialized by a startup lock, so they run exactly once no matter how many replicas start at once. * `infisical.siteUrl` — The public base URL Infisical advertises in links, cookies, and callbacks. Leave empty to derive it automatically from the canonical `*.cpln.app` endpoint; set it (including `https://`) only when you front the workload with a custom domain. * `infisical.resources` — CPU and memory per replica. Keep `maxMemory` at `2Gi` — a cold multi-replica boot runs out of memory at `1Gi`. ### Prerequisite secret * `secrets.name` — Name of the user-created dictionary secret holding `ENCRYPTION_KEY` and `AUTH_SECRET`. It must exist **before** install — see [Prerequisites](#prerequisites). ### SMTP / email Off by default — the zero-config install works via the no-email admin-signup bootstrap. Enable it for email invites, address verification, and password reset: * `smtp.enabled` — Turn email on. `smtp.host` and `smtp.fromAddress` are required when enabled. * `smtp.requireTls` — STARTTLS is required by default; set `false` only for a plaintext mail catcher (e.g. Mailpit on port `1025`). * `smtp.auth.secretName` — Name of an optional dictionary secret with `SMTP_USERNAME` and `SMTP_PASSWORD` for authenticated SMTP. Empty means no authentication; when set, the workload is granted `reveal` on exactly that secret. ### Access * `publicAccess.enabled` — Serve the UI and API on the canonical `*.cpln.app` HTTPS endpoint (default). Set to `false` for an internal-only instance. * `internalAccess.type` — Internal firewall scope of the server workload: | Type | Description | | --------------- | ---------------------------------------------------------------------- | | `none` | No internal access. | | `same-gvc` | Allow access from all workloads in the same GVC (default). | | `same-org` | Allow access from all workloads in the same organization. | | `workload-list` | Allow access only from workloads listed in `internalAccess.workloads`. | ### PostgreSQL * `postgres.config.password` — The bundled database password. **Change it before installing.** * `postgres.volumeset.capacity` — Initial volume size in GiB (minimum 10). PostgreSQL stores everything: secrets, versions, projects, environments, and users. ### Redis * `redis.redis.auth.password.enabled` — When `false` (default), Redis runs authless with the same-GVC firewall as the boundary. Set to `true` to require AUTH on the data nodes; the master password is then wired into Infisical as `REDIS_PASSWORD` (Sentinels stay authless). Both paths are tested end to end, including Sentinel monitoring the auth-protected master. * `redis.redis.replicas` / `redis.sentinel.replicas` — Redis and Sentinel replica counts (3 + 3 by default). Redis is required — Infisical will not boot without it, so the template hard-wires the dependency (no enable/disable toggle). ## Connecting | Target | Address | Credentials | | ------------------- | -------------------------------------------------- | --------------------------------- | | Public UI + API | `https://.cpln.app` | first sign-up becomes super-admin | | Internal (same GVC) | `http://{release}-infisical.{gvc}.cpln.local:8080` | account login / API token | | Health check | `GET /api/status` | none | The canonical `*.cpln.app` hostname appears under `status.canonicalEndpoint` (`cpln workload get {release}-infisical -o yaml`). **The first account to sign up becomes the super-admin.** There are no admin bootstrap variables — with public access enabled, create your admin account immediately after install, then disable open sign-ups in the Admin panel. ## Important Notes * **The prerequisite secret must exist before install** — `secrets.name` is a dictionary secret with `ENCRYPTION_KEY` and `AUTH_SECRET`. A missing secret wedges the deployment and makes it look broken. * **`ENCRYPTION_KEY` and `AUTH_SECRET` are write-once — never rotate them.** `ENCRYPTION_KEY` encrypts every stored secret; `AUTH_SECRET` signs all sessions. * **The first account to sign up becomes super-admin** — create it immediately after install, then disable open sign-ups in the Admin panel. * **Redis is required, not optional** — Infisical will not boot without it (authless behind the same-GVC firewall by default). * **Keep `infisical.resources.maxMemory` at `2Gi`** — a cold boot with `replicas` ≥ 2 runs out of memory at `1Gi`. * **Stored secrets live in PostgreSQL and survive an app restart or reinstall** — to wipe all data you must also reinstall the PostgreSQL dependency (which deletes its volume set). * **First boot takes roughly 2–5 minutes** — the server waits for PostgreSQL to finish initializing and runs its migrations, logging transient `Boot up migration failed` retries in the meantime. This self-heals; the workload becomes ready once migrations complete. * **Set `smtp.requireTls: false` only for a plaintext mail catcher** — real providers on port `587` need it left `true`. ## External References Official Infisical documentation Environment variable reference for self-hosted Infisical The standalone image this template deploys SMTP setup for invites, verification, and password reset View the source files, default values, and chart definition # Kafka Source: https://docs.controlplane.com/template-catalog/templates/kafka Deploy Apache Kafka on Control Plane using the Template Catalog. Covers configuration, volumes, scaling, and KRaft mode clustering with optional UI, REST proxy, and connectors. ## Overview Apache Kafka is a distributed event streaming platform designed for high-throughput, fault-tolerant publish-subscribe messaging. This template deploys a Kafka cluster in **KRaft mode** (no ZooKeeper required), using built-in Raft consensus for distributed coordination. Optional components include a Kafbat UI for cluster management, a REST Proxy for HTTP-based access, Kafka Connect for sink and source connectors, and Prometheus exporters for metrics. ### What Gets Created * **Kafka Cluster Workload** — A stateful Kafka broker cluster with configurable replicas (minimum 3). Each replica runs as a combined controller and broker. Optional sidecar containers for Kafka Exporter and JMX Exporter can be co-located on the same workload. * **Volume Set** — Persistent storage for Kafka log directories with autoscaling, snapshot retention, and optional AWS KMS encryption. * **Secrets** — Three opaque secrets: the controller configuration (KRaft quorum and SASL settings), the cluster initialization script (startup logic, listener setup, and log directory initialization), and the Kafka credentials (KRaft cluster ID, inter-broker password, controller password, and listener admin credentials). A JMX exporter configuration secret is also created when JMX exporter is enabled. * **Identity & Policy** — An identity bound to the cluster workload with `reveal` access to the Kafka secrets. * **Kafbat UI Workload** (optional) — A web-based Kafka management interface with its own identity, policy, and optional domain routing. Enabled when `kafbat_ui.enabled: true`. * **Kafka REST Proxy Workload** (optional) — An HTTP API gateway for producing and consuming Kafka messages without a native client. Enabled when `kafka_rest_proxy.enabled: true`. * **Kafka Connect Workload** (optional) — A worker cluster for running sink and source connectors. Configured via the `kafka_connectors` section (commented out by default). * **Kafka Client Workload** (optional) — A lightweight client workload for testing and debugging cluster connectivity from within the GVC. Enabled when `kafka_client` is uncommented in the values file. * **Domain** (optional) — Public listener routing for external Kafka access. Created when the public listener is configured, using either direct replica routing or multi-port routing. This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites Prerequisites are only required if you plan to use the public listener or Kafbat UI. Skip the relevant section if those features are not needed. ### Public Listener (External Access) To expose Kafka brokers to clients outside the GVC, you need: 1. A registered domain (e.g., `kafka.example.com`). 2. A **Dedicated Load Balancer** enabled on the GVC. This is required for external Kafka access and reduces cross-zone traffic costs when `kafka.multiZone` is enabled. 3. DNS configured as described in the [Configure Domain](https://docs.controlplane.com/guides/configure-domain) guide. ### Kafbat UI Kafbat UI requires a pre-created Control Plane secret containing its YAML configuration before the template is installed. The secret name must match the value of `kafbat_ui.configuration_secret` (default: `kafka-kafbat-ui-config`). Create the secret with a `config.yaml` key containing your Kafbat UI configuration. A minimal example connecting to the Kafka cluster with SASL authentication: ```yaml theme={null} kafka: clusters: - name: local bootstrapServers: RELEASE_NAME-cluster:9092 properties: security.protocol: SASL_PLAINTEXT sasl.mechanism: PLAIN sasl.jaas.config: >- org.apache.kafka.common.security.plain.PlainLoginModule required username="admin" password="your-admin-password"; ``` ## Installation To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} kafka: name: cluster image: apache/kafka:3.9.1 suspend: false deletionProtection: false replicas: 3 # must not be 2 minReadySeconds: 0 debug: false multiZone: false # If true: It's recommended to enable multi-zone on the Dedicated Load Balancer setting on GVC to reduce the cross-zone traffic logDirs: /opt/kafka/logs-0,/opt/kafka/logs-1 env: [] # If you need to set environment variables, add them here volumes: logs: initialCapacity: 10 # In GB performanceClass: general-purpose-ssd # general-purpose-ssd / high-throughput-ssd (Min 1000GB) fileSystemType: ext4 # ext4 / xfs snapshots: createFinalSnapshot: true retentionDuration: 7d schedule: 0 0 * * * # UTC autoscaling: maxCapacity: 1000 # In GB minFreePercentage: 20 scalingFactor: 1.2 # customEncryption: # enabled: false # region: aws-us-east-2 # Replace with the appropriate region # keyId: arn:aws:kms:us-east-2:1234567890:key/d411f35a-1d31-4515-9934-4f193e042d80 # Replace with your AWS KMS key ARN cpu: 1000m memory: 2000Mi minCpu: 250m minMemory: 2000Mi # overrideHeapOpts: "-Xmx1024m -Xms1024m" firewall: internal_inboundAllowType: "same-gvc" # Options: same-org / same-gvc (Recommended) # external_inboundAllowCIDR: 0.0.0.0/0 # inboundAllowWorkload: # - //gvc/main-kafka/workload/main-kafka-kafbat-ui # - //gvc/client-gvc/workload/client # external_outboundAllowCIDR: "111.222.333.444/16,111.222.444.333/32" listeners: client: protocol: SASL_PLAINTEXT name: CLIENT containerPort: 9092 sasl: admin: username: admin password: "your-admin-password" users: "user" passwords: "your-user-password" # public: # protocol: SASL_PLAINTEXT # name: PUBLIC # directReplicaRouting: # enabled: true # containerPort: 9095 # publicAddress: kafka.example.com # Dedicated Load Balancer must be enabled on the GVC # sasl: # users: "public-user" # passwords: "your-public-user-password" acl: superUsers: "User:admin" allowEveryoneIfNoAclFound: false secrets: kraft_cluster_id: your-kraft-cluster-id # Example: bkdDtS1Rsf536si7BGM0JY inter_broker_password: your-inter-broker-password controller_password: your-controller-password extra_configurations: default.replication.factor: 3 auto.create.topics.enable: true log.retention.hours: 168 kafka_exporter: name: exporter image: danielqsj/kafka-exporter:v1.9.0 debug: false cpu: 50m memory: 128Mi listener: client env: [] dropMetrics: [] jmx_exporter: name: jmx-exporter image: ghcr.io/controlplane-com/bitnami/jmx-exporter kafkaJmxPort: 5557 exporterPort: 5556 debug: false cpu: 250m memory: 256Mi minCpu: 80m minMemory: 125Mi listener: client dropMetrics: [] kafbat_ui: enabled: true deletionProtection: false name: kafbat-ui image: ghcr.io/kafbat/kafka-ui cpu: 300m memory: 1000Mi minCpu: 100m minMemory: 400Mi replicas: 1 timeoutSeconds: 30 configuration_secret: kafka-kafbat-ui-config # Pre-create this secret before installing # domain: kafbat-ui.example.com firewall: external_inboundAllowCIDR: "0.0.0.0/0" external_outboundAllowCIDR: "0.0.0.0/0" kafka_rest_proxy: enabled: true deletionProtection: false name: rest-proxy image: confluentinc/cp-kafka-rest:latest cpu: 500m memory: 1000Mi capacityAI: enabled: true minCpu: 125m minMemory: 200Mi replicas: 1 timeoutSeconds: 15 # domain: kafka-rest.example.com firewall: external_inboundAllowCIDR: 0.0.0.0/0 external_outboundAllowCIDR: "0.0.0.0/0" properties: bootstrap.servers: SASL_PLAINTEXT://kafka-dev-cluster:9092 resource.extension: ALL api.v3.enable: true api.v2.enable: true client.sasl.mechanism: PLAIN api.compatibility.mode: BOTH listeners: http://0.0.0.0:8082 authentication.realm: KafkaRest authentication.method: BASIC authentication.roles: user client.security.protocol: SASL_PLAINTEXT jaas_conf: KafkaClient { org.apache.kafka.common.security.plain.PlainLoginModule required username="admin" password="your-admin-password"; }; KafkaRest { org.eclipse.jetty.jaas.spi.PropertyFileLoginModule required debug="true" file="/etc/kafka-rest/password.properties"; }; password_properties: user: your-user-password,user kafka_client: name: client image: apache/kafka:3.9.1 cpu: 500m memory: 1000Mi firewall: external_outboundAllowCIDR: "0.0.0.0/0" # kafka_connectors: # Uncomment and configure to deploy Kafka Connect workers # - name: cluster # image: apache/kafka:3.9.1 # replicas: 1 # ... # See template source for full connector configuration and plugin examples ``` ### Kafka Cluster * `kafka.name` — Suffix used to form the workload name (`{release}-{kafka.name}`). * `kafka.replicas` — Number of broker replicas. Must be 3 or more and **must not be 2**. Maximum 5 replicas act as combined controller+broker; replicas beyond 5 are broker-only. * `kafka.multiZone` — When `true`, distributes replicas across availability zones. Enable the **Dedicated Load Balancer** on the GVC to reduce cross-zone traffic costs. * `kafka.resources` — CPU and memory bounds for each Kafka broker (`cpu`, `memory`, `minCpu`, `minMemory`). * `kafka.overrideHeapOpts` — Override the default JVM heap settings. If unset, the heap is derived from the configured memory. * `kafka.logDirs` — Comma-separated paths for Kafka log directories. Each path maps to a separate volume. ### Storage * `kafka.volumes.logs.initialCapacity` — Initial volume size in GB per replica (minimum 10). * `kafka.volumes.logs.performanceClass` — Storage class: `general-purpose-ssd` or `high-throughput-ssd` (minimum 1000 GB for high-throughput). * `kafka.volumes.logs.fileSystemType` — Filesystem type: `ext4` or `xfs`. * `kafka.volumes.logs.snapshots` — Snapshot configuration: `createFinalSnapshot` (taken on workload deletion), `retentionDuration` (e.g., `7d`), and optional `schedule` (cron, UTC). * `kafka.volumes.logs.autoscaling` — Automatically expand the volume as it fills: * `maxCapacity` — Maximum volume size in GB. * `minFreePercentage` — Trigger scale-up when free space drops below this percentage. * `scalingFactor` — Multiply current capacity by this factor when scaling up. #### Custom Encryption (AWS KMS) To encrypt Kafka data volumes with a customer-managed key, uncomment `kafka.volumes.customEncryption`: * `region` — AWS region where the KMS key is located (e.g., `aws-us-east-2`). * `keyId` — The full ARN of the AWS KMS key. Custom encryption can only be applied when the volume is first created. Existing volumes cannot be re-encrypted after creation. ### Firewall * `kafka.firewall.internal_inboundAllowType` — Controls which workloads can reach the Kafka cluster internally (`same-gvc` recommended, or `same-org`). * `kafka.firewall.external_inboundAllowCIDR` — CIDR ranges allowed to reach Kafka from the internet. Commented out by default. * `kafka.firewall.inboundAllowWorkload` — Explicit workload links allowed to connect (use with `same-gvc` or `workload-list`). ### Listeners #### Client Listener (Internal) The client listener is the primary access point for producers and consumers within the GVC. * `kafka.listeners.client.protocol` — Security protocol: `PLAINTEXT` or `SASL_PLAINTEXT`. * `kafka.listeners.client.containerPort` — Port for client connections (default `9092`). Automatically overridden to the range `3000–3004` when the public listener with direct replica routing is enabled. * `kafka.listeners.client.sasl.admin` — Admin username and password. The admin user is also added as an ACL superuser. **Change the default password before deploying to production.** * `kafka.listeners.client.sasl.users` / `.passwords` — Comma-separated lists of additional usernames and their passwords for client connections. #### Public Listener (Optional, External Access) Uncomment `kafka.listeners.public` to expose Kafka brokers to clients outside the GVC. Two routing approaches are supported: **Direct Replica Routing (Recommended)** Generates a subdomain per replica, enabling zone-aware routing and minimizing cross-zone traffic. Requires a dedicated load balancer on the GVC. ```yaml theme={null} public: protocol: SASL_PLAINTEXT name: PUBLIC directReplicaRouting: enabled: true containerPort: 9095 publicAddress: kafka.example.com sasl: users: "public-user" passwords: "your-public-user-password" ``` Each broker becomes reachable at a replica-specific subdomain, for example: ```text theme={null} kafka-0-aws-us-east-1.kafka.example.com:9095 kafka-1-aws-us-east-1.kafka.example.com:9095 ``` **Multi-Port Routing** Assigns one port per replica starting at 3000. Does not require per-replica subdomains but is **not recommended for multi-zone deployments** due to potential cross-zone routing charges. For external access, Kafka clients should use `SASL_SSL` as the security protocol since TLS is enforced at the load balancer level. ### ACL * `kafka.acl.superUsers` — Semicolon-separated list of Kafka superusers (e.g., `User:admin;User:connectors`). Superusers bypass ACL checks. * `kafka.acl.allowEveryoneIfNoAclFound` — When `true`, allows all operations on topics or groups that have no ACL defined. Set to `false` for strict access control. ### Secrets These values are stored as a Control Plane secret and injected into the cluster at startup. **Change all three before deploying to production.** * `kafka.secrets.kraft_cluster_id` — A unique identifier for the KRaft cluster. Generate with `kafka-storage.sh random-uuid` or any base64-encoded random string. * `kafka.secrets.inter_broker_password` — Password used for inter-broker communication (SASL). * `kafka.secrets.controller_password` — Password used for controller-to-broker communication (SASL). ### Extra Configurations * `kafka.extra_configurations.default.replication.factor` — Default replication factor for new topics. Cannot exceed the number of replicas. * `kafka.extra_configurations.auto.create.topics.enable` — When `true`, topics are automatically created when first produced to or consumed from. * `kafka.extra_configurations.log.retention.hours` — How long Kafka retains log segments before deletion (default `168` hours = 7 days). ### Exporters Both exporters run as sidecar containers on the Kafka cluster workload. **Kafka Exporter** (`kafka_exporter`) — Exposes consumer group lag and topic metrics in Prometheus format. * `kafka_exporter.cpu` / `kafka_exporter.memory` — Resources for the exporter sidecar. * `kafka_exporter.listener` — Listener name to connect to (default: `client`). * `kafka_exporter.dropMetrics` — List of metric name patterns to exclude (e.g., `["kafka_consumergroup.*"]`). **JMX Exporter** (`jmx_exporter`) — Exposes Kafka JMX metrics in Prometheus format. * `jmx_exporter.kafkaJmxPort` — JMX port on the Kafka broker (default `5557`). * `jmx_exporter.exporterPort` — Port where the exporter serves metrics (default `5556`). * `jmx_exporter.cpu` / `jmx_exporter.memory` — Resources for the JMX exporter sidecar. ### Kafbat UI (Optional) Kafbat UI is a web-based interface for browsing topics, viewing consumer groups, and managing the cluster. * `kafbat_ui.enabled` — Enable or disable the Kafbat UI workload (default: `true`). * `kafbat_ui.configuration_secret` — Name of the pre-created Control Plane secret containing the Kafbat configuration YAML (see [Prerequisites](#prerequisites)). * `kafbat_ui.domain` — Optional custom domain for the Kafbat UI. Requires DNS configuration pointing to the GVC's load balancer. * `kafbat_ui.replicas` — Number of Kafbat UI replicas (default: `1`). * `kafbat_ui.resources` — CPU and memory for the UI workload. * `kafbat_ui.firewall` — Firewall rules for the UI workload. By default, external access is open to `0.0.0.0/0`. ### Kafka REST Proxy (Optional) The REST Proxy provides an HTTP API for producing, consuming, and managing Kafka resources without a native client. * `kafka_rest_proxy.enabled` — Enable or disable the REST Proxy workload (default: `true`). * `kafka_rest_proxy.domain` — Optional custom domain for the REST Proxy. * `kafka_rest_proxy.properties.bootstrap.servers` — Kafka cluster bootstrap address (update to match your release name and cluster name). * `kafka_rest_proxy.jaas_conf` — JAAS configuration for the Kafka client and REST Proxy HTTP authentication. * `kafka_rest_proxy.password_properties` — Users and passwords for REST Proxy BASIC authentication. Update `kafka_rest_proxy.properties.bootstrap.servers` to match your release name and cluster workload name before deploying (e.g., `SASL_PLAINTEXT://my-release-cluster:9092`). ### Kafka Connect (Optional) Kafka Connect workers for running sink and source connectors are configured via the `kafka_connectors` list (commented out by default). Each entry defines a Connect worker with connector plugins and their configurations. Supported plugin artifact types: `jar`, `zip`, `tgz`. Plugins are downloaded and extracted into the `plugins_folder` on startup. See the [template source](https://github.com/controlplane-com/templates/tree/main/kafka) for full connector configuration examples including MirrorMaker 2, Camel S3 Sink, ClickHouse Sink, and Snowflake Sink. ### Kafka Client (Optional) The Kafka client workload provides a persistent container running in the GVC that you can connect to via the Control Plane UI or CLI for testing and debugging. To enable it, uncomment the `kafka_client` section in your values file. Once deployed, connect via the workload terminal and use the Kafka CLI tools: ```bash theme={null} # Create a client properties file for SASL authentication cat > /tmp/client.properties < Official Apache Kafka documentation Kafka KRaft consensus mode without ZooKeeper Kafbat UI documentation View the source files, default values, and chart definition # Keycloak Source: https://docs.controlplane.com/template-catalog/templates/keycloak Deploy Keycloak on Control Plane using the Template Catalog. Covers clustering, PostgreSQL backing stores, access configuration, and optional database backups. ## Overview Keycloak is an open-source identity and access management platform that provides single sign-on, OIDC/SAML, user federation, and fine-grained authorization. This template runs clustered Keycloak 26 in production mode with a highly available PostgreSQL backing store by default, delivering zero-downtime restarts and upgrades. ### What Gets Created * **Stateful Keycloak Workload** — 2 replicas by default, clustered via embedded Infinispan (JGroups JDBC\_PING through the shared database — no extra clustering infrastructure). Setting `replicas: 1` runs a dev mode with clustering fully disabled. * **Highly Available PostgreSQL Stack** (default) — The [PostgreSQL Highly Available template](/template-catalog/templates/postgres-highly-available) as a subchart: 3 Patroni PostgreSQL replicas, 3 etcd replicas, and an HAProxy leader-routing workload that Keycloak connects through. * **Single-Instance PostgreSQL Workload** (optional) — The [PostgreSQL template](/template-catalog/templates/postgres) instead, for lighter dev/test deployments. * **Volume Sets** — Persistent storage for the PostgreSQL (and etcd) data. * **Backup Cron Workload** (optional) — Created by the backing store subchart when its backup pass-through is enabled. * **Secrets** — A dictionary secret with the bootstrap admin credentials, an opaque startup script secret, and the database credentials secret created by the PostgreSQL subchart. * **Identity & Policy** — An identity bound to the Keycloak workload with a least-privilege policy granting `reveal` access to exactly the secrets it mounts. All durable state — realms, users, and active sessions — lives in PostgreSQL; the Keycloak tier is stateless on disk. This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites None for a default install. If you enable the optional database backups, you need a cloud account and a bucket for the backing store's backup job. The backup configuration is a pass-through to the backing PostgreSQL template — follow the [PostgreSQL Highly Available backup prerequisites](/template-catalog/templates/postgres-highly-available#backup-prerequisites) (default store) or the [PostgreSQL backup prerequisites](/template-catalog/templates/postgres#backup-prerequisites) (dev/test store). ## Installation To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} image: quay.io/keycloak/keycloak:26.6.3 # Replica count. 2+ (default) forms an Infinispan cluster over JGroups JDBC_PING # for zero-downtime restarts. 1 = dev mode: clustering fully disabled (local cache). replicas: 2 resources: # per replica cpu: 1000m memory: 2Gi # JVM heap is sized to 70% of this limit; do not set below 1.5Gi minCpu: 500m minMemory: 1Gi # ─── Admin Bootstrap ────────────────────────────────────────────────────────── # Creates a temporary admin on first boot. Log in and create a permanent admin, # then remove the temporary one (Keycloak warns until you do). admin: username: admin password: change-me-keycloak-admin # change before installing # ─── Backing Store: Highly Available PostgreSQL (default) ──────────────────── # Deploys the postgres-highly-available template: 3 Patroni replicas, 3 etcd # replicas, and an HAProxy leader-routing endpoint Keycloak connects to. postgresHA: enabled: true postgres: username: keycloak password: change-me-keycloak-db # change before installing database: keycloak replicas: 3 volumeset: capacity: 10 # initial capacity in GiB per replica (minimum is 10) backup: # pass-through to the postgres-highly-available template's backup feature enabled: false mode: logical # logical or wal-g resources: cpu: 100m memory: 128Mi logical: image: ghcr.io/controlplane-com/backup-images/postgres-backup:17.1.0 schedule: "0 2 * * *" # daily at 2am UTC walg: intervalSeconds: 21600 # every 6 hours provider: aws # Options: aws, gcp, or minio aws: bucket: my-backup-bucket region: us-east-1 cloudAccountName: my-backup-cloudaccount policyName: my-backup-policy prefix: postgres/backups gcp: bucket: my-backup-bucket cloudAccountName: my-backup-cloudaccount prefix: postgres/backups minio: # any S3-compatible endpoint, e.g. an internal MinIO template deployment endpoint: http://my-minio-workload:9000 bucket: my-backup-bucket accessKey: my-minio-username secretKey: my-minio-password prefix: postgres/backups # ─── Backing Store: Single-instance PostgreSQL (dev/test) ──────────────────── # Enable this and disable postgresHA for a lighter non-HA deployment. postgres: enabled: false config: username: keycloak password: change-me-keycloak-db # change before installing database: keycloak volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) backup: # pass-through to the postgres template's backup feature (Postgres 17+) enabled: false image: ghcr.io/controlplane-com/backup-images/postgres-backup:18.1.0 schedule: "0 2 * * *" # daily at 2am UTC resources: cpu: 100m memory: 128Mi provider: aws # Options: aws, gcp, or minio aws: bucket: my-backup-bucket region: us-east-1 cloudAccountName: my-backup-cloudaccount policyName: my-backup-policy prefix: postgres/backups gcp: bucket: my-backup-bucket cloudAccountName: my-backup-cloudaccount prefix: postgres/backups minio: endpoint: http://my-minio-workload:9000 bucket: my-backup-bucket accessKey: my-minio-username secretKey: my-minio-password prefix: postgres/backups # ─── Access ─────────────────────────────────────────────────────────────────── publicAccess: enabled: true # HTTPS via the canonical *.cpln.app endpoint internalAccess: type: same-gvc # options: none, same-gvc, same-org, workload-list workloads: [] # note: replicas > 1 requires type != none # workloads: # - //gvc/GVC_NAME/workload/WORKLOAD_NAME ``` ### Replicas and Clustering * `replicas` — `2` or more (default) runs Keycloak in a cluster: the replicas form an Infinispan cluster over JGroups JDBC\_PING through the shared database, so restarts and upgrades are zero-downtime and in-flight logins and sessions survive a replica restart. `1` runs a dev mode with clustering fully disabled (local cache only). * Clustered replicas must be able to reach each other on ports 7800/57800, so `replicas > 1` requires `internalAccess.type` other than `none` — the chart enforces this at render. * Scaling is operator-driven: change `replicas` via a template upgrade. There is deliberately no autoscaling, so cluster membership only changes intentionally. ### Keycloak Resources * `resources` — CPU and memory per replica. The JVM heap is sized to 70% of the memory limit; do not set `memory` below `1.5Gi`. ### Admin Bootstrap * `admin.username` / `admin.password` — Credentials for a temporary bootstrap admin created on first boot. **Change the password before installing.** After the first login, create a permanent admin account and remove the temporary one — Keycloak warns until you do. ### Backing Store Exactly one of the two stores must be enabled — the chart enforces this at render. * `postgresHA` (default) — A highly available PostgreSQL cluster from the [PostgreSQL Highly Available template](/template-catalog/templates/postgres-highly-available): 3 Patroni replicas, 3 etcd replicas, and an HAProxy endpoint that routes Keycloak's connections to the current leader. Do not disable the HA proxy (`postgresHA.proxy.enabled`) — Keycloak writes through the HAProxy leader endpoint, and the chart enforces this at render. * `postgres` — A single-instance PostgreSQL from the [PostgreSQL template](/template-catalog/templates/postgres), for lighter dev/test deployments. Set `postgresHA.enabled: false` and `postgres.enabled: true`. * `postgresHA.postgres.*` / `postgres.config.*` — Database credentials and database name. **Change the password before installing.** * `postgresHA.volumeset.capacity` / `postgres.volumeset.capacity` — Initial volume size in GiB (minimum 10, per replica for the HA store). **Template version `1.0.0` did not compact the etcd cluster inside the bundled highly available database**, so etcd's backend grows with time alone and goes read-only once it reaches its 2 GiB quota — after roughly 110 days — taking PostgreSQL failover with it. Only installs running the HA database are affected (`postgresHA.enabled`, the default here); see [etcd History Compaction](/template-catalog/templates/postgres-highly-available#etcd-history-compaction) for the mechanism and the symptoms. Upgrade to `1.0.1` or later to turn compaction on: that stops further growth but cannot shrink a backend that has already grown, and a cluster that has already raised a `NOSPACE` alarm needs operator recovery rather than an upgrade. ### Access * `publicAccess.enabled` — Exposes Keycloak over HTTPS at the automatically assigned canonical `*.cpln.app` endpoint. Keep it enabled for browser-based SSO — end-user browsers must reach Keycloak's login endpoints. Disable it only for pure service-to-service deployments; internal access keeps working. * `internalAccess.type` — Controls which workloads can reach Keycloak inside Control Plane (`none`, `same-gvc`, `same-org`, or `workload-list`). With `workload-list`, list the allowed workloads in `internalAccess.workloads`. ### Backup The `postgresHA.backup` and `postgres.backup` sections are pass-throughs to the backup feature of the corresponding backing store template. Set `backup.enabled: true` and fill in the provider section (`aws`, `gcp`, or `minio`) to schedule database backups to object storage. For provider setup, backup modes, and restore procedures, see the backing store's documentation: * Default HA store: [PostgreSQL Highly Available — Backup](/template-catalog/templates/postgres-highly-available#backup) * Dev/test store: [PostgreSQL — Backup](/template-catalog/templates/postgres#backup) ## Connecting | What | Value | | ----------------- | ------------------------------------------------------------------------------ | | Public URL | `status.canonicalEndpoint` from `cpln workload get {release}-keycloak -o yaml` | | Admin console | `https://{canonical-endpoint}/admin` | | OIDC discovery | `https://{canonical-endpoint}/realms/{realm}/.well-known/openid-configuration` | | In-GVC (internal) | `http://{release}-keycloak.{gvc}.cpln.local:8080` | | Admin credentials | `admin.username` / `admin.password` values | On a first install of the default HA stack, the Keycloak container waits for the PostgreSQL cluster to come up (logging `Waiting for PostgreSQL...`) before starting. Expect the Keycloak workload to become ready roughly 4–5 minutes after install; the dev-mode store (`postgres` with `replicas: 1`) is ready in about 2 minutes. ## Important Notes * **Change both default passwords before installing** — the admin bootstrap password and the database password. The bootstrap admin is temporary by design: log in, create a permanent admin, then remove it. * **Keep `publicAccess` enabled for browser SSO** — end-user browsers must reach Keycloak's login endpoints; disable it only for pure service-to-service deployments. * **Do not disable the HA proxy** (`postgresHA.proxy.enabled`) — Keycloak connects through the HAProxy leader endpoint for writes; the chart enforces this at render. * **Scaling is operator-driven** — change `replicas` via a template upgrade; there is deliberately no autoscaling, so cluster membership only changes intentionally. * **Database volumes survive reinstalls** — uninstalling and reinstalling under the same release name reuses the persisted data unless the volume sets are deleted. ## External References Official Keycloak documentation All Keycloak server configuration options How Keycloak's Infinispan caching and clustering work Create a Control Plane Cloud Account for backup storage access View the source files, default values, and chart definition # Langfuse Source: https://docs.controlplane.com/template-catalog/templates/langfuse Deploy the full Langfuse LLM observability stack on Control Plane. Covers object storage prerequisites, secret generation, accessing the UI, sending traces, LLM connections, and backup recommendations. ## Overview Langfuse is an open-source LLM observability and evaluation platform. This template deploys the full Langfuse stack on Control Plane — UI, background worker, and all required data stores — ready to receive traces from any LLM application. ### Architecture * **Langfuse Web** — Next.js application serving the UI and public API. Autoscales between 2 and 5 replicas. * **Langfuse Worker** — Background processor for trace ingestion, automated evaluations, and third-party integrations. * **PostgreSQL** — Stores all configuration data: users, projects, API keys, prompts, datasets, and evaluation configs. * **Redis** — BullMQ ingestion queue and short-lived cache for API keys and prompts. * **ClickHouse** — Columnar store for all traces, observations, and scores. Powers all dashboard queries. Data files are written directly to object storage. * **Object Storage** — AWS S3 or GCS. Shared between ClickHouse (data files) and Langfuse (raw event buffer and media uploads) using separate key prefixes (`clickhouse/`, `events/`, `media/`). ### What Gets Created * **Standard Langfuse Web Workload** — The Next.js UI and public API with autoscaling enabled. * **Standard Langfuse Worker Workload** — Background trace ingestion and evaluation processor. * **Stateful PostgreSQL Workload** — Single-replica Postgres with a persistent volume set. * **Stateful Redis Workload** — Single-replica Redis for the BullMQ queue and cache. * **Stateful ClickHouse Workload** — Single-node ClickHouse with a persistent volume set for local metadata. * **Volume Sets** — One persistent volume set each for PostgreSQL and ClickHouse. * **Identity & Policy** — An identity bound to all workloads with `reveal` access to all secrets, and cloud storage access for the object store. * **Secrets** — Opaque secrets for all component credentials and Langfuse auth keys. This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites Object storage must be configured before deploying. Both ClickHouse and Langfuse use the same bucket with separate key prefixes — create the bucket and credentials as described below, then fill in the `objectStore` section of `values.yaml`. ### AWS S3 Create an S3 bucket. Set `objectStore.aws.bucket` to its name and `objectStore.aws.region` to its region. If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account). Set `objectStore.aws.cloudAccountName` to its name. Create an IAM policy with the following JSON (replace `YOUR_BUCKET_NAME`) and set `objectStore.aws.policyName` to its name: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket", "s3:GetObjectVersion", "s3:DeleteObjectVersion" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET_NAME", "arn:aws:s3:::YOUR_BUCKET_NAME/*" ] } ] } ``` ### GCS GCS uses S3-compatible HMAC authentication for this template. A Control Plane Cloud Account is not required. Create a GCS bucket. Set `objectStore.gcp.bucket` to its name. In the GCP console, go to **Settings → Interoperability** and click **Create a key for a service account → Create new account**. Name the service account, assign the `Storage Object Admin` role, and click **Done**. Copy the provided HMAC access key and secret into `objectStore.gcp.accessKeyId` and `objectStore.gcp.secretAccessKey`. Or use the CLI: ```bash theme={null} gcloud config set project YOUR_PROJECT_ID gcloud storage buckets create gs://YOUR_BUCKET_NAME gcloud iam service-accounts create langfuse-storage gcloud projects add-iam-policy-binding $(gcloud config get-value project) \ --member="serviceAccount:langfuse-storage@$(gcloud config get-value project).iam.gserviceaccount.com" \ --role="roles/storage.objectAdmin" gsutil hmac create langfuse-storage@$(gcloud config get-value project).iam.gserviceaccount.com ``` ### Installation Once object storage is ready, install using your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} # Object store - used by both ClickHouse (data files) and Langfuse (raw event buffer + media) # Both services share the same bucket using separate key prefixes objectStore: provider: aws # Options: aws, gcp aws: bucket: my-langfuse-bucket region: us-east-1 cloudAccountName: my-cloudaccount policyName: my-custom-policy gcp: bucket: my-langfuse-bucket accessKeyId: my-gcs-key # GCS HMAC key ID secretAccessKey: my-gcs-secret # GCS HMAC secret key # Langfuse application langfuse: web: image: langfuse/langfuse:3 minReplicas: 2 # Keep at 2+ for zero-downtime rolling deploys maxReplicas: 5 resources: minCpu: 500m maxCpu: 1000m minMemory: 1Gi maxMemory: 2Gi worker: image: langfuse/langfuse-worker:3 replicas: 1 resources: minCpu: 250m maxCpu: 500m minMemory: 512Mi maxMemory: 1Gi auth: nextAuthSecret: "" # Required: openssl rand -base64 32 encryptionKey: "" # Required: openssl rand -hex 32 (must be exactly 64 hex characters) salt: "" # Required: openssl rand -base64 32 firewall: inboundAllowCIDR: - 0.0.0.0/0 # Restrict to specific IPs in production # PostgreSQL - stores users, projects, API keys, prompts, datasets, eval configs postgres: image: postgres:18 resources: minCpu: 250m maxCpu: 1 minMemory: 512Mi maxMemory: 1Gi config: username: langfuse password: mypassword database: langfuse volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) # Redis - BullMQ ingestion queue and API key/prompt cache redis: image: redis:7.4 resources: minCpu: 100m maxCpu: 400m minMemory: 256Mi maxMemory: 512Mi auth: password: mypassword # ClickHouse - stores all traces, observations, and scores; powers dashboards # Data files are stored in the object store above; volumeset holds local metadata only clickhouse: image: clickhouse/clickhouse-server:25.10 resources: minCpu: 1 maxCpu: 2 minMemory: 2Gi maxMemory: 4Gi config: password: mypassword database: langfuse volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) ``` ### Required Auth Secrets Three auth values must be generated and set in `values.yaml` before deploying: ```bash theme={null} # nextAuthSecret and salt — any base64 string openssl rand -base64 32 # encryptionKey — must be exactly 64 hex characters (32 bytes) openssl rand -hex 32 ``` `encryptionKey` encrypts LLM API keys and other sensitive project data stored in PostgreSQL. It must be generated with `openssl rand -hex 32` — a base64 value will fail validation on startup. Run each command separately and copy the output into the corresponding field. ### Passwords Change all component passwords before deploying to production: * `postgres.config.password` — PostgreSQL admin password * `redis.auth.password` — Redis password * `clickhouse.config.password` — ClickHouse password ### Firewall The Langfuse web UI and API are publicly accessible by default (`0.0.0.0/0`). To restrict access to specific IP ranges, update `langfuse.firewall.inboundAllowCIDR`: ```yaml theme={null} langfuse: firewall: inboundAllowCIDR: - 203.0.113.0/24 # restrict to your office or application IPs ``` ### Object Store Both ClickHouse and Langfuse share one bucket with separate key prefixes: | Prefix | Used by | | ------------- | ------------------------------------------------- | | `clickhouse/` | ClickHouse data files (tables, parts, metadata) | | `events/` | Langfuse raw event ingestion buffer | | `media/` | Langfuse media uploads (screenshots, attachments) | Set `objectStore.provider` to `aws` or `gcp` and fill in the corresponding block. Leave the unused provider block empty. ## Accessing Langfuse Once deployed, the Langfuse UI is accessible via the Control Plane external endpoint for the `{release-name}-langfuse-web` workload. Navigate to the endpoint in your browser to create an account and log in. ### Sending Traces via API After creating a project and generating API keys (**Settings → API Keys**), send traces using the public API: ```bash theme={null} curl -X POST https://YOUR_LANGFUSE_ENDPOINT/api/public/traces \ -H "Content-Type: application/json" \ -u "YOUR_PUBLIC_KEY:YOUR_SECRET_KEY" \ -d '{ "name": "my-first-trace", "input": "Hello", "output": "Hello back" }' ``` Or integrate using the [Langfuse SDK](https://langfuse.com/docs/sdk) for Python, TypeScript, and other languages. ### LLM Connections (Playground and Evaluations) The Langfuse playground and LLM-as-a-Judge evaluations require LLM API keys configured through the UI. These keys are stored encrypted in PostgreSQL using your `encryptionKey`. To configure: 1. In the Langfuse UI, go to **Settings → LLM API Keys** (for the playground) or **Evaluation → Set up default model** (for automated evals). 2. Click **Add LLM Connection**, choose your provider (OpenAI, Anthropic, etc.), and enter your API key. ## Backups Stores all critical config: users, projects, API keys, prompts, datasets, and eval configs. **Most important to back up.** Enable snapshot policies on the PostgreSQL volume set via the Control Plane console — snapshots capture the full disk state and restore quickly. Data files are stored directly in your object store (S3 or GCS) and are inherently durable — the volume set only holds local metadata. A full restore is performed by redeploying and pointing ClickHouse at the existing bucket. Holds only the transient BullMQ ingestion queue and short-lived cache. Backup is not required. ## External References Full Langfuse product documentation Python, TypeScript, and other language SDK guides Self-hosting architecture and configuration reference Set up a Control Plane Cloud Account for S3 access # Listmonk Source: https://docs.controlplane.com/template-catalog/templates/listmonk Deploy listmonk on Control Plane using the Template Catalog. Self-hosted newsletter and mailing list manager — a Mailchimp alternative — with automatic schema install and admin bootstrap on first boot, persistent media uploads, public subscription pages, and a single-instance or highly available PostgreSQL backing store. ## Overview Listmonk is a high-performance, self-hosted newsletter and mailing list manager distributed as a single Go binary — a self-hosted alternative to Mailchimp, Sendy, and Mautic. This template deploys the listmonk server on port `9000` — admin dashboard, campaign engine, public subscription and tracking pages, and the transactional-mail API — backed by PostgreSQL, with the database schema install and Super Admin bootstrap handled automatically on first boot. ### Architecture * **Listmonk** — A stateful, single-replica workload serving the admin UI, the public subscription/tracking pages, and the HTTP API on port `9000`. The container boots through listmonk's own idempotent install chain: it waits for the database, installs the schema, applies any pending migrations, then starts the server. A default install is ready in about a minute with no manual setup step, and a restart re-runs the same chain safely — it detects an already-provisioned database, skips the install, and leaves existing data untouched. * **PostgreSQL (single-instance, default)** — The [postgres](/template-catalog/templates/postgres) template as a subchart: the store for all lists, subscribers, campaigns, templates, and settings. * **PostgreSQL (HA, optional)** — The [postgres-highly-available](/template-catalog/templates/postgres-highly-available) template instead: 3× Patroni PostgreSQL with automatic failover, fronted by an HAProxy leader endpoint that listmonk connects through for writes and schema migrations. ### What Gets Created * **Stateful Listmonk Workload** — The listmonk server on port `9000`, one replica, with configurable CPU and memory. * **Database Workloads** — Single mode: one stateful PostgreSQL workload. HA mode: a stateful Patroni PostgreSQL workload, a stateful etcd workload, and a standard HAProxy leader-routing workload. * **Volume Sets** — A persistent uploads volume set mounted at `/listmonk/uploads` for media uploaded through the admin UI, plus the database subchart's data volumes (10 GiB by default; per replica in HA mode). * **Secret** — A dictionary secret holding the admin bootstrap username and password. The database credentials secret is created by the backing store subchart. * **Identity & Policy** — An identity bound to the listmonk workload, with a policy granting `reveal` on exactly two secrets: the admin bootstrap secret and the active backing store's credentials secret. * **Cron Backup Workload** *(optional)* — Created in the backing PostgreSQL store when database backups are enabled. This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites None for a default install — it deploys with a working PostgreSQL backend, an installed schema, a bootstrapped admin account, and an auto-assigned HTTPS endpoint. Two things are configured after install rather than at install time: * **SMTP** — required before any mail is delivered. Configured in listmonk's own admin UI (see [Post-Install Setup](#post-install-setup)). * **Database backups** *(optional)* — need a bucket and provider access set up beforehand (see [Backing Up](#backing-up)). Install the template using your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Choosing a Database Mode Exactly one of the two backing stores must be enabled — the chart enforces this at render. Listmonk is wired to the active database automatically, including for the first-boot schema install. | | `postgres` (default) | `postgresHA` | | ----------------- | -------------------------------------- | ---------------------------------------------------- | | What runs | One single-replica PostgreSQL workload | 3× Patroni PostgreSQL, etcd, HAProxy leader endpoint | | Database failover | None | Automatic (Patroni leader election) | | Best for | Development and lightweight installs | Production | To switch to HA mode, set `postgres.enabled: false` and `postgresHA.enabled: true`. ## Configuration Key configuration values (see the template's `values.yaml` for the complete set): ```yaml theme={null} image: listmonk/listmonk:v6.2.0 # official Docker Hub image resources: # single Go binary — light footprint cpu: 500m memory: 512Mi minCpu: 150m minMemory: 256Mi # Persistent media uploads (filesystem media store), mounted at /listmonk/uploads volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) admin: username: admin # min 3 chars password: change-me-listmonk-admin # min 8 chars — change before installing timezone: Etc/UTC # container TZ — governs campaign scheduling times publicAccess: enabled: true # HTTPS admin UI + public subscription/tracking pages via the canonical *.cpln.app endpoint internalAccess: type: same-gvc # options: none, same-gvc, same-org, workload-list workloads: [] # only used when type is same-gvc or workload-list postgres: # default: single-instance PostgreSQL enabled: true image: postgres:18 # listmonk requires Postgres 12+ config: username: listmonk password: change-me-listmonk-db # change before installing database: listmonk volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) backup: enabled: false # true = scheduled DB backups to object storage schedule: "0 2 * * *" # daily at 2am UTC provider: aws # aws | gcp | minio aws: bucket: my-backup-bucket region: us-east-1 cloudAccountName: my-backup-cloudaccount policyName: my-backup-policy prefix: listmonk/backups postgresHA: # durable HA: 3-replica Patroni store with an HAProxy leader endpoint (disable postgres first) enabled: false postgres: username: listmonk password: change-me-listmonk-db # change before installing database: listmonk replicas: 3 volumeset: capacity: 10 # initial capacity in GiB per replica (minimum is 10) backup: enabled: false # true = scheduled backups to object storage mode: logical # logical | wal-g provider: aws # aws | gcp | minio aws: bucket: my-backup-bucket region: us-east-1 cloudAccountName: my-backup-cloudaccount policyName: my-backup-policy prefix: listmonk/backups ``` ### Application * `image` — The official listmonk image from Docker Hub. * `resources` — CPU and memory bounds for the listmonk workload. Listmonk is a single Go binary, so the defaults suit typical installs. * `volumeset.capacity` — Initial size in GiB (minimum 10) of the uploads volume set mounted at `/listmonk/uploads`. It holds images and files uploaded through the admin **Media** page, including generated thumbnails, and survives restarts and redeploys. * `timezone` — The container timezone, which governs the times used for scheduled campaigns. ### Admin Bootstrap ```yaml theme={null} admin: username: admin # min 3 chars password: change-me-listmonk-admin # min 8 chars — change before installing ``` These become the Super Admin account created during the first boot's schema install, and are stored in the template-managed `{release}-listmonk-admin` dictionary secret. The chart rejects a username shorter than 3 characters or a password shorter than 8 at render time, before anything is deployed. The Super Admin is created on the **first install only**. Afterwards the account lives in the database, and changing `admin.username` / `admin.password` in values does not update it. Set a strong password before you install; manage users afterwards in **Admin → Settings → Users**. ### Access * `publicAccess.enabled` — Serve listmonk on the auto-assigned `*.cpln.app` HTTPS canonical endpoint (default). This is what makes the subscriber-facing pages — subscription forms, unsubscribe links, and tracking pixels — reachable from the internet. The admin UI and admin API on the same endpoint remain authentication-gated. Set to `false` for an internal-only instance: external requests are then refused at the edge, and in-GVC callers still reach it per `internalAccess`. * `internalAccess.type` — Controls which workloads can reach listmonk over the internal network: | Type | Description | | --------------- | ---------------------------------------------------------------------- | | `none` | No internal access. | | `same-gvc` | Allow access from all workloads in the same GVC (default). | | `same-org` | Allow access from all workloads in the same organization. | | `workload-list` | Allow access only from workloads listed in `internalAccess.workloads`. | Firewall changes take up to a couple of minutes to propagate after a `helm upgrade` reports success. ### Backing Store Enable exactly one of `postgres` (single-instance, default) or `postgresHA` (HA) — see [Choosing a Database Mode](#choosing-a-database-mode). In both modes, **change the database password before installing** (`postgres.config.password` / `postgresHA.postgres.password`); it seeds the database on first boot and cannot be changed by editing values afterwards. The database holds everything except uploaded media: lists, subscribers, campaigns, templates, users, and all of the settings you configure in the admin UI. **Template version `1.0.0` did not compact the etcd cluster inside the bundled highly available database**, so etcd's backend grows with time alone and goes read-only once it reaches its 2 GiB quota — after roughly 110 days — taking PostgreSQL failover with it. Only installs running the HA database are affected (`postgresHA.enabled`, which is off by default here); see [etcd History Compaction](/template-catalog/templates/postgres-highly-available#etcd-history-compaction) for the mechanism and the symptoms. Upgrade to `1.0.1` or later to turn compaction on: that stops further growth but cannot shrink a backend that has already grown, and a cluster that has already raised a `NOSPACE` alarm needs operator recovery rather than an upgrade. ## Connecting | What | Value | | ------------------------ | --------------------------------------------------------------------------------------------------------- | | Public URL | `status.canonicalEndpoint` of `{release}-listmonk` (`cpln workload get {release}-listmonk -o yaml`) | | Admin UI / login | `https://.cpln.app/admin` | | Public subscription page | `https://.cpln.app/subscription/form` | | Health check | `https://.cpln.app/health` — public and unauthenticated (`/api/health` requires a session) | | HTTP API | `https://.cpln.app/api/...` — authenticated; lists, subscribers, campaigns, transactional mail | | Internal (same GVC) | `http://{release}-listmonk.{gvc}.cpln.local:9000` | | Admin credentials | The `admin.username` / `admin.password` values, stored in the `{release}-listmonk-admin` secret | | Database (single mode) | `{release}-postgres.{gvc}.cpln.local:5432` | | Database (HA mode) | `{release}-postgres-ha-proxy.{gvc}.cpln.local:5432` (HAProxy leader endpoint) | Once the workload reports ready, open `https://.cpln.app/admin` and sign in with the bootstrap credentials — the schema is already installed and the Super Admin already exists, so there is no setup wizard and no manual install step. ## Post-Install Setup Mail delivery and object-storage media are listmonk settings stored in its database, not template values. Configure them in the admin UI after installing: In **Admin → Settings → SMTP**, add your mail provider (Amazon SES, SendGrid, Mailgun, Postmark, or any SMTP relay) and save. Until a working SMTP server is configured, campaigns still run to completion but no mail is delivered. In **Admin → Settings → General**, set the root URL to your canonical `*.cpln.app` endpoint, or to your custom domain once you attach one, so that links and tracking URLs inside your emails point at the right host. Filesystem storage on the bundled uploads volume set works out of the box. To store media in S3-compatible object storage instead, switch the provider in **Admin → Settings → Media**. ## Backing Up Database backups are optional and disabled by default. When enabled, a scheduled backup job runs inside the backing PostgreSQL store and uploads to your bucket under the configured prefix — covering lists, subscribers, campaigns, and settings, but not the uploaded media on the listmonk volume set. Enable with `postgres.backup.enabled` or `postgresHA.backup.enabled` (matching your database mode), and complete the storage setup for your provider **before** installing. Create an S3 bucket. Set `backup.aws.bucket` and `backup.aws.region` to match. If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) for your AWS account. Set `backup.aws.cloudAccountName` to its name. Create an IAM policy granting the required S3 actions on the bucket, and set `backup.aws.policyName` to its name: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["s3:ListBucket"], "Resource": "arn:aws:s3:::my-backup-bucket" }, { "Effect": "Allow", "Action": ["s3:PutObject", "s3:GetObject", "s3:DeleteObject"], "Resource": "arn:aws:s3:::my-backup-bucket/*" } ] } ``` Create a GCS bucket. Set `backup.gcp.bucket` to its name. If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) for your GCP project and grant its service account **Storage Object Admin** (`roles/storage.objectAdmin`) on the bucket. Set `backup.gcp.cloudAccountName` to its name. Create your bucket on the server. Set `backup.minio.bucket` to its name. Set `backup.minio.endpoint` to the S3 API address including port, and `backup.minio.accessKey` / `backup.minio.secretKey` to credentials with access to the bucket. No Cloud Account is required — the keys authenticate directly. In HA mode, `postgresHA.backup.mode` selects `logical` (scheduled `pg_dump`) or `wal-g` (continuous WAL archiving). The full per-provider walkthrough lives in the backing [postgres](/template-catalog/templates/postgres) and [postgres-highly-available](/template-catalog/templates/postgres-highly-available) template documentation. ## Important Notes * **Single instance by design — there is no `replicas` knob.** Listmonk runs its campaign workers in-process, so two instances against one database would send every campaign twice. The workload is pinned to one replica and rolls out without surge: the old replica stops before the new one starts, which means an upgrade or restart causes a brief gap in availability instead of overlapping senders. Durability comes from PostgreSQL and the uploads volume set. * **Change `admin.password` and the database password before installing.** Both are applied on the first boot only; editing them afterwards does not change the existing account or database. * **No mail is delivered until SMTP is configured** in **Admin → Settings → SMTP**. Before that, starting a campaign is not an error — it runs and finishes with zero messages sent. * **Keep `publicAccess` enabled for subscriber-facing pages to work.** Subscription forms, unsubscribe links, and tracking pixels must be reachable from the internet; the admin UI and API stay authentication-gated either way. * **Use `/health` for external health checks**, not `/api/health` — the latter requires an authenticated session. * **Volumes survive reinstalls under the same release name; uninstalling deletes them** — the database volumes and the uploads volume set go with the release, taking all lists, subscribers, campaigns, and media with them. Use `postgresHA` and/or enable backups for durable production data. ## External References Official listmonk documentation Settings, SMTP options, and filesystem or S3 media storage How lists, subscribers, campaigns, and templates fit together Manage lists, subscribers, and campaigns over HTTP View the source files, default values, and chart definition # LiteLLM Source: https://docs.controlplane.com/template-catalog/templates/litellm Deploy LiteLLM on Control Plane using the Template Catalog. An OpenAI-compatible LLM gateway and proxy — one endpoint in front of 100+ providers with virtual API keys, per-key and per-team spend tracking, budgets, rate limiting, and routing — backed by PostgreSQL and Redis. Covers the prerequisite secret, model list, Redis auth, scaling, and access. ## Overview LiteLLM is an OpenAI-compatible LLM gateway and proxy: a single endpoint in front of 100+ model providers, with virtual API keys, per-key and per-team spend tracking and budgets, rate limiting, and routing with fallbacks. This template deploys the stateless LiteLLM proxy tier backed by PostgreSQL (virtual keys, spend, teams, budgets) and Redis (shared rate-limit counters and response cache). Clients call it exactly as they would call OpenAI — point any OpenAI SDK at the endpoint and swap in a LiteLLM key. ### Architecture * **LiteLLM proxy** — A stateless `standard` workload serving the OpenAI-compatible API and Admin UI on port `4000`. Runs a single replica by default; set `litellm.replicas` to `2` or more for an always-on tier with zero-downtime rolling restarts. All state lives in PostgreSQL and Redis, so replicas are independent — no clustering. * **PostgreSQL** ([postgres](/template-catalog/templates/postgres) subchart) — The durable store for virtual keys, spend, teams, and budgets. The DB-bundled proxy image runs its Prisma migrations automatically on boot. * **Redis + Sentinel** ([redis](/template-catalog/templates/redis) subchart, optional via `redis.enabled`) — Shared rate-limit / budget counters and response cache across proxy replicas. ### What Gets Created * **Standard LiteLLM Workload** — One or more stateless replicas serving the API and Admin UI on port `4000`. * **Stateful PostgreSQL Workload** — Single-replica Postgres with a persistent volume set for keys and spend. * **Redis Workloads** *(optional)* — A Redis master-replica set with Redis Sentinel for rate limits and cache. * **Config Secret** — The model/provider list mounted into the proxy as `config.yaml`. * **DB-credentials Secret** — Assembles the proxy's `DATABASE_URL`. * **Identity & Policy** — An identity bound to the proxy with least-privilege `reveal` on exactly its own secrets and your prerequisite secret. This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites **You must create a dictionary secret before installing.** The proxy references it by name (`secrets.name`, default `my-litellm-secrets`) and will not become healthy until it exists — a missing secret wedges the deployment and makes it look broken. Create it first, in the same GVC, as a **dictionary** secret containing: * `LITELLM_MASTER_KEY` — the admin key (format `sk-...`); gates the Admin API/UI and mints virtual keys. * `LITELLM_SALT_KEY` — encrypts provider keys stored in PostgreSQL. **Write-once — never rotate it** (rotating corrupts every stored provider key). * one key per name in `litellm.providerEnv` (default: `OPENAI_API_KEY`) — the actual provider API key. Create it with the CLI (add one entry per provider key you use): ```bash theme={null} cpln secret create-dictionary --name my-litellm-secrets \ --entry LITELLM_MASTER_KEY=sk-$(openssl rand -hex 24) \ --entry LITELLM_SALT_KEY=sk-$(openssl rand -hex 24) \ --entry OPENAI_API_KEY=sk-...your-openai-key... ``` Install the template using your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} litellm: # DB-bundled image variant — runs Prisma `migrate deploy` on boot. image: ghcr.io/berriai/litellm-database:v1.93.0 replicas: 1 # 1 = proven single-replica; 2+ = always-on, zero-downtime restarts resources: minCpu: 250m maxCpu: 1000m minMemory: 1Gi maxMemory: 2Gi storeModelInDb: true # also manage models via the Admin UI (persisted in Postgres) # Model/provider list seeded into config.yaml. api_key values reference env vars # supplied via the prerequisite secret (never inline real keys here). modelList: - model_name: gpt-4o-mini litellm_params: model: openai/gpt-4o-mini api_key: os.environ/OPENAI_API_KEY # Env-var NAMES (non-sensitive) exposed to the proxy from the prerequisite secret. # Every name here must exist as a key in the secrets.name dictionary secret. providerEnv: - OPENAI_API_KEY # Prerequisite dictionary secret — MUST EXIST BEFORE INSTALL (see Prerequisites). secrets: name: my-litellm-secrets publicAccess: enabled: true # key-gated *.cpln.app endpoint (UI + API); false = internal-only internalAccess: type: same-gvc # options: none, same-gvc, same-org, workload-list workloads: [] # used only with workload-list # PostgreSQL — virtual keys / spend / budgets postgres: image: postgres:18 config: username: litellm password: change-me-litellm-pg # change before installing database: litellm resources: minCpu: 250m maxCpu: 1000m minMemory: 512Mi maxMemory: 1Gi volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) # Redis (Sentinel HA) — rate limits / cache redis: enabled: true # false = in-memory per-replica rate limiting (not shared) redis: image: redis:7.4 replicas: 3 auth: password: enabled: false # true = require AUTH; wires the master password into LiteLLM's cache config value: change-me-litellm-redis sentinel: image: redis:7.4 replicas: 3 ``` ### LiteLLM proxy * `litellm.image` — The DB-bundled LiteLLM proxy image (`litellm-database`), which runs Prisma migrations on boot. Pin a concrete tag. * `litellm.replicas` — Number of stateless proxy replicas. `1` is the proven single-replica shape; `2` or more gives an always-on tier where rolling restarts cycle one replica at a time with no downtime. Keep Redis enabled at `2`+ so rate limits are shared across replicas. * `litellm.resources` — CPU and memory per replica. * `litellm.storeModelInDb` — When `true`, models added through the Admin UI/API are persisted in PostgreSQL alongside the static `modelList`. * `litellm.modelList` — The model/provider list seeded into the mounted `config.yaml`. Each entry's `api_key` references an environment variable name (`os.environ/OPENAI_API_KEY`) — never a literal key. * `litellm.providerEnv` — The environment-variable **names** exposed to the proxy from the prerequisite secret. Every name here must exist as a key in that dictionary secret. ### Prerequisite secret * `secrets.name` — Name of the user-created dictionary secret holding `LITELLM_MASTER_KEY`, `LITELLM_SALT_KEY`, and every `providerEnv` key. It must exist **before** install — see [Prerequisites](#prerequisites). ### Access * `publicAccess.enabled` — Serve the API and Admin UI on the canonical `*.cpln.app` HTTPS endpoint (default). The endpoint is **key-gated** — a valid master or virtual key is always required. Set to `false` for an internal-only instance. * `internalAccess.type` — Internal firewall scope of the proxy workload: | Type | Description | | --------------- | ---------------------------------------------------------------------- | | `none` | No internal access. | | `same-gvc` | Allow access from all workloads in the same GVC (default). | | `same-org` | Allow access from all workloads in the same organization. | | `workload-list` | Allow access only from workloads listed in `internalAccess.workloads`. | ### PostgreSQL * `postgres.config.password` — The bundled database password. **Change it before installing.** * `postgres.volumeset.capacity` — Initial volume size in GiB (minimum 10). PostgreSQL stores all virtual keys, spend, teams, and budgets. ### Redis * `redis.enabled` — When `true` (default), deploys Redis + Sentinel for shared rate-limit counters and response cache. When `false`, no Redis is created and each proxy replica rate-limits in-memory (not shared across replicas). * `redis.redis.auth.password.enabled` — When `false` (default), Redis runs authless with the same-GVC firewall as the boundary. Set to `true` to require AUTH; the master password is then wired into the proxy's cache config. Both paths are tested end to end, including Sentinel monitoring the auth-protected master. * `redis.redis.replicas` / `redis.sentinel.replicas` — Redis and Sentinel replica counts. ## Connecting | Target | Address | Credentials | | ----------------------- | ----------------------------------------------------------- | ---------------------------------------------- | | Public API + Admin UI | `https://.cpln.app` (UI at `/ui`) | `LITELLM_MASTER_KEY` (Bearer token / UI login) | | Internal (same GVC) | `http://{release}-litellm.{gvc}.cpln.local:4000` | `LITELLM_MASTER_KEY` | | OpenAI-compatible calls | `POST /chat/completions` with `Authorization: Bearer ` | master key or a minted virtual key | The canonical `*.cpln.app` hostname appears under `status.canonicalEndpoint` (`cpln workload get {release}-litellm -o yaml`). Point any OpenAI SDK at this base URL and pass a LiteLLM key. ## Managing Virtual Keys Rather than handing out the master key, mint scoped virtual keys — each with its own budget and rate limits — and give those to your applications and teams. Spend is tracked per key in PostgreSQL. ```bash theme={null} curl -X POST https://.cpln.app/key/generate \ -H "Authorization: Bearer $LITELLM_MASTER_KEY" \ -H "Content-Type: application/json" \ -d '{"key_alias": "team-a", "max_budget": 10}' ``` The response includes a new `sk-...` key usable on `/chat/completions` and `/v1/models`. Manage keys, teams, and budgets visually in the Admin UI at `/ui`. ## Important Notes * **The prerequisite secret must exist before install.** `secrets.name` is a dictionary secret with `LITELLM_MASTER_KEY`, `LITELLM_SALT_KEY`, and every `providerEnv` key. A missing secret wedges the deployment. * **`LITELLM_SALT_KEY` is write-once — never rotate it.** It encrypts stored provider keys; changing it corrupts every stored key. The master key may be rotated. * **The endpoint is key-gated, not open.** Public access still requires a valid master or virtual key — hand out virtual keys with budgets and limits rather than the master key. * **Keep Redis enabled for `replicas` ≥ 2.** Without it, each replica rate-limits in-memory, so the effective limit becomes N× the configured value. * **Redis ships authless by default** (same-GVC firewall is the boundary). Set `redis.redis.auth.password.enabled: true` to require AUTH; the master password is then wired into the proxy's cache config. * **PostgreSQL data survives reinstalls of the proxy** — to reset virtual keys and spend you must also reinstall the database (which deletes its volume set). * **First install self-heals a brief DB-timing gap.** On a cold install the proxy can start before PostgreSQL accepts connections and log a `P1001` error with one restart; it recovers automatically once the database is ready (about 1.5–2 minutes to healthy). No action needed. ## External References Official LiteLLM documentation LiteLLM proxy deployment reference Create and manage virtual keys, budgets, and rate limits Full proxy config.yaml settings reference View the source files, default values, and chart definition # Manticore Search Source: https://docs.controlplane.com/template-catalog/templates/manticore Deploy Manticore Search on Control Plane using the Template Catalog. Covers configuration, volumes, scaling, and distributed Galera clustering with orchestrator API and S3 imports. ## Overview Manticore Search is a high-performance, open-source search engine built for fast full-text search at scale. This template deploys a distributed Manticore Search cluster using Galera replication for high availability, with an orchestrator API for cluster management, a web UI for monitoring and operations, and support for zero-downtime data imports from AWS S3. ### What Gets Created * **Manticore Search Workload** — A stateful Galera cluster with configurable replicas. Each replica runs a sidecar agent that handles cluster coordination, data imports, and recovery operations. * **Orchestrator API Workload** — A REST API service for triggering imports, monitoring cluster health, initiating repairs, and managing backup and restore operations. * **Orchestrator Cron Job** — A scheduled workload that runs import, health check, or repair operations on a configured schedule. * **UI Workload** — A web dashboard for managing the cluster, monitoring replication, triggering operations, and visualizing queries. * **Volume Set** — Persistent storage allocated per replica for Manticore data directories. * **Shared Volume Set** — A shared volume accessible by all replicas and the orchestrator, used for slot-based import coordination. * **Secrets** — Four opaque secrets: the Manticore `searchd` base configuration, the startup/shutdown handler script, the table schema registry used by the agent, and the agent bearer token. A K6 load test script secret is also created when load testing is enabled. * **Identities & Policies** — Identities for the main cluster, orchestrator, and backup workloads. Policies grant `reveal` access to the configuration secrets, `exec` permissions on the orchestrator cron job, and `view` access to the Manticore workload for orchestration. * **Backup Cron Job** (optional) — Scheduled logical backups of delta and full tables to an S3 bucket. Enabled when `orchestrator.backup.enabled: true`. * **Domain** (optional) — Routes `/api/*` to the orchestrator API and all other traffic to the UI. Enabled when `domain.enabled: true`. * **Load Test Workload** (optional) — A k6-based load test runner with a controller for automated scheduling. Enabled when `loadTest.enabled: true`. This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites This template requires an AWS S3 bucket for CSV source data and a Control Plane Cloud Account before installation. ### AWS S3 (Source Data) 1. Create an S3 bucket and upload your CSV source files. Set `buckets.sourceBucket` to the bucket name and `buckets.awsRegion` to its region. 2. If you do not have a Control Plane Cloud Account set up, follow the [Create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) guide. Set `buckets.cloudAccountName` to the name of your Cloud Account. 3. Set `buckets.awsPolicyRefs` to the IAM policies granting S3 access. For read-only source access, use the AWS managed policy `aws::AmazonS3ReadOnlyAccess`. For custom policies, omit the `aws::` prefix. ### Agent Token Generate a secure bearer token for internal cluster communication and set it in `orchestrator.agent.token`: ```bash theme={null} openssl rand -base64 32 ``` Anyone with network access to the UI can perform admin operations. Restrict external access using `orchestrator.ui.allowExternalAccess: false` or configure the domain firewall to limit access. ### AWS S3 (Backup, Optional) Only required if `orchestrator.backup.enabled: true`: 1. Create a separate S3 bucket for backups. Set `orchestrator.backup.s3Bucket` and `orchestrator.backup.s3Region`. 2. Create an IAM policy with the following permissions, replacing `YOUR_BUCKET_NAME`: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET_NAME", "arn:aws:s3:::YOUR_BUCKET_NAME/*" ] } ] } ``` 3. Set `orchestrator.backup.cloudAccountName` and `orchestrator.backup.s3Policy` to the name of your Cloud Account and the custom policy created above. ## Installation To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} # ============================================================================= # AWS Cloud Account and S3 Configuration # ============================================================================= buckets: cloudAccountName: my-cloud-account # Name of your configured Cloud Account awsPolicyRefs: # IAM policies for S3 access - aws::AmazonS3ReadOnlyAccess # Note: if using a custom policy, omit the aws:: prefix awsRegion: us-east-1 # Region of your S3 bucket sourceBucket: my-bucket # S3 bucket containing files to import # ============================================================================= # Tables Configuration # ============================================================================= tables: - name: addresses csvPath: - manticore-imports/addresses/data.csv config: haStrategy: noerrors agentRetryCount: 3 clusterMain: false segmentCount: 1 charsetTable: non_cont memLimit: 2G hasHeader: true secondaryIndexes: false schema: columns: - name: address_id type: attr_uint - name: street_number type: attr_uint - name: street_name type: field - name: city type: field - name: county type: field - name: state type: field - name: postal_code type: field - name: country type: field - name: latitude type: attr_float - name: longitude type: attr_float # ============================================================================= # Manticore Search Configuration # ============================================================================= manticore: image: manticoresearch/manticore:25.0.0 clusterName: manticore # Galera cluster name resources: cpu: 4 memory: 8Gi volumeset: capacity: 200 # GB per replica sharedVolumeset: capacity: 100 # GB shared across replicas and orchestrator # minScale = replica count used by orchestrator for coordination autoscaling: minScale: 3 maxScale: 4 metric: rps # rps for stateful, cpu for standard target: 100 scaleToZeroDelay: 300 rolloutOptions: maxSurgeReplicas: 25% maxUnavailableReplicas: '0' minReadySeconds: 10 scalingPolicy: OrderedReady terminationGracePeriodSeconds: 60 # IMPORTANT: same-gvc required for Galera replication firewall: internalAccess: type: same-gvc workloads: [] # ============================================================================= # Orchestrator Configuration # ============================================================================= orchestrator: version: v6.0.5 image: ghcr.io/controlplane-com/manticore-orchestrator/manticore-cpln-api logLevel: debug # debug, info, warn, error resources: cpu: 1 memory: 2Gi # Cron job settings schedule: "0 * * * *" # Cron schedule (default = every hour) action: import # init, import, health, repair tableName: addresses # Must match a name in tables[] suspend: true # Start suspended (trigger via UI/API) timeoutSeconds: 900 # Container timeout (default 15 minutes) importMemLimit: 2G # Memory limit for import jobs activeDeadlineSeconds: 14400 # Max job runtime (default 4 hours) # Orchestrator API api: version: v6.0.5 image: ghcr.io/controlplane-com/manticore-orchestrator/manticore-cpln-api logLevel: debug importPollInterval: 30s importPollTimeout: 2h resources: cpu: 0.25 memory: 256Mi autoscaling: maxScale: 3 minScale: 2 metric: cpu target: 80 # Agent sidecar agent: version: v6.0.5 image: ghcr.io/controlplane-com/manticore-orchestrator/manticore-cpln-agent # REQUIRED: Generate with `openssl rand -base64 32` token: "6Gl5uO9KkKAh1u+ymoBW98WCtjTFpljuhpLdKb+tNAA=" resources: cpu: 250m minCpu: 100m memory: 512Mi minMemory: 128Mi import: batchSize: 20000 # Rows per INSERT statement recovery: maxRetries: 5 # Retry attempts for cluster recovery initialBackoffSec: 5 # Initial delay between retries maxBackoffSec: 60 # Max backoff delay (exponential) # Web UI ui: version: v6.0.5 image: ghcr.io/controlplane-com/manticore-orchestrator/manticore-cpln-ui resources: cpu: 0.25 memory: 0.25Gi allowExternalAccess: true # false = internal GVC access only autoscaling: maxScale: 2 minScale: 1 metric: cpu target: 80 # Backup Configuration (Optional) backup: enabled: false version: v6.0.5 image: ghcr.io/controlplane-com/manticore-orchestrator/manticore-cpln-backup cloudAccountName: my-backup-cloud-account s3Bucket: my-backup-bucket s3Policy: - my-backup-policy s3Region: us-east-1 dataSet: addresses # Data set to back up prefix: manticore-backups # S3 prefix/folder for backups schedules: - {"table":"addresses","type":"delta","schedule":"0 2 * * *"} # Daily at 2am UTC - {"table":"addresses","type":"main","schedule":"0 2 1 * *"} # Monthly on 1st at 2am UTC activeDeadlineSeconds: 14400 resources: cpu: 1 memory: 1Gi # ============================================================================= # Domain Configuration (Optional) # ============================================================================= domain: enabled: false name: "" # FQDN, e.g., manticore.example.com dnsMode: cname # cname (subdomains) or ns (zone delegation) # ============================================================================= # Load Testing Configuration (Optional) # ============================================================================= loadTest: enabled: false image: grafana/k6:0.47.0 resources: cpu: 0.5 memory: 512Mi vus: 10 # Virtual users duration: "5m" # Test duration (e.g., 30s, 5m, 1h) rps: null # Target RPS (null = unlimited) replicas: 1 controller: image: alpine/curl schedule: "" # Cron expression (empty = manual only) testDurationBuffer: 60 # Seconds added to duration before scale-down target: port: 9308 # Manticore HTTP API port endpoint: search # "search" or "sql" query: index: addresses query: match: "*": "test" limit: 10 thresholds: p95ResponseTime: 500 # ms errorRate: 0.01 # 1% ``` ### S3 and Cloud Account * `buckets.cloudAccountName` — Name of the Control Plane Cloud Account with AWS trust configured. * `buckets.awsPolicyRefs` — IAM policies granting read access to the source S3 bucket. Use `aws::AmazonS3ReadOnlyAccess` for the AWS managed policy, or omit the `aws::` prefix for custom policies. * `buckets.awsRegion` — AWS region of the source S3 bucket. * `buckets.sourceBucket` — Name of the S3 bucket containing CSV files to import. ### Tables Each entry in `tables` defines a searchable index imported from a CSV file. * `name` — Table name used by Manticore and referenced by the orchestrator. * `csvPath` — List of S3 paths (relative to `sourceBucket`) for the CSV source files. Multiple paths create a distributed multi-segment table. * `config.haStrategy` — High-availability behavior when agents are unreachable (`noerrors` ignores unreachable agents). * `config.agentRetryCount` — Number of retry attempts when an agent doesn't respond. * `config.segmentCount` — Number of distributed segments for this table. Use more than 1 for very large datasets split across multiple CSVs. * `config.memLimit` — Memory limit for the Manticore index (e.g., `2G`). * `config.hasHeader` — Set to `true` if the CSV file includes a header row. * `config.secondaryIndexes` — When `true`, enables secondary indexes on both the main and delta tables for this entry. Defaults to `false`. * `schema.columns` — Column definitions. Each column has a `name` and a `type`. **Supported column types:** | Type | Description | | ---------------- | -------------------------------------- | | `field` | Full-text searchable string field | | `attr_uint` | Unsigned integer attribute | | `attr_float` | Float attribute | | `attr_bigint` | 64-bit integer attribute | | `attr_bool` | Boolean attribute | | `attr_string` | Non-indexed string attribute | | `attr_json` | JSON attribute | | `attr_multi` | Multi-value unsigned integer attribute | | `attr_multi_64` | Multi-value 64-bit integer attribute | | `attr_timestamp` | Unix timestamp attribute | ### Manticore Cluster * `manticore.clusterName` — Galera cluster name used for replication coordination. * `manticore.resources` — CPU and memory for each Manticore replica. * `manticore.volumeset.capacity` — Persistent storage per replica in GB. * `manticore.sharedVolumeset.capacity` — Shared storage in GB, accessible by all replicas and the orchestrator for import slot coordination. * `manticore.autoscaling.minScale` — Minimum replica count. This value is also used by the orchestrator to determine cluster quorum. It should match your intended fixed replica count. * `manticore.autoscaling.maxScale` — Maximum replica count for autoscaling. `manticore.firewall.internalAccess.type` must be set to `same-gvc`. Galera replication requires direct peer-to-peer communication between all replicas within the GVC. ### Orchestrator The orchestrator manages all cluster lifecycle operations including data imports, health checks, and repairs. **Cron Job** * `orchestrator.schedule` — Cron schedule for automated operations (default: every hour). * `orchestrator.action` — Operation to run: `init` (initial cluster setup), `import` (load CSV data), `health` (check cluster state), or `repair` (fix split-brain issues). * `orchestrator.tableName` — The table to target for the cron job. Must match a name in `tables`. * `orchestrator.suspend` — When `true`, the cron job is created in a suspended state and must be triggered manually via the UI or CLI. Recommended for production. * `orchestrator.timeoutSeconds` — Timeout for each cron container execution. * `orchestrator.activeDeadlineSeconds` — Maximum total runtime for an import job before it is terminated. **API** * `orchestrator.api.importPollInterval` — How often the API checks import progress. * `orchestrator.api.importPollTimeout` — Maximum time the API waits for an import to complete. * `orchestrator.api.autoscaling` — Min/max replicas and CPU target for the orchestrator API. **Agent Sidecar** * `orchestrator.agent.token` — Bearer token securing all internal communication between the orchestrator and agent sidecars. **Generate with `openssl rand -base64 32` and change before deploying.** * `orchestrator.agent.import.batchSize` — Number of rows per INSERT statement during imports (default: 20000). * `orchestrator.agent.recovery` — Retry settings for Galera cluster recovery after a split-brain event. **UI** * `orchestrator.ui.allowExternalAccess` — When `true`, the UI is accessible from the internet. Set to `false` to restrict access to within the GVC. ### Backup (Optional) Set `orchestrator.backup.enabled: true` to enable scheduled backups to S3. * `orchestrator.backup.cloudAccountName` — Cloud Account with write access to the backup S3 bucket. * `orchestrator.backup.s3Bucket` / `s3Region` — Backup bucket name and region. * `orchestrator.backup.s3Policy` — Custom IAM policy name(s) granting write access to the backup bucket (see [Prerequisites](#prerequisites)). * `orchestrator.backup.dataSet` — The table dataset to back up. * `orchestrator.backup.prefix` — S3 folder prefix for backup archives. * `orchestrator.backup.schedules` — List of backup schedules. Each entry specifies a `table`, a backup `type` (`delta` for incremental, `main` for full), and a `schedule` in cron format. ### Domain (Optional) * `domain.enabled` — Enable or disable the domain resource. * `domain.name` — Fully qualified domain name (e.g., `manticore.example.com`). Requires DNS configuration pointing to the GVC's load balancer. * `domain.dnsMode` — DNS routing mode: `cname` for subdomain-based routing or `ns` for zone delegation. When enabled, the domain routes `/api/*` to the orchestrator API and all other traffic to the UI. ### Load Testing (Optional) * `loadTest.enabled` — Enable or disable the k6 load test workload. * `loadTest.vus` — Number of virtual users. * `loadTest.duration` — Total test duration (e.g., `30s`, `5m`, `1h`). * `loadTest.rps` — Target requests per second (`null` = unlimited). * `loadTest.target.endpoint` — Search endpoint to target: `search` (JSON body) or `sql`. * `loadTest.thresholds.p95ResponseTime` — P95 response time threshold in milliseconds. * `loadTest.thresholds.errorRate` — Maximum acceptable error rate (e.g., `0.01` = 1%). ## External References Official Manticore Search documentation Manticore Search open-source repository Create a Control Plane Cloud Account for S3 access View the source files, default values, and chart definition # MariaDB Source: https://docs.controlplane.com/template-catalog/templates/mariadb Deploy MariaDB on Control Plane using the Template Catalog. Covers configuration, volumes, scaling, and single-replica setup with optional phpMyAdmin web interface. ## Overview MariaDB is an open-source relational database management system and a drop-in replacement for MySQL. This template deploys a single-replica MariaDB instance with persistent storage and an optional phpMyAdmin web interface for database management. MariaDB on Control Plane operates as a single-replica deployment. Do not scale up the replica count, as this would result in multiple isolated instances rather than a replicated cluster. ### What Gets Created * **Stateful MariaDB Workload** — A single-replica MariaDB database container with configurable resources. * **Volume Set** — Persistent storage for MariaDB data, with optional autoscaling. * **Secret** — An opaque secret storing the database name, root password, and user credentials, injected into the container at startup. * **Identity & Policy** — An identity bound to the workload with `reveal` access to the database credentials secret, and cloud storage access when backup is enabled. * **phpMyAdmin Workload** *(optional)* — A web-based database management interface. Enabled when `enablePhpMyAdmin: true`. * **Backup Cron Workload** *(optional)* — A scheduled `mysqldump` backup job that writes compressed dumps to AWS S3 or GCS. This template does not create a GVC. You must deploy it into an existing GVC. ## Installation This template has no external prerequisites unless backup is enabled. To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} image: mariadb:11 resources: minCpu: 100m minMemory: 128Mi maxCpu: 250m maxMemory: 264Mi timeoutSeconds: 15 config: user: username password: password rootPassword: root-password db: test volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) autoscaling: enabled: false maxCapacity: 100 # Maximum capacity in GiB minFreePercentage: 10 # Trigger scaling when free space drops below this percentage scalingFactor: 1.2 # Multiply current capacity by this factor when scaling up internalAccess: type: same-gvc # options: none, same-gvc, same-org, workload-list workloads: # Note: can only be used if type is same-gvc or workload-list #- //gvc/GVC_NAME/workload/WORKLOAD_NAME enablePhpMyAdmin: true backup: enabled: false image: controlplanecorporation/mysql-backup:1.0 # compatible with all MariaDB versions schedule: "0 2 * * *" # daily at 2am UTC resources: cpu: 100m memory: 128Mi provider: aws # Options: aws or gcp aws: bucket: my-backup-bucket region: us-east-1 cloudAccountName: my-backup-cloudaccount policyName: my-backup-policy prefix: mariadb/backups gcp: bucket: my-backup-bucket cloudAccountName: my-backup-cloudaccount prefix: mariadb/backups ``` ### Credentials * `config.db` — Name of the database created on startup. * `config.rootPassword` — Password for the MariaDB root user. **Change before deploying to production.** * `config.user` — Name of the non-root database user created on startup. * `config.password` — Password for the non-root user. **Change before deploying to production.** These values are only applied on first startup when the data directory is empty. Updating them after the initial deployment will have no effect on the running database. To change credentials or the database name on an existing instance, use MariaDB's native commands (e.g. `ALTER USER`, `RENAME DATABASE`). ### Resources * `resources.minCpu` / `resources.minMemory` — Minimum CPU and memory guaranteed to the workload. * `resources.maxCpu` / `resources.maxMemory` — Maximum CPU and memory the workload can use. * `timeoutSeconds` — Workload timeout in seconds. ### Storage * `volumeset.capacity` — Initial volume size in GiB (minimum 10). * `volumeset.autoscaling.enabled` — Automatically expand the volume as it fills. When enabled: * `maxCapacity` — Maximum volume size in GiB. * `minFreePercentage` — Trigger a scale-up when free space drops below this percentage. * `scalingFactor` — Multiply the current capacity by this factor when scaling up. ### Internal Access * `internalAccess.type` — Controls which workloads can connect to MariaDB on port `3306`: | Type | Description | | --------------- | --------------------------------------------------------------- | | `none` | No internal access allowed | | `same-gvc` | Allow access from all workloads in the same GVC | | `same-org` | Allow access from all workloads in the same organization | | `workload-list` | Allow access only from specific workloads listed in `workloads` | ### phpMyAdmin * `enablePhpMyAdmin` — When `true`, deploys a phpMyAdmin workload for browser-based database management. Disable by setting to `false`. ### Connecting to MariaDB Once deployed, connect to the database from within the same GVC using: ```text theme={null} RELEASE_NAME-maria.GVC_NAME.cpln.local:3306 ``` ## Backup Backup is disabled by default. When enabled, a cron workload runs `mysqldump` on the configured schedule and uploads compressed dumps to AWS S3 or GCS. * `backup.enabled` — Enable scheduled backups. * `backup.schedule` — Cron expression for backup frequency (default: daily at 2am UTC). * `backup.provider` — `aws` or `gcp`. * `backup.resources.cpu` / `backup.resources.memory` — Resources for the backup cron container. ### AWS S3 Before enabling backup with `provider: aws`, complete the following in your AWS account: 1. Create an S3 bucket. Set `backup.aws.bucket` to its name and `backup.aws.region` to its region. 2. If you do not have a Cloud Account set up, refer to the docs to [Create a Cloud Account](/guides/create-cloud-account). Set `backup.aws.cloudAccountName` to its name. 3. Create an IAM policy with the following JSON, replacing `YOUR_BUCKET_NAME`: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket", "s3:GetObjectVersion", "s3:DeleteObjectVersion" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET_NAME", "arn:aws:s3:::YOUR_BUCKET_NAME/*" ] } ] } ``` 4. Set `backup.aws.policyName` to the name of the policy created in step 3. 5. Set `backup.aws.prefix` to the folder path where backups will be stored. ### GCS Before enabling backup with `provider: gcp`, complete the following in your GCP account: 1. Create a GCS bucket. Set `backup.gcp.bucket` to its name. 2. If you do not have a Cloud Account set up, refer to the docs to [Create a Cloud Account](/guides/create-cloud-account). Set `backup.gcp.cloudAccountName` to its name. 3. Add the **Storage Admin** role to the GCP service account associated with the Cloud Account. 4. Set `backup.gcp.prefix` to the folder path where backups will be stored. ## Restoring a Backup Run the following from a client with access to the backup bucket. For GCS, replace `aws s3 cp s3://...` with `gsutil cp gs://...`. ```sh theme={null} aws s3 cp s3://BUCKET_NAME/PREFIX/BACKUP_FILE.gz - \ | gunzip \ | sed '/^SET @@GLOBAL.GTID_PURGED/d' \ | mariadb \ --host=RELEASE_NAME-maria.GVC_NAME.cpln.local \ --port=3306 \ --user=root ``` ## External References Official MariaDB documentation phpMyAdmin user documentation Source code for the MySQL backup container image View the source files, default values, and chart definition # Meilisearch Source: https://docs.controlplane.com/template-catalog/templates/meilisearch Deploy Meilisearch on Control Plane using the Template Catalog. An open-source instant, typo-tolerant REST search engine with persistent index storage, master-key authentication, scheduled snapshots, and platform volume backups. Covers the prerequisite key secret, access modes, and upgrades. ## Overview Meilisearch is an open-source (MIT) search engine for instant, typo-tolerant, faceted search — a self-hosted alternative to Algolia, driven entirely by a REST API. This template deploys a single Meilisearch 1.52 server with persistent index storage, mandatory master-key authentication, an indexing memory budget derived from the container's own memory limit, optional scheduled snapshots, and scheduled platform volume backups. The image is the Community build, compiled without any Enterprise code. Everything it contains is available here; replication and sharding are not part of it (see [Important Notes](#important-notes)). ### Architecture * **Meilisearch server** — A single-replica `stateful` workload (`{release}-meilisearch`) serving the REST API on port `7700`. There is no second port: the API, `/metrics`, and the search preview all live behind `7700`. * **Persistent data** — One volume set mounted at `/meili_data` holding the LMDB index (`data.ms`), Meilisearch's own `.snapshot` files, and dumps. All three paths are set explicitly to absolute locations on the volume, so nothing durable ever lands on the container's ephemeral layer. * **Authentication** — Always on. The master key comes from an opaque secret you create yourself and reference by name; Meilisearch derives its four built-in API keys from it. ### What Gets Created * **Stateful Meilisearch Workload** — A single replica serving the REST API on `7700`, with `/health` used for both probes. * **Volume Set** — Persistent `ext4` storage at `/meili_data` with scheduled snapshots and a final snapshot on delete. * **Identity** — An identity bound to the workload, used to read the master-key secret. * **Policy** — `reveal` on exactly that one secret and nothing else. The template creates no secrets of its own, and needs no database, cache, or object store. This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites **Create the master-key secret before installing.** This is required — Meilisearch refuses every route except `GET /health` without a master key, and a workload referencing a secret that does not exist waits indefinitely with no obvious error. It is an **opaque** secret with `encoding: plain` whose entire payload is the key: ```bash theme={null} printf '%s' "$(openssl rand -base64 32)" | \ cpln secret create-opaque --name my-meilisearch-master-key --encoding plain -f - ``` Then set `auth.secretName` to the name you used. Installing with an empty `auth.secretName` fails at render time. The key must be **at least 16 bytes** while `server.env` is `production`. A shorter one makes the server refuse to launch with `The master key must be at least 16 bytes in a production environment.` Rotating the key later regenerates every API key derived from it, breaking every deployed client — treat it as write-once, or plan to redistribute all keys. Nothing else is required: no cloud account, no bucket, no external database. Backups use platform volume snapshots. Install the template using your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} # ─── Image ──────────────────────────────────────────────────────────────────── image: getmeili/meilisearch:v1.52.0 # ─── Resources ──────────────────────────────────────────────────────────────── # Searches are served from a memory-mapped index, so steady-state RAM is modest; # indexing is the hungry phase. Upstream sizing guide: RAM ≈ ⅓ of on-disk index. resources: minCpu: 250m maxCpu: 1000m minMemory: 512Mi maxMemory: 2Gi tuning: indexingMemoryPercent: 50 # MEILI_MAX_INDEXING_MEMORY = this percent of maxMemory (20–80) # ─── Storage ────────────────────────────────────────────────────────────────── volumeset: capacity: 20 # GiB (platform minimum 10) — index, snapshots and dumps share it # ─── Master key (REQUIRED — create the secret BEFORE installing) ────────────── # The workload will NOT start until this opaque secret exists. Create it with: # printf '%s' "$(openssl rand -base64 32)" | \ # cpln secret create-opaque --name my-meilisearch-master-key --encoding plain -f - # Must be at least 16 bytes while server.env is production. # Changing it later regenerates every Meilisearch API key derived from it. auth: secretName: my-meilisearch-master-key # name of your pre-created opaque secret # ─── Server ─────────────────────────────────────────────────────────────────── server: env: production # production | development — development also serves the search-preview UI at / logLevel: INFO # ERROR | WARN | INFO | DEBUG | TRACE maxPayloadSize: "100 MB" # largest accepted request body; a bigger import is rejected with HTTP 413 telemetry: false # true = send anonymous usage data upstream upgradeDb: false # true for ONE deploy when moving to a newer image tag, then set back to false # ─── Metrics (experimental upstream) ────────────────────────────────────────── metrics: enabled: false # true = Prometheus /metrics on port 7700 (needs an API key with the metrics.get action) # ─── Meilisearch snapshots (app-consistent files on the volumeset) ──────────── snapshots: enabled: false # scheduled .snapshot files in /meili_data/snapshots — only the newest is kept intervalSeconds: 86400 # seconds between snapshots # ─── Backup (platform volume snapshots — no cloud account needed) ───────────── backup: enabled: true schedule: "0 3 * * *" # cron in UTC — daily 03:00 (hourly is the platform max) retention: 7d # how long each snapshot is kept (e.g. 7d, 720h, 30d) # ─── Access ─────────────────────────────────────────────────────────────────── publicAccess: enabled: false # true = REST API over HTTPS on the auto *.cpln.app endpoint internalAccess: type: same-gvc # none | same-gvc | same-org | workload-list workloads: [] # used only with workload-list # workloads: # - //gvc/GVC_NAME/workload/WORKLOAD_NAME ``` ### Image and resources * `image` — The Meilisearch server image. Pin a concrete tag; changing it on a populated volume requires the upgrade procedure below. * `resources.minCpu` / `resources.maxCpu` / `resources.minMemory` / `resources.maxMemory` — CPU reservation and limit, memory reservation and limit for the Meilisearch container. * `tuning.indexingMemoryPercent` — Percentage of `resources.maxMemory` given to the indexing arena. Accepted range is 20–80; anything else is rejected at render time. Searches are served from a memory-mapped index, so steady-state RAM is modest — indexing is the hungry phase. Upstream's rough guide is RAM ≈ ⅓ of the on-disk index size. The indexing budget is **derived, not absolute**: `MEILI_MAX_INDEXING_MEMORY` is set to `indexingMemoryPercent` of `resources.maxMemory`. At the defaults that is `1024MiB`; with `maxMemory: 8Gi` it becomes `4096MiB`. There is deliberately no absolute override, so the budget can never drift from the container's own limit. Left to itself, Meilisearch budgets two thirds of the RAM it detects — in a container that is routinely the node's, not your limit — and OOM-kills itself part-way through an indexing task. Raise `maxMemory` to index faster and to hold a larger index; raise `indexingMemoryPercent` only if the instance indexes far more often than it searches. The API rejects a `maxCpu`-to-`minCpu` ratio greater than 4:1. The defaults sit exactly at 4:1, so raising `maxCpu` means raising `minCpu` too. ### Storage * `volumeset.capacity` — Initial volume size in GiB. The platform minimum is 10, and the chart rejects anything smaller at render time. The single `ext4` volume at `/meili_data` holds `data.ms` (the LMDB index), `snapshots/`, and `dumps/`. Upstream advises against network-mounted storage for the memory-mapped index, so this is a block device rather than shared storage. ### Master key * `auth.secretName` — Name of your prerequisite opaque secret. Its whole payload is the master key. The chart creates a policy granting the workload `reveal` on exactly that secret. ### Server * `server.env` — `production` (default) or `development`. Only `development` serves the browser search-preview UI at `/`; the master key protects every route either way. * `server.logLevel` — One of `ERROR`, `WARN`, `INFO` (default), `DEBUG`, `TRACE`. * `server.maxPayloadSize` — Largest accepted request body. A larger import is rejected with **HTTP 413** and `The provided payload reached the size limit.` * `server.telemetry` — When `false` (default), no anonymous usage data is sent upstream. * `server.upgradeDb` — Set to `true` for exactly one deploy when moving to a newer image tag, then back to `false`. See [Upgrading Meilisearch](#upgrading-meilisearch). With `server.env: production`, `GET /` returns **HTTP 200** with `{"status":"Meilisearch is running"}` and no HTML. That is a healthy instance, not a broken one — the search-preview UI is suppressed in production mode by design. Set `server.env: development` if you want the playground at `/`. ### Metrics * `metrics.enabled` — When `true`, Meilisearch exposes Prometheus metrics at `/metrics` on port `7700`. The route requires an API key with the `metrics.get` action; unauthenticated requests get `401`. These are index-level metrics (documents indexed, searches per index, database size) that the platform's built-in workload metrics cannot see. With `metrics.enabled: false` (the default), `GET /metrics` returns **HTTP 400** with `{"code":"feature_not_enabled"}` — not a `404`. ### Snapshots and backup Two different things are called "snapshots" here, and they do different jobs: | Block | What it is | Default | | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ------- | | `backup` | **Platform** volume snapshots — crash-consistent whole-disk copies. No cloud account or bucket needed. | On | | `snapshots` | **Meilisearch's own** `.snapshot` files written onto the same volume. Only the newest is kept. Required by upstream's restore procedure. | Off | * `backup.enabled` — When `true` (default), the volume set takes snapshots on `backup.schedule`. * `backup.schedule` — Cron expression in UTC. Hourly is the platform maximum frequency. * `backup.retention` — How long each snapshot is kept (for example `7d`, `720h`, `30d`). * `snapshots.enabled` — When `true`, Meilisearch writes a scheduled `.snapshot` file into `/meili_data/snapshots`, rewriting it each interval. * `snapshots.intervalSeconds` — Seconds between those snapshots. The volume set also takes a final snapshot when it is deleted, retained for `backup.retention`. That final snapshot happens regardless of `backup.enabled` — setting it to `false` disables *scheduled* snapshots only. Changing any `backup` value updates the volume set without restarting the workload. You can also trigger a Meilisearch snapshot on demand with `POST /snapshots`, whether or not the scheduled ones are enabled. ### Access * `publicAccess.enabled` — When `true`, the REST API is served over HTTPS on the automatically assigned `*.cpln.app` canonical endpoint. When `false` (default), no external inbound traffic is allowed. * `internalAccess.type` — Internal firewall scope of the workload: | Type | Description | | --------------- | ---------------------------------------------------------------------- | | `none` | No internal access — in-GVC traffic is refused. | | `same-gvc` | Allow access from all workloads in the same GVC (default). | | `same-org` | Allow access from all workloads in the same organization. | | `workload-list` | Allow access only from workloads listed in `internalAccess.workloads`. | * `internalAccess.workloads` — Workload links (`//gvc/GVC_NAME/workload/WORKLOAD_NAME`), used only with `workload-list`. Firewall changes take **45–150 seconds** to propagate. After toggling `publicAccess` or `internalAccess`, requests may keep returning the old behavior (or `503`) for more than a minute before settling — re-test rather than concluding the knob does not work. ## Connecting | Target | Address | Credentials | | -------------------------- | ---------------------------------------------------- | ---------------------------------------------------- | | Internal REST (same GVC) | `http://{release}-meilisearch.{gvc}.cpln.local:7700` | `Authorization: Bearer ` | | Public REST *(if enabled)* | `https://.cpln.app` | `Authorization: Bearer ` | | Health | `GET /health` on `7700` | None — the one route the master key does not protect | The canonical hostname appears under `status.canonicalEndpoint`: ```bash theme={null} cpln workload get {release}-meilisearch --gvc {gvc} -o yaml ``` Public traffic is HTTPS at the platform edge; same-GVC traffic is plain HTTP carried over the mesh's own mTLS. A fresh install typically reaches `ready: true` in 45–60 seconds. ### Use a scoped API key, not the master key On first boot Meilisearch derives four keys from the master key. Fetch them once and hand the right one to each caller: ```bash theme={null} curl -H "Authorization: Bearer YOUR_MASTER_KEY" \ http://{release}-meilisearch.{gvc}.cpln.local:7700/keys ``` | Key | Use it for | | ------------------------------- | ------------------------------------------------------------------------ | | Default Search API Key | Front-ends — search only; writes are rejected with `403 invalid_api_key` | | Default Admin API Key | Your indexing pipeline — everything except key management | | Default Read-Only Admin API Key | Dashboards and monitoring | | Default Chat API Key | The experimental chat route | ### Index and search Indexing is asynchronous: writes return `202 Accepted` with a `taskUid` you can poll on `/tasks`. ```bash theme={null} MEILI=http://{release}-meilisearch.{gvc}.cpln.local:7700 KEY=YOUR_ADMIN_API_KEY # create an index curl -X POST "$MEILI/indexes" -H "Authorization: Bearer $KEY" \ -H 'Content-Type: application/json' \ -d '{"uid":"movies","primaryKey":"id"}' # add documents curl -X POST "$MEILI/indexes/movies/documents" -H "Authorization: Bearer $KEY" \ -H 'Content-Type: application/json' \ -d '[{"id":1,"title":"Interstellar","genre":"sci-fi"}]' # search — typo tolerance is on by default curl -X POST "$MEILI/indexes/movies/search" -H "Authorization: Bearer $KEY" \ -H 'Content-Type: application/json' \ -d '{"q":"interstelar"}' ``` The misspelled query returns the `Interstellar` document. Use the Search API key, not the admin key, for anything running in a browser. ## Upgrading Meilisearch Meilisearch refuses to open a database written by an older version, so raising `image` on a populated volume fails by design until you ask for the migration: The migration is not atomic. Check the volume set's snapshots before starting. Set `image` to the new tag **and** `server.upgradeDb: true` in the same deploy. The database is upgraded on startup. Once the workload is ready on the new tag, set `server.upgradeDb: false` and redeploy so a migration flag is not left permanently on. If you raise the image tag *without* the flag, the new container exits with `Your database version (X) is incompatible with your current engine version (Y).` and restarts in a loop. The platform keeps the old, working version serving for roughly two minutes before the deployment flips to not-ready, so a forgotten flag looks fine at first and then goes down — check the deployment status, not just the first minute after the upgrade. ## Important Notes * **The master-key secret must exist before you install.** If it does not, the deployment hangs waiting on a secret that will never resolve and looks broken. Verify with `cpln secret get my-meilisearch-master-key` first. * **Rotating the master key changes every API key.** All four built-in keys are derived from it, so a rotation silently breaks every deployed client. Treat it as write-once, or plan to redistribute all keys. * **Single replica, by design and by edition.** Replication and sharding are Meilisearch Enterprise features and are not compiled into the Community image this template ships, so there is no `replicas` knob and no failover. A restart or upgrade is a real outage of the search endpoint: measured at **87 seconds** (68 consecutive failed requests out of 349, at 1 request per second across a `helm upgrade`). The failures begin about **51 seconds after the upgrade command returns**, so the CLI looks finished well before the outage starts. An abrupt replica loss recovers faster — about **20 seconds**, with data intact. Have your application fall back to a database query while search is unavailable. * **`server.env: production` does not serve the search-preview UI**, and `GET /` returns **200** with `{"status":"Meilisearch is running"}`. That is a healthy instance. Use `server.env: development` for the browser playground. * **`/metrics` returns 400 `feature_not_enabled`, not 404, when metrics are disabled.** Set `metrics.enabled: true` to serve it, and call it with a key that has the `metrics.get` action. * **Size indexing with `resources.maxMemory`.** The indexing budget is derived from it via `tuning.indexingMemoryPercent` (default 50%), and there is no absolute override, so the two cannot drift apart. * **Over-size imports return HTTP 413.** A body larger than `server.maxPayloadSize` is rejected outright and nothing is written — batch large imports or raise the limit. * **Install into a single-location GVC.** A workload runs in every location its GVC has, and each location gets its own volume — two locations means two independent indexes diverging silently behind one endpoint. * **Uninstall deletes the volume set.** A final snapshot is retained for `backup.retention`, and your own master-key secret is left untouched. ## External References Official Meilisearch documentation Every environment variable and command-line option Master key, derived keys, and scoping access Meilisearch's own snapshot and dump formats The database migration procedure between versions View the source files, default values, and chart definition # Metabase Source: https://docs.controlplane.com/template-catalog/templates/metabase Deploy Metabase on Control Plane using the Template Catalog. Open-source BI with dashboards, a SQL editor, and scheduled reports, backed by a highly available PostgreSQL app database. Covers the encryption-key prerequisite, database modes, first-boot admin setup, and backups. ## Overview Metabase is an open-source business intelligence platform — dashboards, a SQL editor, and scheduled report subscriptions. This template deploys the free open-source edition backed by a highly available PostgreSQL cluster by default. The bundled PostgreSQL is Metabase's own **app database** (users, dashboards, saved connections); the databases you analyze are **data sources** you connect in the app after install — they are never installed or touched by this template. The admin account is created automatically on first boot, and the workload only starts receiving traffic once setup is complete, so there is never a publicly reachable setup page. ### Architecture * **Metabase** — A single-replica standard workload serving the UI and API on port `3000`. Stateless by design: all application state (questions, dashboards, users, saved connections) lives in the PostgreSQL app database, so Metabase itself has no volume set. * **PostgreSQL (HA, default)** — The [postgres-highly-available](/template-catalog/templates/postgres-highly-available) template as a subchart: 3× Patroni PostgreSQL, 3× etcd, and a HAProxy leader-routing endpoint Metabase connects through. * **PostgreSQL (dev/lightweight, optional)** — The single-instance [postgres](/template-catalog/templates/postgres) template instead, for lighter non-HA deployments. ### What Gets Created * **Standard Metabase Workload** — A single stateless replica serving the UI and API on port `3000`. * **Database Workloads** — HA mode: a stateful Patroni PostgreSQL workload, a stateful etcd workload, and a standard HAProxy leader-routing workload. Single mode: one stateful PostgreSQL workload. * **Volume Sets** — The database subchart's persistent volumes (10 GiB per replica by default). Metabase itself has none. * **Secrets** — Admin bootstrap credentials, the start script that creates the admin account on first boot, and the database credentials from the subchart. * **Identity & Policy** — A least-privilege policy granting the Metabase identity `reveal` on exactly the secrets it uses, including your pre-created encryption-key secret. * **Cron Backup Workload** *(optional)* — When database backups are enabled. This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites Metabase encrypts the connection details of every data source you save with a key it reads from an [opaque secret](/guides/create-secret/opaque) that you create **before** installing. The key is never passed through values. Generate a random string of at least 16 characters, for example: ```bash theme={null} openssl rand -hex 24 ``` Create an [opaque secret](/guides/create-secret/opaque) in your org with encoding `plain` whose payload is the generated key. Set its name in `encryptionKey.secretName`. Store a copy of the key somewhere safe, outside Control Plane. Losing or changing the encryption key means re-entering every saved database connection — Metabase can no longer decrypt them. Rotation is only possible offline, using Metabase's `rotate-encryption-key` command. Back the key up before installing. For optional database backups, you also need a bucket and access setup for one of the supported providers — see [Backing Up](#backing-up). Once your encryption-key secret exists, install the template using your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Choosing a Database Mode Exactly one of the two database modes must be enabled — the chart enforces this at render and fails the install with a clear message otherwise. | | `postgresHA` (default) | `postgres` | | ----------------- | --------------------------------------------------------------- | -------------------------------------- | | What runs | 3× Patroni PostgreSQL, 3× etcd, HAProxy leader endpoint | One single-replica PostgreSQL workload | | Database failover | Automatic (Patroni leader election) | None | | Footprint | 8 replicas across 3 workloads (3× Patroni, 3× etcd, 2× HAProxy) | 1 workload | | Best for | Production | Development and lightweight installs | A fresh HA-mode install takes roughly 10–15 minutes to fully converge; single mode is ready in about 2 minutes. ## Configuration The default `values.yaml` for this template: ```yaml theme={null} image: metabase/metabase:v0.63.1.3 resources: cpu: 1000m memory: 2Gi minCpu: 500m minMemory: 1Gi encryptionKey: secretName: my-metabase-encryption-key # name of your pre-created opaque secret (see Prerequisites) admin: # admin account, created automatically on first boot email: admin@example.com # admin login email firstName: Metabase lastName: Admin password: change-me-metabase-1 # change before installing; letters + digits, 8+ chars siteName: Metabase # instance name shown in the UI and emails publicAccess: enabled: true # UI + API on the canonical *.cpln.app HTTPS endpoint internalAccess: # internal firewall scope (in-GVC API callers) type: same-gvc # options: none, same-gvc, same-org, workload-list workloads: [] # used with workload-list, e.g. //gvc/GVC_NAME/workload/WORKLOAD_NAME postgresHA: # default: highly available PostgreSQL app database enabled: true postgres: username: metabase password: change-me-metabase-db-password # change before installing database: metabase replicas: 3 volumeset: capacity: 10 # initial capacity in GiB per replica (minimum is 10) backup: enabled: false # optional — see Backing Up mode: logical # logical or wal-g resources: cpu: 100m memory: 128Mi logical: image: ghcr.io/controlplane-com/backup-images/postgres-backup:17.1.0 schedule: "0 2 * * *" walg: intervalSeconds: 21600 provider: aws # options: aws, gcp, minio aws: bucket: metabase-pg-backup-bucket region: us-east-1 cloudAccountName: my-s3-cloud-account policyName: metabase-pg-backup-policy prefix: postgres/backups gcp: bucket: metabase-pg-backup-bucket cloudAccountName: my-gcs-cloud-account prefix: postgres/backups minio: endpoint: http://my-minio-workload:9000 bucket: metabase-pg-backup-bucket accessKey: my-minio-username secretKey: my-minio-password prefix: postgres/backups postgres: # dev/lightweight: single-instance PostgreSQL (disable postgresHA first) enabled: false config: username: metabase password: change-me-metabase-db-password # change before installing database: metabase volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) backup: enabled: false # optional — see Backing Up image: ghcr.io/controlplane-com/backup-images/postgres-backup:18.1.0 schedule: "0 2 * * *" resources: cpu: 100m memory: 128Mi provider: aws # options: aws, gcp, minio aws: bucket: metabase-pg-backup-bucket region: us-east-1 cloudAccountName: my-s3-cloud-account policyName: metabase-pg-backup-policy prefix: postgres/backups gcp: bucket: metabase-pg-backup-bucket cloudAccountName: my-gcs-cloud-account prefix: postgres/backups minio: endpoint: http://my-minio-workload:9000 bucket: metabase-pg-backup-bucket accessKey: my-minio-username secretKey: my-minio-password prefix: postgres/backups ``` ### Metabase Instance * `image` — The Metabase open-source container image. * `resources` — CPU and memory for the Metabase container. The template caps the JVM at 75% of the container memory limit (`JAVA_OPTS: -XX:MaxRAMPercentage=75.0`), so raising `resources.memory` also raises the Java heap. The 2 GiB default is sized for the JVM — lowering it is not recommended. * `encryptionKey.secretName` — Name of your pre-created opaque secret holding the key that encrypts saved data-source credentials. See [Prerequisites](#prerequisites). * `admin.*` — The admin account, created automatically on first boot against the local setup API. There is no unauthenticated setup page at any point: the workload only becomes ready — and only starts receiving traffic — once setup is complete, and if setup fails it never becomes ready at all (fail-closed). **Change `admin.password` before installing** — it must pass Metabase's complexity check (letters + digits, 8+ characters), and none of the `admin.*` values may contain double quotes or backslashes (enforced at render). The account is created exactly once, on first boot — changing these values later does not modify the existing account. * `siteName` — The instance name shown in the UI and in emails Metabase sends. ### Access * `publicAccess.enabled` — Serve the UI and API on the canonical `*.cpln.app` HTTPS endpoint (default). Everything behind the endpoint is gated by Metabase's own login. Set to `false` for an internal-only instance (external requests are blocked at the edge; in-GVC callers still reach it per `internalAccess`). * `internalAccess.type` — Internal firewall scope of the Metabase workload: | Type | Description | | --------------- | ---------------------------------------------------------------------- | | `none` | No internal access. | | `same-gvc` | Allow access from all workloads in the same GVC (default). | | `same-org` | Allow access from all workloads in the same organization. | | `workload-list` | Allow access only from workloads listed in `internalAccess.workloads`. | ### App Database Enable exactly one of `postgresHA` (production, default) or `postgres` (dev/lightweight) — see [Choosing a Database Mode](#choosing-a-database-mode). In both modes, **change the database password before installing** (`postgresHA.postgres.password` / `postgres.config.password`). Metabase is wired to the active database automatically — the HAProxy leader endpoint in HA mode, or the single instance directly in dev mode. **Template version `1.0.0` did not compact the etcd cluster inside the bundled highly available database**, so etcd's backend grows with time alone and goes read-only once it reaches its 2 GiB quota — after roughly 110 days — taking PostgreSQL failover with it. Only installs running the HA database are affected (`postgresHA.enabled`, the default here); see [etcd History Compaction](/template-catalog/templates/postgres-highly-available#etcd-history-compaction) for the mechanism and the symptoms. Upgrade to `1.0.1` or later to turn compaction on: that stops further growth but cannot shrink a backend that has already grown, and a cluster that has already raised a `NOSPACE` alarm needs operator recovery rather than an upgrade. ## Connecting | What | Value | | ------------------------------------ | ---------------------------------------------------------------------------------------------------------- | | UI / API (public) | `https://.cpln.app` — `status.canonicalEndpoint` of `{release}-metabase` | | Internal (same GVC) | `http://{release}-metabase.{gvc}.cpln.local:3000` | | Login | `admin.email` / `admin.password` | | App database (internal, HA mode) | `{release}-postgres-ha-proxy.{gvc}.cpln.local:5432`, credentials in the `{release}-postgres-config` secret | | App database (internal, single mode) | `{release}-postgres.{gvc}.cpln.local:5432`, credentials in the `{release}-pg-config` secret | ### Adding Data Sources The databases you analyze are added inside Metabase after install (**Admin → Databases**) — the template never installs or touches them. To analyze a database running on Control Plane, use its internal endpoint as the host, e.g. `{workload}.{gvc}.cpln.local:5432`. Any database Metabase can reach — inside or outside Control Plane — works as a data source. Saved connection credentials are encrypted at rest with your encryption key. ## Backing Up Database backups are optional and disabled by default. They cover the app database — the questions, dashboards, users, and saved connections that make up your Metabase instance. Enable them with `postgresHA.backup.enabled` or `postgres.backup.enabled` (matching your database mode), and complete the storage setup for your provider **before** installing. The values below are shown under `backup.*` — set them within the enabled database block. Create an S3 bucket. Set `backup.aws.bucket` and `backup.aws.region` to match. If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) for your AWS account. Set `backup.aws.cloudAccountName` to its name. Create an AWS IAM policy with the JSON below (replace `YOUR_BUCKET`), then set `backup.aws.policyName` to the policy's name: ```json theme={null} { "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Action": [ "s3:ListBucket", "s3:GetBucketLocation", "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:AbortMultipartUpload" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET", "arn:aws:s3:::YOUR_BUCKET/*" ] }] } ``` Create a GCS bucket. Set `backup.gcp.bucket` to its name. If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) for your GCP project. Set `backup.gcp.cloudAccountName` to its name — the backup identity is granted access to the bucket keylessly (no stored credentials). Create your bucket on the server. Set `backup.minio.bucket` to its name. Set `backup.minio.endpoint` to the S3 API address including port. For the `minio` marketplace template deployed in the same GVC, this is `http://WORKLOAD_NAME:9000`. Set `backup.minio.accessKey` and `backup.minio.secretKey` to credentials with access to the bucket. In HA mode, `backup.mode` selects `logical` (scheduled `pg_dump` via a cron workload) or `wal-g` (continuous WAL archiving). The single-instance mode takes scheduled logical dumps. ## Important Notes * **Back up the encryption-key secret** — losing or changing it means re-entering every saved database connection; rotation is only possible offline via Metabase's `rotate-encryption-key` command. * **Change `admin.password` and the database password** (`postgresHA.postgres.password` / `postgres.config.password`) before installing. An admin password that fails Metabase's complexity check (letters + digits, 8+ characters) keeps the workload unready by design. * **Metabase is single-replica in this template** — the default HA PostgreSQL backend removes the database as a failure point. * **Upgrades restart the single replica** — expect a few minutes of UI downtime per Helm upgrade; the HA app database keeps running and no data is lost. * **Uninstall deletes the app-database volume sets** — all questions, dashboards, and users. Enable backups if the data matters. * **This template ships the open-source image only** — Pro/Enterprise features (SSO, sandboxing, config-file init) are not available. ## External References Official Metabase documentation Metabase environment variables reference How the encryption key protects saved connection credentials Upstream guidance on running Metabase in production View the source files, default values, and chart definition # Mimir Source: https://docs.controlplane.com/template-catalog/templates/mimir Deploy Grafana Mimir on Control Plane using the Template Catalog. A self-hosted long-term metrics store for your own Prometheus, Alloy, or OpenTelemetry metrics, backed by S3, GCS, or S3-compatible object storage. ## Overview Grafana Mimir is a long-term Prometheus metrics store you own. This template deploys Mimir in monolithic mode: your own collectors (Prometheus, Grafana Alloy, OpenTelemetry) push metrics in via Prometheus `remote_write`, and anything that speaks PromQL — your own Grafana, dashboards, scripts — queries them back, with metric blocks durably stored in your object bucket. This is a **self-hosted metrics store for your own metrics from your own sources**. It is separate from — and not a replacement for — Control Plane's built-in observability, which continues to collect and dashboard your workloads' metrics natively. ### Architecture * **Mimir** — A single-replica stateful workload running all Mimir components in one process (`target: all`). Remote-write ingest and PromQL query on port `8080`, internal gRPC on `9095`, memberlist on `7946` (self-contained ring — no Consul or etcd). * **Object storage** — Metric blocks live in your S3, GCS, or S3-compatible bucket; the persistent volume holds only the ingester WAL/TSDB and compactor workspace. ### What Gets Created * **Stateful Mimir Workload** — Serves remote-write ingest and PromQL query on port `8080`. * **Volume Set** — 20 GiB at `/data` for the ingester WAL/TSDB and compactor workspace. * **Secret** — An opaque secret holding the rendered Mimir configuration, mounted as a file. * **Identity & Policy** — An identity bound to the workload with `reveal` access to the config secret only, plus cloud access scoped to exactly your bucket (AWS and GCP backends). This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites Mimir requires an existing bucket in one of the supported backends and the access setup for it. Complete the steps for your chosen backend before installing. AWS S3 uses a Control Plane cloud identity — no credentials are stored; the workload's identity vends temporary credentials at runtime. Create an S3 bucket. Set `storage.aws.bucket` and `storage.aws.region` to match. If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) for your AWS account. Set `storage.aws.cloudAccountName` to its name. Create an AWS IAM policy with the JSON below (replace `YOUR_BUCKET`), then set `storage.aws.policyName` to the policy's name (bare name, not ARN): ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["s3:ListBucket", "s3:GetObject", "s3:PutObject", "s3:DeleteObject"], "Resource": [ "arn:aws:s3:::YOUR_BUCKET", "arn:aws:s3:::YOUR_BUCKET/*" ] } ] } ``` Google Cloud Storage uses a Control Plane cloud identity — no credentials are stored. Create a GCS bucket. Set `storage.gcp.bucket` to its name. If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) for your GCP project. Set `storage.gcp.cloudAccountName` to its name. No policy to author — the template grants the workload identity `roles/storage.objectAdmin` scoped to the bucket automatically. Any S3-compatible server uses static access credentials, since these cannot federate with a cloud account. Create your bucket on the server and credentials that can read and write it. Set `storage.minio.bucket`, `storage.minio.accessKey`, and `storage.minio.accessSecret` to match. Set `storage.minio.endpoint` to the S3 API address as `host:port` with **no scheme** (e.g. `my-minio:9000`), and `storage.minio.insecure: true` when the endpoint is plain HTTP. For the `minio` marketplace template deployed in the same GVC, this is `WORKLOAD_NAME.GVC_NAME.cpln.local:9000` with `insecure: true`. Once your backend is ready, install the template using your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} image: grafana/mimir:3.1.3 resources: # monolithic Mimir; memory governs ingestable series count cpu: 1000m memory: 2Gi minCpu: 500m minMemory: 1Gi replicas: 1 # 1 for a single instance; 3 or more forms an HA cluster with 3-way replication storage: type: aws # options: aws, gcp, minio aws: # AWS S3 — via Control Plane cloud identity bucket: my-mimir-bucket # bucket must already exist region: us-east-1 cloudAccountName: my-s3-cloud-account # Control Plane AWS cloud account policyName: my-mimir-s3-policy # custom IAM policy granting bucket access (bare name) gcp: # Google Cloud Storage — via Control Plane cloud identity bucket: my-mimir-bucket # bucket must already exist cloudAccountName: my-gcs-cloud-account # Control Plane GCP cloud account minio: # Any S3-compatible server (MinIO, R2, Wasabi, …) — static keys + endpoint endpoint: my-minio:9000 # host:port, NO scheme insecure: true # true when the endpoint is plain HTTP bucket: my-mimir-bucket # bucket must already exist region: us-east-1 accessKey: my-minio-username accessSecret: my-minio-password multitenancy: enabled: false # when true, every client request must send X-Scope-OrgID: retention: period: "0" # blocks retention, e.g. 30d, 13w, 1y — "0" keeps data forever volumeset: capacity: 20 # GiB — ingester WAL/TSDB and compactor workspace at /data internalAccess: type: same-gvc # options: none, same-gvc, same-org, workload-list workloads: [] # used with workload-list, e.g. //gvc/GVC/workload/NAME ``` ### Resources * `image` — The Grafana Mimir container image. * `resources` — CPU and memory for the Mimir container. Memory governs how many active series you can ingest. ### Storage Backend Set `storage.type` to `aws`, `gcp`, or `minio`, and configure that block. AWS and GCP use a Control Plane cloud identity — no credentials are stored; the workload's identity vends temporary credentials at runtime. See [Prerequisites](#prerequisites) for the per-backend setup steps. | Field | Backends | Description | | ---------------------------- | -------------- | --------------------------------------------------- | | `bucket` | all | Target bucket. Must already exist. | | `region` | `aws`, `minio` | Bucket region. | | `cloudAccountName` | `aws`, `gcp` | Control Plane cloud account granting bucket access. | | `policyName` | `aws` | Bare name of the custom bucket-scoped IAM policy. | | `endpoint` | `minio` | S3 API address as `host:port` — no scheme. | | `insecure` | `minio` | `true` when the endpoint is plain HTTP. | | `accessKey` / `accessSecret` | `minio` | Static credentials with access to the bucket. | One bucket serves the whole deployment; the template namespaces its contents internally. ### Multitenancy * `multitenancy.enabled` — When `true`, **every** request (push and query) must carry an `X-Scope-OrgID: ` header; requests without it are rejected with `401 no org id`. Tenants are implicit — writing with a new tenant ID creates it, and each tenant's series are fully isolated from the others. No provisioning step. `X-Scope-OrgID` identifies a tenant; it does **not** authenticate anyone. Any client that can reach Mimir can send any tenant header. Tenancy is a namespacing mechanism, not a security boundary. ### Retention * `retention.period` — Blocks retention enforced by the compactor, e.g. `30d`, `13w`, `1y`. The default `"0"` keeps data forever. Changing the period applies to existing blocks too. ### Storage Volume * `volumeset.capacity` — Volume size in GiB for the ingester WAL/TSDB and compactor workspace at `/data`. Metric blocks are durably stored in your object bucket, not on the volume. ### Internal Access Mimir is **internal-only by design** — it has no built-in authentication, so this template never exposes a public endpoint. `internalAccess.type` controls which workloads can reach port `8080`: | Type | Description | | --------------- | --------------------------------------------------------------- | | `none` | No internal access allowed | | `same-gvc` | Allow access from all workloads in the same GVC | | `same-org` | Allow access from all workloads in the same organization | | `workload-list` | Allow access only from specific workloads listed in `workloads` | To serve clients outside Control Plane, front Mimir with your own authenticating proxy behind a custom domain — that proxy is yours to build and operate; the template does not provide one. ## Connecting | What | Value | | ------------------------------------------- | ---------------------------------------------------------------- | | Remote-write ingest (from your collectors) | `http://RELEASE_NAME-mimir.GVC_NAME.cpln.local:8080/api/v1/push` | | PromQL / Grafana datasource | `http://RELEASE_NAME-mimir.GVC_NAME.cpln.local:8080/prometheus` | | Tenant header (when `multitenancy.enabled`) | `X-Scope-OrgID: ` on every request | Point your collectors at the ingest endpoint. For Prometheus: ```yaml theme={null} remote_write: - url: http://RELEASE_NAME-mimir.GVC_NAME.cpln.local:8080/api/v1/push # headers: # X-Scope-OrgID: team-a # only when multitenancy.enabled ``` Point a Grafana Prometheus datasource at the PromQL URL. Because Mimir is internal-only, collectors and Grafana must run inside the GVC (or org, per `internalAccess`) to reach it. ## Important Notes * **After scaling `replicas` 1 → 3 on a live install**, metrics written shortly before the scale-up can be intermittently invisible to queries for up to \~12 hours while their blocks age into the store — no data is lost and it resolves on its own. * **Mimir has no built-in authentication, so this template never exposes a public endpoint.** The `X-Scope-OrgID` header identifies a tenant; it does not authenticate anyone. To serve clients outside Control Plane, front Mimir with your own authenticating proxy behind a custom domain. * **With `multitenancy.enabled: true`, every request needs `X-Scope-OrgID`** — pushes and queries without it are rejected; tenants are implicit (no provisioning step). * **Transient "Access Denied" warnings in the first seconds of a fresh boot may appear** — the workload identity's cloud credentials are still being issued; Mimir retries and proceeds on its own. * **Data lives in your bucket** — the volume set only holds the WAL and scratch space. Reinstalling the template against the same bucket resumes with your data; deleting data means emptying the bucket. * **Retention is enforced by the compactor** — changing `retention.period` applies to existing blocks too. * **After uninstall, re-check your bucket** — the terminating replica can re-write a small cluster-seed file (`blocks/__mimir_cluster/`) minutes after teardown; delete it if you are emptying the bucket. ## External References Official Grafana Mimir documentation Remote-write tuning and best practices Ingest, query, and status endpoints reference Source code and releases View the source files, default values, and chart definition # MinIO Source: https://docs.controlplane.com/template-catalog/templates/minio Deploy MinIO on Control Plane using the Template Catalog. Covers configuration, volumes, scaling, and distributed S3-compatible object storage with erasure coding. ## Overview MinIO is a high-performance, S3-compatible object storage system. This template deploys a distributed MinIO cluster with erasure coding across multiple replicas for durability and fault tolerance, backed by persistent storage and an optional autoscaling volume set. ### What Gets Created * **Stateful Workload** — A distributed MinIO cluster with a configurable number of replicas (minimum 4, must be even). * **Volume Set** — Persistent storage per replica with optional autoscaling. * **Secrets** — An opaque startup script secret that configures the distributed server list and handles node initialization, and a dictionary secret storing the admin username and password. * **Identity & Policy** — An identity bound to the workload with `reveal` access to both secrets. This template does not create a GVC. You must deploy it into an existing GVC. ## Installation This template has no external prerequisites. To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} image: minio/minio:RELEASE.2025-09-07T16-13-09Z replicas: 6 # Must be at least 4 and an even number admin: # MinIO admin credentials username: myuser password: mypassword123 resources: # Defaults are set for minimal production usage minCpu: 1 minMemory: 2Gi maxCpu: 2 maxMemory: 4Gi volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) autoscaling: enabled: false maxCapacity: 100 # Maximum capacity in GiB minFreePercentage: 10 # Trigger scaling when free space drops below this percentage scalingFactor: 1.2 # Multiply current capacity by this factor when scaling up internalAccess: type: same-gvc # options: none, same-gvc, same-org, workload-list workloads: # Note: can only be used if type is same-gvc or workload-list #- //gvc/GVC_NAME/workload/WORKLOAD_NAME ``` ### Credentials * `admin.username` — MinIO admin username. **Change before deploying to production.** * `admin.password` — MinIO admin password. **Change before deploying to production.** ### Replicas * `replicas` — Number of MinIO instances in the distributed cluster (minimum 4, must be even). MinIO requires at least 4 replicas to form a distributed cluster with erasure coding. For production workloads, 6 or more replicas are recommended. Depending on your organization's default quotas, you may need to request higher limits. ### Resources * `resources.minCpu` / `resources.minMemory` — Minimum CPU and memory guaranteed to each replica. * `resources.maxCpu` / `resources.maxMemory` — Maximum CPU and memory each replica can use. ### Storage * `volumeset.capacity` — Initial volume size in GiB per replica (minimum 10). * `volumeset.autoscaling.enabled` — Automatically expand each volume as it fills. When enabled: * `maxCapacity` — Maximum volume size in GiB. * `minFreePercentage` — Trigger a scale-up when free space drops below this percentage. * `scalingFactor` — Multiply the current capacity by this factor when scaling up. ### Internal Access * `internalAccess.type` — Controls which workloads can connect to MinIO on ports `9000` and `9001`: | Type | Description | | --------------- | --------------------------------------------------------------- | | `none` | No internal access allowed | | `same-gvc` | Allow access from all workloads in the same GVC | | `same-org` | Allow access from all workloads in the same organization | | `workload-list` | Allow access only from specific workloads listed in `workloads` | Do not set `internalAccess.type` to `none`. MinIO replicas must be able to communicate with each other for distributed mode to function. ### Connecting to MinIO Once deployed, connect to the MinIO S3 API from within the same GVC using the MinIO client: ```bash theme={null} mc alias set minio http://RELEASE_NAME-minio:9000 USERNAME PASSWORD ``` The web console is available on port `9001` at: ```text theme={null} https://RELEASE_NAME-minio.GVC_NAME.cpln.app ``` ## External References Official MinIO documentation MinIO distributed deployment quickstart View the source files, default values, and chart definition # MongoDB Source: https://docs.controlplane.com/template-catalog/templates/mongodb Deploy MongoDB on Control Plane using the Template Catalog. Covers configuration, volumes, scaling, and single-replica document database with optional external access. ## Overview MongoDB is a document-oriented NoSQL database designed for flexible, schema-free data storage at scale. This template deploys a single-replica MongoDB instance with persistent storage and optional external access via a direct load balancer. MongoDB on Control Plane operates as a single-replica deployment. Do not scale up the replica count, as this would result in multiple isolated instances rather than a replicated cluster. ### What Gets Created * **Stateful MongoDB Workload** — A single-replica MongoDB container with configurable resources. * **Volume Set** — Persistent storage for MongoDB data, with optional autoscaling. * **Secret** — A dictionary secret storing the admin username, password, and database name, injected into the container at startup. * **Identity & Policy** — An identity bound to the workload with `reveal` access to the database credentials secret, and cloud storage access when backup is enabled. * **Backup Cron Workload** *(optional)* — A scheduled `mongodump` backup job that writes compressed archives to AWS S3 or GCS. This template does not create a GVC. You must deploy it into an existing GVC. ## Installation This template has no external prerequisites unless backup is enabled. To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} image: mongo:8.2.3 resources: minCpu: 200m minMemory: 256Mi maxCpu: 500m maxMemory: 512Mi config: # initdb credentials and database name username: username password: password database: test volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) autoscaling: enabled: false # Set to true to enable autoscaling maxCapacity: 100 # Maximum capacity in GiB when autoscaling is enabled minFreePercentage: 10 # Minimum free percentage to trigger scaling when autoscaling is enabled scalingFactor: 1.2 # Scaling factor to determine how much to scale up when autoscaling is triggered internalAccess: # Sets the internal firewall scope type: same-gvc # options: none, same-gvc, same-org, workload-list workloads: # Note: can only be used if type is same-gvc or workload-list #- //gvc/GVC_NAME/workload/WORKLOAD_NAME directLoadBalancer: enabled: false backup: enabled: false image: controlplanecorporation/mongo-backup:1.0 # compatible with all MongoDB versions schedule: "0 2 * * *" # daily at 2am UTC resources: cpu: 100m memory: 128Mi provider: aws # Options: aws or gcp aws: bucket: my-backup-bucket region: us-east-1 cloudAccountName: my-backup-cloudaccount policyName: my-backup-policy prefix: mongodb/backups gcp: bucket: my-backup-bucket cloudAccountName: my-backup-cloudaccount prefix: mongodb/backups ``` ### Credentials * `config.username` — MongoDB admin username. **Change before deploying to production.** * `config.password` — MongoDB admin password. **Change before deploying to production.** * `config.database` — Name of the initial database to create (default: `test`). These values are only applied on first startup when the data directory is empty. Updating them after the initial deployment will have no effect on the running database. To change credentials on an existing instance, use MongoDB's native commands (e.g. `db.updateUser()`). ### Resources * `resources.minCpu` / `resources.minMemory` — Minimum CPU and memory guaranteed to the workload. * `resources.maxCpu` / `resources.maxMemory` — Maximum CPU and memory the workload can use. ### Storage * `volumeset.capacity` — Initial volume size in GiB (minimum 10). * `volumeset.autoscaling.enabled` — Automatically expand the volume as it fills. When enabled: * `maxCapacity` — Maximum volume size in GiB. * `minFreePercentage` — Trigger a scale-up when free space drops below this percentage. * `scalingFactor` — Multiply the current capacity by this factor when scaling up. ### Internal Access * `internalAccess.type` — Controls which workloads can connect to MongoDB on port `27017`: | Type | Description | | --------------- | --------------------------------------------------------------- | | `none` | No internal access allowed | | `same-gvc` | Allow access from all workloads in the same GVC | | `same-org` | Allow access from all workloads in the same organization | | `workload-list` | Allow access only from specific workloads listed in `workloads` | ### Direct Load Balancer * `directLoadBalancer.enabled` — When `true`, exposes MongoDB externally on port `27017` via a dedicated load balancer IP. ### Connecting to MongoDB Once deployed, connect to MongoDB from within the same GVC using: ```text theme={null} RELEASE_NAME-mongo.GVC_NAME.cpln.local:27017 ``` ## Backup Backup is disabled by default. When enabled, a cron workload runs `mongodump` on the configured schedule and uploads compressed archives to AWS S3 or GCS. The backup image is compatible with all MongoDB versions. * `backup.enabled` — Enable scheduled backups. * `backup.schedule` — Cron expression for backup frequency (default: daily at 2am UTC). * `backup.provider` — `aws` or `gcp`. * `backup.resources.cpu` / `backup.resources.memory` — Resources for the backup cron container. ### AWS S3 Before enabling backup with `provider: aws`, complete the following in your AWS account: 1. Create an S3 bucket. Set `backup.aws.bucket` to its name and `backup.aws.region` to its region. 2. If you do not have a Cloud Account set up, refer to the docs to [Create a Cloud Account](/guides/create-cloud-account). Set `backup.aws.cloudAccountName` to its name. 3. Create an IAM policy with the following JSON, replacing `YOUR_BUCKET_NAME`: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket", "s3:GetObjectVersion", "s3:DeleteObjectVersion" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET_NAME", "arn:aws:s3:::YOUR_BUCKET_NAME/*" ] } ] } ``` 4. Set `backup.aws.policyName` to the name of the policy created in step 3. 5. Set `backup.aws.prefix` to the folder path where backups will be stored. ### GCS Before enabling backup with `provider: gcp`, complete the following in your GCP account: 1. Create a GCS bucket. Set `backup.gcp.bucket` to its name. 2. If you do not have a Cloud Account set up, refer to the docs to [Create a Cloud Account](/guides/create-cloud-account). Set `backup.gcp.cloudAccountName` to its name. 3. Add the **Storage Admin** role to the GCP service account associated with the Cloud Account. 4. Set `backup.gcp.prefix` to the folder path where backups will be stored. ## Restoring a Backup Run the following from a client with access to the backup bucket. For GCS, replace `aws s3 cp s3://...` with `gsutil cp gs://...`. ```sh theme={null} aws s3 cp s3://BUCKET_NAME/PREFIX/BACKUP_FILE.gz - \ | gunzip \ | mongorestore \ --host=RELEASE_NAME-mongo.GVC_NAME.cpln.local \ --port=27017 \ --username=USERNAME \ --archive ``` ## External References Official MongoDB documentation Source code for the MongoDB backup container image View the source files, default values, and chart definition # MongoDB Cluster Source: https://docs.controlplane.com/template-catalog/templates/mongodb-cluster Deploy a highly available MongoDB replica set cluster on Control Plane using Percona Server for MongoDB. Covers single and multi-location topologies, HAProxy write routing, logical and physical backups, and scaling. ## Overview MongoDB Cluster deploys a highly available MongoDB replica set using Percona Server for MongoDB 8.0. The cluster provides automatic leader election, self-healing replica membership, and seamless failover across one or more locations. An optional HAProxy sidecar provides a stable write endpoint that always routes to the current primary. ### Architecture * **MongoDB Replica Set** — Multi-replica cluster with keyfile authentication and automatic replica set initialization. All replicas participate in elections; only the primary accepts writes. * **HAProxy** *(optional, enabled by default)* — Leader-routing proxy that performs active health checks across all replicas and routes write traffic to the current primary. Provides a stable connection endpoint that survives failover. * **Backup** *(optional)* — Logical backup via `mongodump` or physical backup via Percona Backup for MongoDB (PBM). ### What Gets Created * **Stateful MongoDB Workload** — A multi-replica MongoDB container per configured location with persistent storage per replica. * **Standard HAProxy Proxy Workload** *(optional, enabled by default)* — Routes write traffic to the current primary replica. * **Volume Set** — One persistent volume per MongoDB replica for data storage. * **Identity & Policy** — An identity bound to the workload with `reveal` access to the credential secrets, and cloud storage access when backup is enabled. * **Secrets** — A dictionary secret holding admin credentials injected at startup. * **Cron Backup Workload** *(optional, logical mode)* — A scheduled `mongodump` job that writes compressed archives to AWS S3 or GCS. * **PBM Agent Sidecar + Cron Trigger** *(optional, physical mode)* — A continuously-running `pbm-agent` sidecar on each MongoDB replica, plus a lightweight cron workload that triggers the backup on schedule. * **GVC** — A GVC spanning all configured locations. This template creates its own GVC. Configure `gvc.locations` in `values.yaml` before installing. ## Prerequisites * At least one Control Plane [location](/reference/location) to deploy into. * A unique `replicaSetKey` generated before deploying: `openssl rand -base64 32` * For backup: an AWS or GCP [cloud account](/guides/create-cloud-account) and a storage bucket. ### Installation To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} gvc: name: mongodb-gvc locations: - name: aws-us-east-1 replicas: 3 - name: aws-us-west-2 replicas: 3 - name: aws-eu-central-1 replicas: 3 image: percona/percona-server-mongodb:8.0 multiZone: false resources: cpu: 1 memory: 2Gi mongodb: username: admin password: mypassword database: mydatabase # REQUIRED: Generate with `openssl rand -base64 32` replicaSetKey: "Ol0GnqpqntkcnjprS+Pu/1Ji8fcSEKb8f4zkF5c+dEQ=" volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) autoscaling: enabled: false maxCapacity: 100 minFreePercentage: 10 scalingFactor: 1.2 firewall: internalAllowType: same-gvc # options: same-gvc, same-org, workload-list workloads: [] # - //gvc/GVC_NAME/workload/WORKLOAD_NAME proxy: enabled: true image: haproxy:2.9 resources: cpu: 100m memory: 128Mi minReplicas: 2 maxReplicas: 2 backup: enabled: false mode: logical # options: logical, physical schedule: "0 2 * * *" # daily at 2am UTC provider: aws # options: aws or gcp logical: image: ghcr.io/controlplane-com/backup-images/mongo-backup:8.0 resources: cpu: 100m memory: 128Mi physical: image: percona/percona-backup-mongodb:2.14.0 resources: # pbm-agent sidecar (runs continuously) cpu: 100m memory: 128Mi cron: resources: # backup trigger job (runs briefly on schedule) cpu: 50m memory: 64Mi aws: bucket: my-backup-bucket region: us-east-1 cloudAccountName: my-backup-cloudaccount policyName: my-backup-policy prefix: mongodb-cluster/backups gcp: bucket: my-backup-bucket cloudAccountName: my-backup-cloudaccount prefix: mongodb-cluster/backups ``` ### Locations Configure which Control Plane locations to deploy into and how many replicas to run per location. Each entry in `gvc.locations` creates a set of MongoDB replicas at that location. **Single Location** — all replicas in one location. A minimum of 3 replicas is required for majority quorum: ```yaml theme={null} gvc: locations: - name: aws-us-east-1 replicas: 3 ``` **Multi-Location** — distribute replicas across locations for resilience to a full location outage. For production, use 3 locations with 3 replicas each (9 total). Losing an entire location leaves 6 of 9 replicas online — a clear majority — and the cluster continues serving traffic: ```yaml theme={null} gvc: locations: - name: aws-us-east-1 replicas: 3 - name: aws-us-west-2 replicas: 3 - name: aws-eu-central-1 replicas: 3 ``` Aim for an odd total replica count (3, 5, 7, 9) across all locations to guarantee a clear majority in all split-brain scenarios. ### Multi-Zone Set `multiZone: true` to spread replicas across availability zones within each location, protecting against zone-level failures. Verify your selected location(s) support multi-zone before enabling. ### MongoDB Settings * `mongodb.username` / `mongodb.password` — Admin credentials. Change these before deploying to production. * `mongodb.database` — Default database name created at startup. * `mongodb.replicaSetKey` — Shared secret used to authenticate replica set members to each other. **Must be generated before deploying** with `openssl rand -base64 32`. This value must not be changed after the cluster is initialized — doing so requires a full cluster restart. * `resources.cpu` / `resources.memory` — Resource limits applied to each MongoDB replica. * `image` — MongoDB image. Defaults to `percona/percona-server-mongodb:8.0`. ### HAProxy Proxy HAProxy is enabled by default and strongly recommended. In a MongoDB replica set, only the primary accepts writes — HAProxy provides a stable endpoint that routes all write traffic to the current primary automatically, surviving failover without any client-side changes. * `proxy.enabled` — Enable or disable the proxy workload. * `proxy.minReplicas` / `proxy.maxReplicas` — Number of HAProxy instances. Defaults to 2 for availability. * `proxy.resources` — CPU and memory limits for the proxy workload. HAProxy is required when using logical backups (`backup.mode: logical`). Physical (PBM) backups connect directly to replicas and do not require the proxy. ### Storage * `volumeset.capacity` — Initial volume size in GiB. Minimum is 10. * `volumeset.autoscaling.enabled` — Set to `true` to automatically expand volumes as data grows. * `volumeset.autoscaling.maxCapacity` — Maximum volume size in GiB. * `volumeset.autoscaling.minFreePercentage` — Percentage of free space that triggers a scale-up. * `volumeset.autoscaling.scalingFactor` — Multiplier applied to the current capacity when scaling up. ### Firewall * `firewall.internalAllowType` — Controls which workloads can reach MongoDB: * `same-gvc` — All workloads in the same GVC (default). * `same-org` — All workloads in the org. * `workload-list` — Only workloads listed in `firewall.workloads`. * `firewall.workloads` — List of specific workload paths when using `workload-list`. ## Connecting to MongoDB Connect using the appropriate endpoint depending on your setup: | Setup | Hostname | Port | | ------------------------------------------ | -------------------------------------------------------------- | ------- | | Via HAProxy (writes + reads) | `{release-name}-mongo-proxy.{gvc}.cpln.local` | `27017` | | Direct per-replica (read-only or internal) | `replica-{N}.{release-name}-mongo.{location}.{gvc}.cpln.local` | `27017` | Example connection string via proxy: ``` mongodb://admin:mypassword@{release-name}-mongo-proxy.{gvc}.cpln.local:27017/mydatabase?authSource=admin ``` To offload reads from the primary, add `readPreference=secondaryPreferred` to your connection string and connect directly to a replica: ``` mongodb://admin:mypassword@replica-0.{release-name}-mongo.{location}.{gvc}.cpln.local:27017/mydatabase?authSource=admin&readPreference=secondaryPreferred ``` Secondary reads may be slightly stale due to replication lag. Use `readPreference=primary` (the default) when read-your-own-writes consistency is required. Configure `maxPoolSize` in your MongoDB driver to prevent connection exhaustion. A per-app-replica pool of 10–50 connections is a reasonable starting point for most workloads. ## Backing Up Two backup modes are available: | Mode | Mechanism | Best For | | ---------- | -------------------------------- | -------------------------------------------------------------------------- | | `logical` | `mongodump` (BSON archives) | Smaller databases, cross-version migrations, selective collection restores | | `physical` | Percona Backup for MongoDB (PBM) | Large databases — all replicas participate and upload concurrently | Set `backup.enabled: true`, choose a `mode`, set `backup.provider`, and fill in the corresponding provider block. The backup job region determines where the backup cron workload runs. For AWS, the Control Plane location is automatically derived from `backup.aws.region` (e.g., `us-east-1` → `aws-us-east-1`). For GCP, the job runs in the first location listed in `gvc.locations`. ### AWS S3 Complete the following in your AWS account before installing: Create an S3 bucket and set `backup.aws.bucket` and `backup.aws.region` to match. If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account). Set `backup.aws.cloudAccountName` to its name. Create an IAM policy with the following JSON (replace `YOUR_BUCKET_NAME`) and set `backup.aws.policyName` to its name: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket", "s3:GetObjectVersion", "s3:DeleteObjectVersion" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET_NAME", "arn:aws:s3:::YOUR_BUCKET_NAME/*" ] } ] } ``` ### GCS Complete the following in your GCP account before installing: Create a GCS bucket and set `backup.gcp.bucket` to its name. If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account). Set `backup.gcp.cloudAccountName` to its name. You must add the `Storage Admin` role to the GCP service account created for the Cloud Account. ## Restoring a Backup ### Logical Restore Run the following from a client with network access to the cluster and access to the bucket. Connect to the **proxy** workload so writes land on the current primary. **AWS S3:** ```sh theme={null} mongorestore \ --uri="mongodb://USERNAME:PASSWORD@{release-name}-mongo-proxy.{gvc}.cpln.local:27017/?authSource=admin" \ --gzip \ --archive=<(aws s3 cp s3://BUCKET_NAME/PREFIX/BACKUP_FILE.gz -) ``` **GCS:** ```sh theme={null} mongorestore \ --uri="mongodb://USERNAME:PASSWORD@{release-name}-mongo-proxy.{gvc}.cpln.local:27017/?authSource=admin" \ --gzip \ --archive=<(gsutil cp gs://BUCKET_NAME/PREFIX/BACKUP_FILE.gz -) ``` ### Physical Restore (PBM) Physical restores require stopping the MongoDB workload and restoring data files directly. All replicas must participate. ```sh theme={null} cpln workload exec {release-name}-mongo --gvc {gvc} --location {location} --replica 0 --container pbm-agent -- /bin/sh ``` ```sh theme={null} MONGO_URI="mongodb://${MONGO_INITDB_ROOT_USERNAME}:${MONGO_INITDB_ROOT_PASSWORD}@localhost:27017/admin?replicaSet=rs0&authSource=admin&authMechanism=SCRAM-SHA-256" pbm list --mongodb-uri="${MONGO_URI}" ``` Stop the workload via the Control Plane console or CLI before restoring. ```sh theme={null} pbm restore BACKUP_NAME --mongodb-uri="${MONGO_URI}" ``` Restart the MongoDB workload. All replicas will resync automatically. ## Scaling ### Scaling Up Increase `replicas` for a location in `values.yaml` and apply the template upgrade. New replicas connect to the primary via seed nodes and self-register into the replica set automatically. ### Scaling Down Scaling down requires manually removing departing replicas from the replica set configuration first. If stale members are not removed, the replica set config will reference non-existent hosts, which can affect elections and quorum calculations. Before reducing the replica count, connect to the primary and remove each departing replica: ```js theme={null} rs.remove("replica-{N}.{release-name}-mongo.{location}.{gvc}.cpln.local:27017") ``` After all departing replicas are removed from the config, apply the template upgrade to reduce the count. ## Important Notes * **Minimum replicas**: Use at least 3 replicas per location for HA. A 2-replica cluster cannot maintain quorum if one replica fails. * **Replica set key**: The `replicaSetKey` must be generated before deployment and must not be changed after the cluster is initialized. Changing it requires a full cluster restart. * **Odd total replica count**: Aim for an odd total number of replicas across all locations (3, 5, 7, 9) to guarantee a clear majority in all failover scenarios. * **Read from secondaries**: To offload reads from the primary, use `readPreference=secondaryPreferred` in your connection string. Secondary reads may be slightly stale due to replication lag. * **Connection pooling**: Configure `maxPoolSize` in your MongoDB driver to prevent connection exhaustion. A per-app-replica pool of 10–50 is a reasonable starting point. ## External References Official documentation for Percona Server for MongoDB Documentation for PBM physical backup and restore MongoDB replica set concepts and administration Connection string URI format and options # MySQL Source: https://docs.controlplane.com/template-catalog/templates/mysql Deploy MySQL on Control Plane using the Template Catalog. Covers configuration, volumes, scaling, and single-replica setup with optional phpMyAdmin and S3/GCS backups. ## Overview MySQL is a widely used open-source relational database management system. This template deploys a single-replica MySQL instance with persistent storage, an optional phpMyAdmin web interface, and optional scheduled backups to AWS S3 or GCS. MySQL on Control Plane operates as a single-replica deployment. Do not scale up the replica count, as this would result in multiple isolated instances rather than a replicated cluster. ### What Gets Created * **Stateful Workload** — A single-replica MySQL database container with configurable resources. * **Volume Set** — Persistent storage for MySQL data, with optional autoscaling. * **Secret** — A dictionary secret storing the database name, root password, and user credentials. When backup is enabled, the secret also includes backup storage configuration. * **Identity & Policy** — An identity bound to the workload with `reveal` access to the database credentials secret. When backup is enabled, the identity also grants the backup cron job access to the configured cloud storage. * **Backup Cron Job** (optional) — A scheduled job that dumps the MySQL database to an S3 or GCS bucket. Enabled when `backup.enabled: true`. * **phpMyAdmin Workload** (optional) — A web-based database management interface. Enabled when `enablePhpMyAdmin: true`. This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites Prerequisites are only required if you plan to enable MySQL backups (`backup.enabled: true`). The backup feature requires MySQL 9+. Skip this section if backups are not needed. ### AWS S3 1. Create an S3 bucket. Set `backup.aws.bucket` and `backup.aws.region` in your values file. 2. If you do not have a Control Plane Cloud Account set up, follow the [Create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) guide. Set `backup.aws.cloudAccountName` to the name of your Cloud Account. 3. Create an IAM policy with the following JSON, replacing `YOUR_BUCKET_NAME`: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket", "s3:GetObjectVersion", "s3:DeleteObjectVersion" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET_NAME", "arn:aws:s3:::YOUR_BUCKET_NAME/*" ] } ] } ``` 4. Set `backup.aws.policyName` to the name of the policy created in step 3. ### GCS 1. Create a GCS bucket. Set `backup.gcp.bucket` in your values file. 2. If you do not have a Control Plane Cloud Account set up, follow the [Create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) guide. Set `backup.gcp.cloudAccountName` to the name of your Cloud Account. 3. Add the **Storage Admin** role to the GCP service account associated with the Cloud Account. ## Installation To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} image: mysql:9 # versions before mysql:9 are compatible but do not support backup feature resources: minCpu: 100m maxCpu: 400m minMemory: 128Mi maxMemory: 512Mi timeoutSeconds: 15 config: db: test rootPassword: root-password user: username password: password internalAccess: type: same-gvc # options: none, same-gvc, same-org, workload-list workloads: # Note: can only be used if type is same-gvc or workload-list #- //gvc/GVC_NAME/workload/WORKLOAD_NAME volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) autoscaling: enabled: false maxCapacity: 100 # Maximum capacity in GiB minFreePercentage: 10 # Trigger scaling when free space drops below this percentage scalingFactor: 1.2 # Multiply current capacity by this factor when scaling up enablePhpMyAdmin: false backup: # compatible with MySQL 9+ enabled: true image: controlplanecorporation/mysql-backup:9.1 # tag 9.1 = MySQL 9 compatible schedule: "0 2 * * *" # daily at 2am UTC resources: cpu: 100m memory: 128Mi provider: aws # Options: aws or gcp aws: bucket: mysql-backup-bucket region: us-east-1 cloudAccountName: mysql-backup-cloudaccount policyName: mysql-backup-policy prefix: mysql/backups # Folder path within the bucket gcp: bucket: mysql-backup-bucket cloudAccountName: mysql-backup-cloudaccount prefix: mysql/backups ``` ### Credentials * `config.db` — Name of the database created on startup. * `config.rootPassword` — Password for the MySQL root user. **Change before deploying to production.** * `config.user` — Name of the non-root database user created on startup. * `config.password` — Password for the non-root user. **Change before deploying to production.** These values are only applied on first startup when the data directory is empty. Updating them after the initial deployment will have no effect on the running database. To change credentials or the database name on an existing instance, use MySQL's native commands (e.g. `ALTER USER`, `RENAME DATABASE`). ### Resources * `resources.minCpu` / `resources.minMemory` — Minimum CPU and memory guaranteed to the workload. * `resources.maxCpu` / `resources.maxMemory` — Maximum CPU and memory the workload can use. * `timeoutSeconds` — Workload timeout in seconds. ### Storage * `volumeset.capacity` — Initial volume size in GiB (minimum 10). * `volumeset.autoscaling.enabled` — Automatically expand the volume as it fills. When enabled: * `maxCapacity` — Maximum volume size in GiB. * `minFreePercentage` — Trigger a scale-up when free space drops below this percentage. * `scalingFactor` — Multiply the current capacity by this factor when scaling up. ### Internal Access * `internalAccess.type` — Controls which workloads can connect to MySQL on port `3306`: | Type | Description | | --------------- | --------------------------------------------------------------- | | `none` | No internal access allowed | | `same-gvc` | Allow access from all workloads in the same GVC | | `same-org` | Allow access from all workloads in the same organization | | `workload-list` | Allow access only from specific workloads listed in `workloads` | ### phpMyAdmin * `enablePhpMyAdmin` — When `true`, deploys a phpMyAdmin workload for browser-based database management. ### Backup Set `backup.enabled: true` to enable scheduled database dumps to object storage. The backup feature requires MySQL 9+. Set `backup.provider` to `aws` or `gcp` and fill in the corresponding section. The `prefix` field controls the folder path within the bucket where backups are stored. ### Restoring a Backup To restore from a backup, run the following from a client with access to the bucket: **AWS S3** ```bash theme={null} export MYSQL_PWD="ROOT_PASSWORD" aws s3 cp "s3://BUCKET_NAME/PREFIX/BACKUP_FILE.sql.gz" - \ | gunzip \ | mysql \ --host=WORKLOAD_NAME \ --port=3306 \ --user=root \ DATABASE_NAME unset MYSQL_PWD ``` **GCS** ```bash theme={null} export MYSQL_PWD="ROOT_PASSWORD" gsutil cp "gs://BUCKET_NAME/PREFIX/BACKUP_FILE.sql.gz" - \ | gunzip \ | mysql \ --host=WORKLOAD_NAME \ --port=3306 \ --user=root \ DATABASE_NAME unset MYSQL_PWD ``` ### Connecting to MySQL Once deployed, connect to the database from within the same GVC using: ```text theme={null} RELEASE_NAME-mysql.GVC_NAME.cpln.local:3306 ``` ## External References Official MySQL documentation phpMyAdmin user documentation Create a Control Plane Cloud Account for backup storage access Source code for the MySQL backup container image View the source files, default values, and chart definition # n8n Source: https://docs.controlplane.com/template-catalog/templates/n8n Deploy n8n on Control Plane using the Template Catalog. Workflow automation backed by a highly available PostgreSQL cluster. Covers the encryption-key prerequisite, database modes, webhooks on the canonical endpoint, and database backups. ## Overview n8n is a workflow automation platform (fair-code, distributed under the [Sustainable Use License](https://docs.n8n.io/sustainable-use-license/)). This template deploys an n8n instance backed by a highly available PostgreSQL cluster by default. The editor, REST API, and webhook endpoints are served on one public HTTPS endpoint, and the instance owner account is pre-provisioned at install — there is never an unauthenticated setup page. ### Architecture * **n8n** — A single-replica stateful workload serving the editor, REST API, and webhooks on port `5678`. Public URLs are derived from the canonical endpoint at start, so webhook URLs work out of the box. * **PostgreSQL (HA, default)** — The [postgres-highly-available](/template-catalog/templates/postgres-highly-available) template as a subchart: 3× Patroni PostgreSQL, 3× etcd, and a HAProxy leader-routing endpoint n8n connects through. * **PostgreSQL (dev/lightweight, optional)** — The single-instance [postgres](/template-catalog/templates/postgres) template instead, for lighter non-HA deployments. ### What Gets Created * **Stateful n8n Workload** — Single replica serving the editor, API, and webhooks on port `5678`. * **Database Workloads** — HA mode: a stateful Patroni PostgreSQL workload, a stateful etcd workload, and a standard HAProxy leader-routing workload. Single mode: one stateful PostgreSQL workload. * **Volume Sets** — 10 GiB persistent storage for n8n instance config and binary execution data (`/home/node/.n8n`), plus the database subchart's own volume sets. * **Secrets** — Owner bootstrap credentials (bcrypt-hashed), a start script that derives public URLs at runtime, and the database credentials from the subchart. * **Identity & Policy** — A least-privilege policy granting the n8n identity `reveal` on exactly the secrets it uses, including your pre-created encryption-key secret. * **Cron Backup Workload** *(optional)* — When database backups are enabled. This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites n8n encrypts every credential it stores with a key it reads from an [opaque secret](/guides/create-secret/opaque) that you create **before** installing. The key is never passed through values. Generate a long random string, for example: ```bash theme={null} openssl rand -hex 24 ``` Create an [opaque secret](/guides/create-secret/opaque) in your org with encoding `plain` whose payload is the generated key. Set its name in `encryptionKey.secretName`. Store a copy of the key somewhere safe, outside Control Plane. Losing the encryption key makes every credential n8n has stored permanently undecryptable, and the key must never change after first boot — n8n fails to start on a key mismatch. Back it up before installing. For optional database backups, you also need a bucket and access setup for one of the supported providers — see [Backing Up](#backing-up). Once your encryption-key secret exists, install the template using your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Choosing a Database Mode Exactly one of the two database modes must be enabled — the chart enforces this at render and fails the install with a clear message otherwise. | | `postgresHA` (default) | `postgres` | | ----------------- | --------------------------------------------------------------- | -------------------------------------- | | What runs | 3× Patroni PostgreSQL, 3× etcd, HAProxy leader endpoint | One single-replica PostgreSQL workload | | Database failover | Automatic (Patroni leader election) | None | | Footprint | 8 replicas across 3 workloads (3× Patroni, 3× etcd, 2× HAProxy) | 1 workload | | Best for | Production | Development and lightweight installs | ## Configuration The default `values.yaml` for this template: ```yaml theme={null} image: n8nio/n8n:2.29.8 resources: cpu: 1000m memory: 1Gi minCpu: 250m minMemory: 512Mi encryptionKey: secretName: my-n8n-encryption-key # name of your pre-created opaque secret (see Prerequisites) owner: # instance owner, created automatically on first boot email: admin@example.com # owner login email firstName: Instance lastName: Owner password: change-me-n8n-owner # change before installing timezone: UTC # IANA timezone for Schedule triggers and $now volumeset: capacity: 10 # GiB (minimum 10) — instance config and binary execution data publicAccess: enabled: true # editor + webhooks on the canonical *.cpln.app HTTPS endpoint internalAccess: # internal firewall scope (in-GVC webhook callers) type: same-gvc # options: none, same-gvc, same-org, workload-list workloads: [] # used with workload-list, e.g. //gvc/GVC_NAME/workload/WORKLOAD_NAME postgresHA: # default: highly available PostgreSQL enabled: true postgres: username: n8n password: change-me-n8n-db-password # change before installing database: n8n replicas: 3 volumeset: capacity: 10 # initial capacity in GiB per replica (minimum is 10) backup: enabled: false # optional — see Backing Up mode: logical # logical or wal-g resources: cpu: 100m memory: 128Mi logical: image: ghcr.io/controlplane-com/backup-images/postgres-backup:17.1.0 schedule: "0 2 * * *" walg: intervalSeconds: 21600 provider: aws # options: aws, gcp, minio aws: bucket: n8n-pg-backup-bucket region: us-east-1 cloudAccountName: my-s3-cloud-account policyName: n8n-pg-backup-policy prefix: postgres/backups gcp: bucket: n8n-pg-backup-bucket cloudAccountName: my-gcs-cloud-account prefix: postgres/backups minio: endpoint: http://my-minio-workload:9000 bucket: n8n-pg-backup-bucket accessKey: my-minio-username secretKey: my-minio-password prefix: postgres/backups postgres: # dev/lightweight: single-instance PostgreSQL (disable postgresHA first) enabled: false config: username: n8n password: change-me-n8n-db-password # change before installing database: n8n volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) backup: enabled: false # optional — see Backing Up image: ghcr.io/controlplane-com/backup-images/postgres-backup:18.1.0 schedule: "0 2 * * *" resources: cpu: 100m memory: 128Mi provider: aws # options: aws, gcp, minio aws: bucket: n8n-pg-backup-bucket region: us-east-1 cloudAccountName: my-s3-cloud-account policyName: n8n-pg-backup-policy prefix: postgres/backups gcp: bucket: n8n-pg-backup-bucket cloudAccountName: my-gcs-cloud-account prefix: postgres/backups minio: endpoint: http://my-minio-workload:9000 bucket: n8n-pg-backup-bucket accessKey: my-minio-username secretKey: my-minio-password prefix: postgres/backups ``` ### n8n Instance * `image` — The n8n container image. * `resources` — CPU and memory for the n8n container. * `encryptionKey.secretName` — Name of your pre-created opaque secret holding the credential-encryption key. See [Prerequisites](#prerequisites). * `owner.*` — The instance owner account, created automatically on first boot. Because the owner is managed through the environment, there is no unauthenticated setup page at any point. **Change `owner.password` before installing.** To rotate it later, change the value and run a Helm upgrade — the new password takes effect on the restart. * `timezone` — IANA timezone applied to Schedule triggers and `$now` expressions (e.g. `America/Chicago`). * `volumeset.capacity` — Volume size in GiB (minimum 10) for instance config and binary execution data. ### Access * `publicAccess.enabled` — Serve the editor, API, and webhooks on the canonical `*.cpln.app` HTTPS endpoint. Set to `false` for an internal-only instance (external requests are blocked at the edge; in-GVC callers still reach it per `internalAccess`). * `internalAccess.type` — Internal firewall scope of the n8n workload: | Type | Description | | --------------- | ---------------------------------------------------------------------- | | `none` | No internal access. | | `same-gvc` | Allow access from all workloads in the same GVC (default). | | `same-org` | Allow access from all workloads in the same organization. | | `workload-list` | Allow access only from workloads listed in `internalAccess.workloads`. | ### Database Enable exactly one of `postgresHA` (production, default) or `postgres` (dev/lightweight) — see [Choosing a Database Mode](#choosing-a-database-mode). In both modes, **change the database password before installing** (`postgresHA.postgres.password` / `postgres.config.password`). n8n is wired to the active database automatically — the HAProxy leader endpoint in HA mode, or the single instance directly in dev mode. **Template version `1.0.0` did not compact the etcd cluster inside the bundled highly available database**, so etcd's backend grows with time alone and goes read-only once it reaches its 2 GiB quota — after roughly 110 days — taking PostgreSQL failover with it. Only installs running the HA database are affected (`postgresHA.enabled`, the default here); see [etcd History Compaction](/template-catalog/templates/postgres-highly-available#etcd-history-compaction) for the mechanism and the symptoms. Upgrade to `1.0.1` or later to turn compaction on: that stops further growth but cannot shrink a backend that has already grown, and a cluster that has already raised a `NOSPACE` alarm needs operator recovery rather than an upgrade. ## Connecting | What | Value | | ---------------------------------- | ---------------------------------------------------------------------------------------------------------- | | Editor / API (public) | `https://.cpln.app` — `status.canonicalEndpoint` of `{release}-n8n` | | Production webhooks | `https://.cpln.app/webhook/` | | Test webhooks | `https://.cpln.app/webhook-test/` | | Internal (same GVC) | `http://{release}-n8n.{gvc}.cpln.local:5678` | | Login | `owner.email` / `owner.password` | | PostgreSQL (internal, HA mode) | `{release}-postgres-ha-proxy.{gvc}.cpln.local:5432`, credentials in the `{release}-postgres-config` secret | | PostgreSQL (internal, single mode) | `{release}-postgres.{gvc}.cpln.local:5432`, credentials in the `{release}-pg-config` secret | ### Webhooks Webhook URLs are derived from the canonical endpoint at startup, so the URLs shown in the editor are the ones external callers use — no extra configuration needed. Synchronous webhook responses must finish within 30 seconds — the platform edge times out longer responses with a `504`, although the workflow itself still runs to completion. For long-running workflows, set the Webhook node to respond immediately (or add a Respond to Webhook node early) so the caller gets its response right away while the workflow keeps running. ## Backing Up Database backups are optional and disabled by default. Enable them with `postgresHA.backup.enabled` or `postgres.backup.enabled` (matching your database mode), and complete the storage setup for your provider **before** installing. The values below are shown under `backup.*` — set them within the enabled database block. Create an S3 bucket. Set `backup.aws.bucket` and `backup.aws.region` to match. If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) for your AWS account. Set `backup.aws.cloudAccountName` to its name. Create an AWS IAM policy with the JSON below (replace `YOUR_BUCKET`), then set `backup.aws.policyName` to the policy's name: ```json theme={null} { "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Action": [ "s3:ListBucket", "s3:GetBucketLocation", "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:AbortMultipartUpload" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET", "arn:aws:s3:::YOUR_BUCKET/*" ] }] } ``` Create a GCS bucket. Set `backup.gcp.bucket` to its name. If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) for your GCP project. Set `backup.gcp.cloudAccountName` to its name — the backup identity is granted access to the bucket keylessly (no stored credentials). Create your bucket on the server. Set `backup.minio.bucket` to its name. Set `backup.minio.endpoint` to the S3 API address including port. For the `minio` marketplace template deployed in the same GVC, this is `http://WORKLOAD_NAME:9000`. Set `backup.minio.accessKey` and `backup.minio.secretKey` to credentials with access to the bucket. In HA mode, `backup.mode` selects `logical` (scheduled `pg_dump` via a cron workload) or `wal-g` (continuous WAL archiving). The single-instance mode takes scheduled logical dumps. ## Important Notes * **Back up the encryption-key secret** — losing it permanently bricks every credential n8n has stored; never change it after first boot (n8n fails to start on a key mismatch). * **Change `owner.password` and the database password** (`postgresHA.postgres.password` / `postgres.config.password`) before installing. * **The n8n main instance is single-replica by upstream design** — the default HA PostgreSQL backend removes the database as a failure point. * **Upgrades restart the single replica** — expect roughly a minute of editor/webhook downtime per Helm upgrade. * **Synchronous webhook responses must finish within 30 seconds** — see [Webhooks](#webhooks). * **Uninstall deletes the database and n8n volume sets** — all workflows, credentials, and execution data. Enable backups if the data matters. * **n8n is fair-code under the Sustainable Use License** — free to self-host, but not OSI open source. ## External References Official n8n documentation n8n deployment environment variables reference Webhook and endpoint configuration reference Owner account and user management guide The fair-code license n8n is distributed under View the source files, default values, and chart definition # NATS Source: https://docs.controlplane.com/template-catalog/templates/nats Deploy NATS on Control Plane using the Template Catalog. Covers configuration, multi-region super cluster, WebSocket support, gateway routing, and optional JetStream persistent storage. ## Overview NATS is an open-source, high-performance, lightweight messaging system optimized for cloud-native architectures. It supports pub/sub, queueing, and request/reply patterns. This template deploys a NATS super cluster using the official NATS image across multiple regions, with each location running independent replicas. JetStream can be enabled for persistent streams, durable consumers, K-V store, and object store — each replica gets its own dedicated volume. By default it exposes a WebSocket interface on port 443 via Control Plane's TLS termination. ### What Gets Created * **GVC** — A dedicated GVC spanning the configured locations. * **Stateful NATS Workload** — A NATS super cluster using the official `nats` image, with per-location replica counts, exposing the client port (4222), cluster port (6222), gateway port (7222), and optionally the WebSocket port (8080). * **Volume Set** *(optional, when JetStream is enabled)* — Persistent storage for JetStream data. Each replica gets its own volume, with optional autoscaling. * **Secrets** — An opaque configuration secret containing the full NATS server config, and an opaque extra data secret used for the `nats_extra_config` block. * **Identity & Policy** — An identity bound to the workload with `reveal` access to both secrets. This template creates a GVC. If you plan to deploy multiple instances, you must assign a unique GVC name for each deployment. ## Installation This template has no external prerequisites. To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} gvc: name: nats-gvc locations: - name: aws-us-east-1 replicas: 2 - name: aws-us-west-2 replicas: 2 - name: aws-eu-central-1 replicas: 2 resources: cpu: 100m memory: 256Mi image: nats:2.11.6-alpine # official NATS image tag — bump to upgrade NATS version nats_defaults: port: 4222 cluster: listen: "0.0.0.0:6222" port: 6222 noAdvertise: false gateway: listen: "0.0.0.0:7222" port: 7222 websocket: enabled: true port: 8080 compression: false noTls: true # Control Plane handles TLS termination # allowedOrigins: [] # Uncomment and add origins if needed # advertise: "ws://example.com:8080" # Uncomment if different from default allowCIDR: - 0.0.0.0/0 # Allow all traffic. Change to specific CIDRs to limit exposure. # Only applicable to the WebSocket port. Control Plane exposes it on port 443. jetstream: enabled: false # set to true to enable persistent JetStream streaming and storage volumeset: capacity: 10 # initial capacity in GiB per replica (minimum is 10) autoscaling: enabled: false maxCapacity: 100 minFreePercentage: 10 scalingFactor: 1.2 # Add any additional valid NATS configuration here — appended to the server config at startup nats_extra_config: "" internalAccess: # Sets the internal firewall scope type: same-gvc # options: none, same-gvc, same-org, workload-list workloads: # Note: can only be used if type is same-gvc or workload-list #- //gvc/GVC_NAME/workload/WORKLOAD_NAME ``` ### Image * `image` — The NATS container image to use. Defaults to the official `nats:2.11.6-alpine` image. Bump the tag to upgrade the NATS version. ### GVC and Locations * `gvc.name` — Name of the GVC to create. Must be unique per deployment if running multiple instances. * `gvc.locations` — List of locations where NATS replicas will run. Each entry requires: * `name` — The location identifier (e.g. `aws-us-east-1`). * `replicas` — Number of NATS replicas in that location. Set to `0` to suspend a location without removing it. ### Resources * `resources.cpu` / `resources.memory` — CPU and memory allocated to each NATS replica. ### JetStream * `jetstream.enabled` — When `true`, enables NATS's built-in persistence layer, adding durable streams, consumers, K-V store, and object store on top of the core pub/sub model. Without it, NATS is purely in-memory. When JetStream is enabled, each replica gets its own dedicated persistent volume (configured via `volumeset`). Streams default to `num_replicas: 1`, meaning stream data lives on a single server. For streams that must survive a location outage, set `num_replicas` to 2 or higher in your application — this is configured per-stream, not in this template. ### Volume Set The `volumeset` block applies only when `jetstream.enabled: true`. * `volumeset.capacity` — Initial volume size in GiB per replica (minimum 10). * `volumeset.autoscaling.enabled` — Automatically expand the volume as it fills. When enabled: * `maxCapacity` — Maximum volume size in GiB. * `minFreePercentage` — Trigger a scale-up when free space drops below this percentage. * `scalingFactor` — Multiply the current capacity by this factor when scaling up. ### WebSocket * `nats_defaults.websocket.enabled` — When `true`, exposes a WebSocket endpoint on the configured port. Control Plane handles TLS termination and serves it externally on port 443. * `nats_defaults.websocket.port` — Internal WebSocket port (default: `8080`). ### External Access * `allowCIDR` — CIDR ranges allowed to reach the WebSocket port from the internet. Defaults to `0.0.0.0/0` (open to all). Restrict this to limit exposure. `allowCIDR` only applies to the WebSocket port. The NATS client port (4222), cluster port (6222), and gateway port (7222) are only accessible internally. ### Internal Access * `internalAccess.type` — Controls which workloads can reach NATS on the client (4222), cluster (6222), and gateway (7222) ports: | Type | Description | | --------------- | --------------------------------------------------------------- | | `none` | No internal access allowed | | `same-gvc` | Allow access from all workloads in the same GVC | | `same-org` | Allow access from all workloads in the same organization | | `workload-list` | Allow access only from specific workloads listed in `workloads` | Do not set `internalAccess.type` to `none`. NATS replicas must be able to communicate with each other across the cluster and gateway ports for the super cluster to function. ### Extra NATS Configuration * `nats_extra_config` — Any additional valid NATS server configuration appended to the server config at startup. Use this to set `max_payload`, configure accounts, or add other NATS server options: ```yaml theme={null} nats_extra_config: | max_payload: 8MB ``` ### Connecting to NATS **Internally** (from workloads in the same GVC), connect on the standard NATS client port: ```text theme={null} nats://RELEASE_NAME-nats.GVC_NAME.cpln.local:4222 ``` **Externally** via WebSocket, connect through the Control Plane endpoint on port 443: ```text theme={null} wss://RELEASE_NAME-nats.GVC_NAME.cpln.app ``` ## External References Official NATS documentation NATS JetStream persistence and streaming View the source files, default values, and chart definition # Nginx Source: https://docs.controlplane.com/template-catalog/templates/nginx Deploy Nginx on Control Plane using the Template Catalog. Covers configuration, scaling, and reverse proxy setup with path-based routing rules for GVC workloads. ## Overview Nginx is a high-performance web server and reverse proxy. This template deploys Nginx as a reverse proxy that routes incoming traffic to workloads in your GVC based on configurable path-based location rules. ### What Gets Created * **Workload** — A serverless Nginx reverse proxy with configurable routing rules and autoscaling. * **Secret** — An opaque secret containing the rendered Nginx configuration file, mounted into the container at startup. * **Identity & Policy** — An identity bound to the workload with `reveal` access to the configuration secret. * **Example Backend Workload** (optional) — A sample backend workload used to verify the proxy is working. Enabled when `enableExample: true`. Disable this before using in production. This template does not create a GVC. You must deploy it into an existing GVC. ## Installation This template has no external prerequisites. To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} proxyWorkload: image: nginx:latest port: 80 capacityAI: false timeoutSeconds: 5 resources: cpu: 100m memory: 128Mi autoscaling: minScale: 1 maxScale: 1 maxConcurrency: 1000 enableExample: true # set to false when routing to your own workloads locations: [] # add your own proxy targets here # - path: / # workload: my-workload # port: 8080 # regexModifier: "" ``` ### Proxy Workload * `proxyWorkload.image` — Nginx container image. * `proxyWorkload.port` — Port Nginx listens on (default: `80`). * `proxyWorkload.capacityAI` — Enable CapacityAI autoscaling on the proxy workload. * `proxyWorkload.timeoutSeconds` — Proxy connection timeout in seconds. ### Resources * `resources.cpu` / `resources.memory` — CPU and memory allocated to each Nginx replica. ### Autoscaling * `autoscaling.minScale` / `autoscaling.maxScale` — Minimum and maximum replica count. * `autoscaling.maxConcurrency` — Maximum number of concurrent connections per replica. ### Routing * `locations` — Array of path-based routing rules. Each entry forwards matching requests to an internal workload: | Field | Description | | --------------- | ------------------------------------------------- | | `path` | URL path prefix to match (e.g. `/`, `/api`) | | `workload` | Name of the target workload in the same GVC | | `port` | Port on the target workload to forward traffic to | | `regexModifier` | Optional regex modifier for path matching | Example: ```yaml theme={null} locations: - path: / workload: my-app port: 8080 - path: /api workload: my-api port: 3000 ``` ### Built-in Endpoints The following endpoints are always active regardless of your `locations` configuration: | Endpoint | Behavior | | --------- | ------------------------------------------------------------------------------------ | | `/health` | Returns `ok` — used for liveness and readiness probes | | `/fail` | Returns `500` — any 5XX errors from upstream workloads are returned as this response | ### Example Backend * `enableExample` — When `true`, deploys a sample helloworld backend workload and routes all traffic to it. Set to `false` when configuring your own `locations`. ### Accessing the Proxy Once deployed, the Nginx proxy is available at: ```text theme={null} https://RELEASE_NAME-GVC_NAME.cpln.app ``` ## External References Official Nginx documentation View the source files, default values, and chart definition # NocoDB Source: https://docs.controlplane.com/template-catalog/templates/nocodb Deploy NocoDB on Control Plane using the Template Catalog. Self-hosted no-code database and smart spreadsheet with grid, kanban, gallery, and calendar views, forms, automations, and a REST API, backed by PostgreSQL, Redis, and local or S3 attachment storage. ## Overview NocoDB is a no-code database and smart spreadsheet — a self-hosted alternative to Airtable, Baserow, and Teable — that turns a database into grid, kanban, gallery, and calendar views with forms, automations, and a REST API. This template deploys NocoDB Community Edition on port `8080`, backed by a PostgreSQL store for all metadata, a bundled Redis, and attachments kept either on a persistent volume or in an S3 bucket you own. ### Architecture * **NocoDB server** — A stateful workload running the single official image, which serves the web UI, the REST and GraphQL APIs, and live updates on port `8080`. It applies its meta-database migrations automatically on boot and scales horizontally through `nocodb.replicas`. * **PostgreSQL (single instance, default)** — The [postgres](/template-catalog/templates/postgres) template as a subchart, running PostgreSQL 18. It holds every base, table, view, user, and automation. * **PostgreSQL (highly available, optional)** — The [postgres-highly-available](/template-catalog/templates/postgres-highly-available) template instead, running PostgreSQL 17: 3× Patroni PostgreSQL with automatic failover, 3× etcd, and an HAProxy leader endpoint that NocoDB connects through. * **Redis** — A bundled single-node Redis with AOF persistence and a `noeviction` memory policy, deployed by this chart. It carries the job and event pub/sub, the metadata cache, and the rate limiter. NocoDB does not run without it, and all three of those roles are what make more than one replica coherent. * **Attachment storage** — Either a persistent volume mounted at `/usr/app/data` (default) or an S3 bucket you own. ### What Gets Created * **Stateful NocoDB Workload** — The NocoDB server on port `8080` (`{release}-nocodb`), with `nocodb.replicas` replicas and configurable CPU and memory. * **Stateful Redis Workload** — The bundled single-node Redis (`{release}-nocodb-redis`) on port `6379` with password authentication. * **Database Workloads** — Single mode: one stateful PostgreSQL workload. HA mode: a stateful Patroni PostgreSQL workload, a stateful etcd workload, and a standard HAProxy leader-routing workload. * **Volume Sets** — One for local attachments at `/usr/app/data` (created only when `storage.type` is `local`), one for the Redis AOF file at `/data`, and the database subchart's data volumes. * **Secret** — A template-created dictionary secret (`{release}-nocodb-creds`) holding the bundled Redis password used to assemble `NC_REDIS_URL`. Your prerequisite key secret is *not* created by the template — see [Prerequisites](#prerequisites). * **Identity & Policy** — One identity shared by the NocoDB and Redis workloads, and a policy granting it `reveal` on exactly the secrets it reads: the credentials secret, the active database's credentials secret, your prerequisite key secret, and — when configured — the admin bootstrap, S3 static-key, and SMTP secrets. In keyless S3 mode the identity also carries the cloud account link and your bucket-scoped IAM policy. * **Cron Backup Workload** *(optional)* — Created inside the backing PostgreSQL store when database backups are enabled. This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites NocoDB signs its auth tokens and encrypts the stored credentials of external data sources with two keys that you supply through a [dictionary secret](/guides/create-secret/dictionary) created **before** installing. The values are never passed through Helm values. The secret holds two random values: ```bash theme={null} cpln secret create-dictionary --name my-nocodb-secrets \ --entry NC_AUTH_JWT_SECRET="$(openssl rand -hex 64)" \ --entry NC_CONNECTION_ENCRYPT_KEY="$(openssl rand -hex 32)" ``` Set `secrets.name` to the secret's name (default `my-nocodb-secrets`). Secrets are org-level, so no GVC flag is involved. Store a copy somewhere safe, outside Control Plane. Create the secret **before** installing. Without it the deployment sits waiting on a secret that does not exist and never becomes ready, which looks like a broken install. Both keys are write-once: rotating `NC_AUTH_JWT_SECRET` logs out every user, and changing `NC_CONNECTION_ENCRYPT_KEY` makes the stored credentials of external data sources undecryptable — upstream has no re-encryption path. Everything else works with the defaults. Four optional features need their own setup first: * **S3 attachment storage** — an existing bucket plus either a Control Plane cloud account (AWS) or a static-key secret (S3-compatible servers). See [Attachment Storage](#attachment-storage). Required if you plan to run more than one replica. * **Super-admin bootstrap** — a dictionary secret holding `NC_ADMIN_EMAIL` and `NC_ADMIN_PASSWORD`. See [Super-Admin Bootstrap](#super-admin-bootstrap). * **Authenticated SMTP** — a dictionary secret holding `NC_SMTP_USERNAME` and `NC_SMTP_PASSWORD`. See [Email](#email). * **Database backups** — a bucket and provider access set up beforehand. See [Backing Up](#backing-up). Once your key secret exists, install the template using your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Choosing a Database Mode Exactly one of the two backing stores must be enabled — the chart enforces this at render. NocoDB is wired to the active database automatically, including for its boot migrations. | | `postgres` (default) | `postgresHA` | | ---------------------- | -------------------------------------- | ------------------------------------------------------- | | What runs | One single-replica PostgreSQL workload | 3× Patroni PostgreSQL, 3× etcd, HAProxy leader endpoint | | PostgreSQL major | 18 | 17 | | Database failover | None | Automatic (Patroni leader election) | | First install to ready | About 1 minute | About 6 minutes | | Best for | Small teams and lightweight installs | Production | The default is the single instance: on Control Plane it sits on a persistent volume, so a node failure reschedules it and reattaches the same volume — the difference between the two modes is minutes of downtime, not data loss. Enabling HA is one flag: set `postgres.enabled: false` and `postgresHA.enabled: true`. The two paths run **different PostgreSQL majors**, so pick the mode before you have data — moving an existing database between them is a `pg_dump` and restore, not a values change. ## First Run NocoDB ships no default account. Unless you bootstrap one, the first person to sign up becomes the super admin. PostgreSQL and Redis come up first, then NocoDB applies its meta-database migrations before it starts serving. A default install typically reaches ready in about a minute; HA mode takes roughly six, most of it Patroni electing a leader. On a cold install a replica can start before PostgreSQL and Redis are accepting connections, log `[ioredis] … write EPIPE` followed by `Error: Connection terminated unexpectedly`, and exit. It restarts automatically and comes up clean about twenty seconds later. One such restart on a first install is expected. Browse to the canonical `*.cpln.app` endpoint of the `{release}-nocodb` workload and create the first account. It receives the `super` role. Do this as soon as the workload is ready. Signup is open by default. Turn on invite-only in **Team & Settings** inside the app — it is an in-app setting, not a template value. Member invitations and password resets are delivered only by email. Configure [SMTP](#email) before you invite your team. ## Configuration The default `values.yaml` for this template: ```yaml theme={null} nocodb: image: nocodb/nocodb:2026.07.0 replicas: 1 # >1 REQUIRES storage.type: s3; replicas coordinate through Redis siteUrl: "" # public base URL; empty = derive from the canonical endpoint resources: minCpu: 250m maxCpu: 1000m minMemory: 512Mi maxMemory: 2Gi # headroom for boot migrations and large imports secrets: name: my-nocodb-secrets # dictionary secret with NC_AUTH_JWT_SECRET + NC_CONNECTION_ENCRYPT_KEY — MUST exist before install admin: secretName: "" # empty = the first signup becomes super admin; set to a dictionary secret to bootstrap instead storage: type: local # local | s3 (s3 required for nocodb.replicas > 1) local: volumeset: capacity: 10 # GiB (minimum 10); mounted at /usr/app/data s3: bucket: my-nocodb-bucket region: us-east-1 endpoint: "" # set for S3-compatible servers (e.g. http://my-minio-workload:9000) forcePathStyle: false # true for most S3-compatible servers (MinIO, SeaweedFS) cloudAccountName: my-s3-cloud-account # keyless AWS auth; used only when auth.secretName is empty policyName: my-nocodb-s3-policy # your pre-created bucket-scoped IAM policy auth: secretName: "" # dictionary secret with NC_S3_ACCESS_KEY + NC_S3_ACCESS_SECRET; S3-compatible servers only fileUploadSizeLimit: 20971520 # max single attachment size in BYTES; 20971520 = 20 MiB smtp: enabled: false # off = invitations and password-reset emails cannot be delivered host: smtp.example.com port: 587 secure: false # false = STARTTLS/plain (587); true = implicit TLS (465) from: no-reply@example.com ignoreTls: false # true for a plaintext mail catcher rejectUnauthorized: false # true to require a valid server certificate auth: secretName: "" # optional dictionary secret with NC_SMTP_USERNAME + NC_SMTP_PASSWORD publicAccess: enabled: true # HTTPS UI, API, and shared views/forms on the auto *.cpln.app endpoint internalAccess: type: same-gvc # options: none, same-gvc, same-org, workload-list workloads: [] # used only with workload-list, e.g. //gvc/GVC_NAME/workload/WORKLOAD_NAME redis: # bundled single node — job/event pub-sub, cache, rate limiter (required) image: redis:8.10.0 auth: password: change-me-nocodb-redis # change before installing; letters, digits, - and _ only resources: minCpu: 100m maxCpu: 400m minMemory: 256Mi maxMemory: 512Mi volumeset: capacity: 10 # GiB (minimum 10); AOF at /data postgres: # single instance (default) — PostgreSQL 18 enabled: true image: postgres:18 config: username: nocodb password: change-me-nocodb-db # change before installing; letters, digits, - and _ only database: nocodb resources: minCpu: 250m maxCpu: 1000m minMemory: 512Mi maxMemory: 1Gi volumeset: capacity: 10 # GiB (minimum 10) backup: enabled: false # see Backing Up; provider: aws | gcp | minio postgresHA: # highly available (opt-in) — PostgreSQL 17 enabled: false postgres: username: nocodb password: change-me-nocodb-db # change before installing; letters, digits, - and _ only database: nocodb replicas: 3 resources: minCpu: 500m maxCpu: 1000m minMemory: 1Gi maxMemory: 2Gi volumeset: capacity: 10 # GiB per replica (minimum 10) backup: enabled: false # see Backing Up; mode: logical | wal-g, provider: aws | gcp | minio ``` ### NocoDB Server * `image` — The official NocoDB image. Pin a released tag. * `replicas` — Number of NocoDB replicas. Anything above `1` requires `storage.type: s3`; the chart fails the render with a clear message otherwise. See [Scaling](#scaling). * `siteUrl` — The public base URL NocoDB puts in invitation and password-reset links, and which decides whether the auth cookie is marked secure. It must match the URL browsers actually use. Leave it empty to derive it from the workload's canonical endpoint; set it, with the `https://` scheme, when you serve NocoDB on a custom domain. * `resources` — CPU and memory bounds for the NocoDB container. The defaults suit a small production instance; raise `maxCpu` and `maxMemory` together for large imports. Upstream recommends 4 vCPU and 8 GB for a busy multi-team instance. ### Super-Admin Bootstrap By default (`admin.secretName: ""`) NocoDB seeds no account and the first person to sign up becomes the super admin. To create the account up front instead, put the credentials in a dictionary secret and reference it by name: ```bash theme={null} cpln secret create-dictionary --name my-nocodb-admin \ --entry NC_ADMIN_EMAIL=admin@example.com \ --entry NC_ADMIN_PASSWORD='Password1!' ``` ```yaml theme={null} admin: secretName: my-nocodb-admin ``` The password must be at least 8 characters with an uppercase letter, a digit, and a special character. When set, the NocoDB identity is granted `reveal` on exactly that secret. NocoDB re-applies these credentials on **every** boot. If the account's password is later changed in the UI, the next restart silently reverts it to the value in the secret. Either leave `admin.secretName` empty or treat the secret as the account's source of truth. ### Attachment Storage `storage.type` decides where uploaded files, images, and thumbnails are kept. Attachments are written to a persistent volume set mounted at `/usr/app/data`, and survive restarts, redeploys, and upgrades under the same release name. ```yaml theme={null} storage: type: local local: volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) ``` Local storage works only with a single replica — each replica gets its own volume, so an attachment uploaded through one replica would be missing from the others. AWS S3 uses a Control Plane cloud identity: no credentials are stored, and the workload's identity vends temporary credentials at runtime — uploaded attachments come back as signed URLs backed by STS session credentials. This is the only supported way to reach AWS S3; the chart rejects static keys unless `storage.s3.endpoint` is set. Create an S3 bucket. Set `storage.s3.bucket` and `storage.s3.region` to match. If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) for your AWS account. Set `storage.s3.cloudAccountName` to its name. Create an AWS IAM policy with the JSON below (replace `YOUR_BUCKET_NAME` with your bucket), then set `storage.s3.policyName` to the policy's name: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET_NAME", "arn:aws:s3:::YOUR_BUCKET_NAME/*" ] } ] } ``` Keep `storage.s3.auth.secretName` empty. The template then attaches the cloud account and your policy to the NocoDB identity, and no access keys exist anywhere. No attachment volume set is created in this mode. S3-compatible servers cannot federate with a cloud account, so they use static access keys held in a dictionary secret. Create the bucket on your server. For the [MinIO](/template-catalog/templates/minio) template deployed in the same GVC, the S3 API address is `http://WORKLOAD_NAME.GVC_NAME.cpln.local:9000`. Set `storage.s3.endpoint` to the S3 API address including scheme and port, and `storage.s3.forcePathStyle: true` (required by most S3-compatible servers). Create a dictionary secret with the server's credentials and set `storage.s3.auth.secretName` to its name: ```bash theme={null} cpln secret create-dictionary --name my-nocodb-s3-keys \ --entry NC_S3_ACCESS_KEY=YOUR_ACCESS_KEY \ --entry NC_S3_ACCESS_SECRET=YOUR_SECRET_KEY ``` The NocoDB identity is granted `reveal` on exactly this secret. `storage.fileUploadSizeLimit` caps the size of a single attachment and is a **byte count**, not a size string — the default `20971520` is 20 MiB. It applies in both storage modes. An upload larger than the limit is rejected outright, not truncated: the API returns `413` with `{"message": "File too large", "error": "Payload Too Large", "statusCode": 413}`. A file under the limit is stored byte-exact. Raise the value before importing large attachments. ### Scaling `nocodb.replicas` sets how many NocoDB replicas run. Replicas coordinate through the bundled Redis, which carries the job and event pub/sub, the shared metadata cache, and the rate limiter — all three are what make a second replica correct rather than merely present. ```yaml theme={null} nocodb: replicas: 2 storage: type: s3 s3: bucket: my-nocodb-bucket region: us-east-1 cloudAccountName: my-s3-cloud-account policyName: my-nocodb-s3-policy ``` `nocodb.replicas` above `1` requires `storage.type: s3`. Local attachments live on per-replica volumes, so an attachment uploaded through one replica would return a `404` from another. The chart refuses to render the combination. Measured at two replicas: a full rolling upgrade served **434 of 434 requests with HTTP 200**, and abruptly killing one replica's process produced **6 × HTTP 503 out of 289 requests inside an \~11 second window** before traffic settled on the survivor. A graceful rollout is therefore seamless, while a hard replica loss costs up to about one readiness-probe period (15 seconds) of partial failures. Live updates reach the browser over HTTP long-poll — `POST /jobs/listen`, backed by the Redis pub/sub — not over websockets. A stalled live update is a Redis or `/jobs/listen` problem. Background jobs run inside the web process. Community Edition ships only an in-process queue, so a long import, export, or base duplication dies with the replica running it and has to be re-run. Multiple replicas buy request availability and clean rolling upgrades, not job durability. ### Email SMTP is **off by default**, and a default install works without it — the first visitor still creates the super-admin account and signs in with its password. ```yaml theme={null} smtp: enabled: true host: smtp.example.com port: 587 secure: false # false = STARTTLS/plain (587); true = implicit TLS (465) from: no-reply@example.com ignoreTls: false rejectUnauthorized: false auth: secretName: my-nocodb-smtp # dictionary secret with NC_SMTP_USERNAME + NC_SMTP_PASSWORD ``` NocoDB activates its mail plugin only when `host`, `port`, and `from` are all set; the chart fails the render if `smtp.enabled` is true and any of them is missing. For an authenticated relay, create the credentials secret before installing and reference it by name: ```bash theme={null} cpln secret create-dictionary --name my-nocodb-smtp \ --entry NC_SMTP_USERNAME=apikey --entry NC_SMTP_PASSWORD=YOUR_PASSWORD ``` Leave `smtp.auth.secretName` empty for a relay that does not require authentication. When it is set, the NocoDB identity is granted `reveal` on exactly that secret. With SMTP disabled there is no way to deliver a member invitation or a password-reset mail. Configure `smtp.*` before inviting collaborators. ### Access * `publicAccess.enabled` — Serve the UI, the API, and shared views and forms on the auto-assigned `*.cpln.app` HTTPS endpoint (default). Set to `false` for an internal-only instance: external requests are then refused at the edge with a `403`, and in-GVC callers still reach it per `internalAccess`. With public access off, `nocodb.siteUrl` falls back to the internal `http://{release}-nocodb.{gvc}.cpln.local:8080` address. * `internalAccess.type` — Controls which workloads can reach NocoDB over the internal network: | Type | Description | | --------------- | ---------------------------------------------------------------------- | | `none` | No internal access. | | `same-gvc` | Allow access from all workloads in the same GVC (default). | | `same-org` | Allow access from all workloads in the same organization. | | `workload-list` | Allow access only from workloads listed in `internalAccess.workloads`. | Firewall changes are not instantaneous. After an upgrade that flips `publicAccess.enabled` or `internalAccess.type` reports success, requests can keep hitting the old rules for roughly half a minute or more. Re-test after a pause rather than concluding the knob did not work. ### Redis * `redis.image` — The Redis image to run. * `redis.auth.password` — Password for the bundled Redis, wired into `NC_REDIS_URL`. **Change it before installing.** Use only letters, digits, `-`, and `_`: the value is embedded in a URL. Authentication is required — an unauthenticated client is refused with `NOAUTH Authentication required.` * `redis.resources` / `redis.volumeset.capacity` — CPU and memory bounds and the volume size for the AOF file at `/data`. Redis runs with `appendonly yes` and reloads its data set after a restart. Redis runs with `maxmemory-policy noeviction`, and this is deliberate: an evicted key would silently drop a job event. NocoDB does not tolerate losing its Redis connection — it logs an ioredis error storm and exits rather than reconnecting, and the platform restarts it. See the note about the first upgrade under [Important Notes](#important-notes). ### Database Enable exactly one of `postgres` (single instance, default) or `postgresHA` (highly available) — see [Choosing a Database Mode](#choosing-a-database-mode). In both modes, **change the database password before installing** (`postgres.config.password` / `postgresHA.postgres.password`); it seeds the database on first boot and cannot be changed by editing values afterwards. Use only letters, digits, `-`, and `_` — the password is embedded in the connection URL. NocoDB connects to the single instance directly, or to the HAProxy leader endpoint in HA mode, and creates its own meta tables and one schema per base on first boot. `postgres.resources` / `postgresHA.resources` and the `volumeset.capacity` values (GiB, minimum 10, per replica in HA mode) size the backing store. `postgresHA.replicas` sets the number of Patroni replicas. **Template version `1.0.0` did not compact the etcd cluster inside the bundled highly available database**, so etcd's backend grows with time alone and goes read-only once it reaches its 2 GiB quota — after roughly 110 days — taking PostgreSQL failover with it. Only installs running the HA database are affected (`postgresHA.enabled`, which is off by default here); see [etcd History Compaction](/template-catalog/templates/postgres-highly-available#etcd-history-compaction) for the mechanism and the symptoms. Upgrade to `1.0.1` or later to turn compaction on: that stops further growth but cannot shrink a backend that has already grown, and a cluster that has already raised a `NOSPACE` alarm needs operator recovery rather than an upgrade. ## Connecting | What | Value | | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | | Public UI / API | `https://.cpln.app` — `status.canonicalEndpoint` of `{release}-nocodb` (`cpln workload get {release}-nocodb -o yaml`) | | Health check | `GET https://.cpln.app/api/v1/health` — unauthenticated | | Version | `GET https://.cpln.app/api/v1/version` — unauthenticated | | Internal (same GVC) | `http://{release}-nocodb.{gvc}.cpln.local:8080` | | Login | The first account created after install, or the `admin.secretName` credentials | | Redis (internal) | `{release}-nocodb-redis.{gvc}.cpln.local:6379`, password from `redis.auth.password` | | PostgreSQL (internal, single mode) | `{release}-postgres.{gvc}.cpln.local:5432`, credentials from `postgres.config` | | PostgreSQL (internal, HA mode) | `{release}-postgres-ha-proxy.{gvc}.cpln.local:5432`, credentials from `postgresHA.postgres` | ## Backing Up Database backups are optional and disabled by default. When enabled, a scheduled job runs inside the backing PostgreSQL store and uploads to your bucket under the configured prefix — covering every base, record, view, and user, but not the attachments on the local storage volume set (use `storage.type: s3` for durable attachments). Enable with `postgres.backup.enabled` or `postgresHA.backup.enabled`, matching your database mode, and complete the storage setup for your provider **before** installing. The values below are shown under `backup.*` — set them within the enabled database block. Create an S3 bucket. Set `backup.aws.bucket` and `backup.aws.region` to match. If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) for your AWS account. Set `backup.aws.cloudAccountName` to its name. Create an IAM policy granting the required S3 actions on the bucket, and set `backup.aws.policyName` to its name: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["s3:ListBucket"], "Resource": "arn:aws:s3:::my-backup-bucket" }, { "Effect": "Allow", "Action": ["s3:PutObject", "s3:GetObject", "s3:DeleteObject"], "Resource": "arn:aws:s3:::my-backup-bucket/*" } ] } ``` Create a GCS bucket. Set `backup.gcp.bucket` to its name. If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) for your GCP project and grant its service account **Storage Object Admin** (`roles/storage.objectAdmin`) on the bucket — not on the whole project. Set `backup.gcp.cloudAccountName` to its name. Create your bucket on the server. Set `backup.minio.bucket` to its name. Set `backup.minio.endpoint` to the S3 API address including port, and `backup.minio.accessKey` / `backup.minio.secretKey` to credentials with access to the bucket. No Cloud Account is required — the keys authenticate directly. In HA mode, `postgresHA.backup.mode` selects `logical` (scheduled `pg_dump`) or `wal-g` (continuous WAL archiving). The single-instance path takes a `postgres.backup.schedule` cron expression (default `0 2 * * *`), and its `postgres.backup.image` tag must match the PostgreSQL major in `postgres.image` — change both together if you move off the default. The full per-provider walkthrough, including restores, lives in the backing [postgres](/template-catalog/templates/postgres) and [postgres-highly-available](/template-catalog/templates/postgres-highly-available) template documentation. ## Important Notes * **Create the key secret before installing** — `secrets.name` must point at an existing dictionary secret holding `NC_AUTH_JWT_SECRET` and `NC_CONNECTION_ENCRYPT_KEY`. Without it the deployment waits on a secret that does not exist and looks broken. * **Both keys are write-once** — rotating `NC_AUTH_JWT_SECRET` logs out every user, and changing `NC_CONNECTION_ENCRYPT_KEY` makes the stored credentials of external data sources undecryptable. Back both values up outside Control Plane. * **Sign up immediately after install** — signup is open by default, so on a public endpoint the first visitor to sign up becomes the super admin. Then turn on invite-only in **Team & Settings**; it is an in-app setting, not a template value. * **The first `helm upgrade` after an install briefly restarts the bundled Redis**, even when nothing about Redis changed, and NocoDB exits rather than reconnecting. At `replicas: 1` that is a short outage on a routine config change — the endpoint returns `503 no healthy upstream` for about a minute. At `replicas: 2` it was not user-visible. Later upgrades of the same release do not restart Redis. * **`nocodb.replicas` above `1` requires `storage.type: s3`** — local attachments are per-replica and would `404` across replicas; the chart refuses to render the combination. * **A hard replica loss is not free** — a rolling upgrade at two replicas served 434 of 434 requests successfully, but killing a replica outright produced 6 failed requests out of 289 across an \~11 second window. The readiness probe runs every 15 seconds, so budget up to one probe period of partial `503`s on abrupt replica death. * **Background jobs are in-process** — a long import, export, or base duplication does not survive the replica running it and must be re-run. * **Live updates use HTTP long-poll, not websockets** — `POST /jobs/listen` backed by Redis pub/sub is the delivery path. * **`storage.fileUploadSizeLimit` is a byte count** — the default `20971520` is 20 MiB. An over-limit upload is rejected with `413` and `File too large`, not truncated. * **AWS S3 is keyless only** — use a cloud account plus a bucket-scoped IAM policy. Static keys are accepted only when `storage.s3.endpoint` points at an S3-compatible server. * **`admin.secretName` re-applies on every boot** — a password changed in the UI is reverted at the next restart. Leave it empty or treat the secret as the source of truth. * **The single-instance and HA database paths run different PostgreSQL majors** — 18 and 17 respectively. Choose before you have data; switching is a dump and restore. * **Change `postgres.config.password` (or `postgresHA.postgres.password`) and `redis.auth.password` before installing** — both seed their component on first boot, and both are embedded in connection URLs, so use only letters, digits, `-`, and `_`. * **Set `nocodb.siteUrl` when NocoDB sits behind a custom domain**, with the scheme (`https://data.example.com`). It drives invitation and password-reset links and the auth cookie's secure flag, so a mismatch with the URL browsers use breaks both. * **Firewall changes take effect after a delay** — flipping `publicAccess.enabled` or `internalAccess.type` can take half a minute or more to be enforced after the upgrade reports success. * **With SMTP off, invitations and password resets cannot be delivered** — configure `smtp.*` before inviting collaborators. * **Data survives restarts and upgrades; uninstalling deletes it** — bases live in the database volume set and local attachments in the storage volume set, and both go with the release. Your prerequisite key secret is yours and survives an uninstall. * **SSO/SAML/OIDC, audit logs, and row-level security are Enterprise features** — they ship in the same image but require a purchased licence key that this template never sets. * **NocoDB Community Edition is fair-code** — the Sustainable Use License permits self-hosting for your own use; offering NocoDB as a hosted service to others requires a commercial licence. ## External References Official NocoDB product documentation Deployment, upgrades, and operational guidance Every setting the NocoDB server reads from its environment REST API reference for reading and writing records Source code and release notes View the source files, default values, and chart definition # Ollama Source: https://docs.controlplane.com/template-catalog/templates/ollama Deploy Ollama on Control Plane using the Template Catalog. Covers configuration, volumes, scaling, and LLM serving with Open WebUI sidecar and automatic model pulling. ## Overview Ollama is an open-source platform for running large language models locally. This template deploys Ollama alongside Open WebUI as a sidecar interface, with a startup script that automatically pulls the configured default model on first launch. The Ollama API is accessible internally, while the Open WebUI is exposed externally. ### What Gets Created * **Stateful Workload** — A single-replica workload with two containers: the Ollama API server and the Open WebUI interface. The API runs on port `11434` (internal only) and the UI runs on port `8080` (externally accessible). * **Volume Set** — Persistent storage shared by both containers: the Ollama API stores model data at `/root/.ollama` and Open WebUI stores state at `/app/backend/data`. Supports optional autoscaling. * **Secret** — An opaque startup script that checks for the default model on launch and pulls it from the Ollama registry if not already present. * **Identity & Policy** — An identity bound to the workload with `reveal` access to the startup script secret. This template does not create a GVC. You must deploy it into an existing GVC. ## Installation The default configuration requests 6 CPU and 8Gi of memory for the Ollama API container. Depending on your organization's quotas, you may need to request a limit increase. GPU access requires explicit enablement — contact Control Plane support to enable GPU resources for your organization. To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} # Alternatives: llava, gemma, mistral, phi3, etc. defaultModel: llama3 workload: containers: ui: name: ollama-ui image: ghcr.io/open-webui/open-webui:main port: 8080 resources: cpu: 500m memory: 1Gi api: name: ollama image: ollama/ollama port: 11434 resources: cpu: 6 memory: 8Gi gpu: nvidia: model: t4 quantity: 1 volumeset: initialCapacity: 10 autoscaling: enabled: false maxCapacity: 100 # Maximum capacity in GiB minFreePercentage: 10 # Trigger scaling when free space drops below this percentage scalingFactor: 1.2 # Multiply current capacity by this factor when scaling up performanceClass: general-purpose-ssd snapshots: retentionDuration: 7d firewall: external: inboundAllowCIDR: - 0.0.0.0/0 outboundAllowCIDR: - 0.0.0.0/0 internal_access: type: same-gvc # options: same-gvc, same-org, workload-list workloads: # Note: can only be used if type is same-gvc or workload-list #- //gvc/GVC_NAME/workload/WORKLOAD_NAME ``` ### Default Model * `defaultModel` — The Ollama model to download on first startup if not already present on the volume. Accepts any model name from the [Ollama model library](https://ollama.com/library) (e.g. `llama3`, `llava`, `gemma`, `mistral`, `phi3`). ### Open WebUI Container * `workload.containers.ui.image` — Open WebUI container image. * `workload.containers.ui.port` — Port the UI listens on (default: `8080`). * `workload.containers.ui.resources.cpu` / `workload.containers.ui.resources.memory` — CPU and memory for the UI container. ### Ollama API Container * `workload.containers.api.image` — Ollama container image. * `workload.containers.api.port` — Port the Ollama API listens on (default: `11434`). * `workload.containers.api.resources.cpu` / `workload.containers.api.resources.memory` — CPU and memory for the Ollama API container. Large models require significant resources. * `workload.containers.api.gpu` — Optional GPU configuration: * `nvidia.model` — GPU model type (e.g. `t4`). * `nvidia.quantity` — Number of GPUs to allocate. GPU access must be explicitly enabled for your organization by Control Plane support before it can be used in a workload. ### Storage * `volumeset.initialCapacity` — Initial volume size in GiB (minimum 10). Large models require more storage — plan accordingly. * `volumeset.autoscaling.enabled` — Automatically expand the volume as it fills. When enabled: * `maxCapacity` — Maximum volume size in GiB. * `minFreePercentage` — Trigger a scale-up when free space drops below this percentage. * `scalingFactor` — Multiply the current capacity by this factor when scaling up. ### Firewall * `firewall.external.inboundAllowCIDR` — CIDR ranges allowed to access the Open WebUI externally (default: `0.0.0.0/0`). * `firewall.external.outboundAllowCIDR` — CIDR ranges the workload can reach externally. Must include `0.0.0.0/0` (or the Ollama registry) for model downloads to succeed. ### Internal Access * `internal_access.type` — Controls which workloads can reach the Ollama API on port `11434` internally: | Type | Description | | --------------- | --------------------------------------------------------------- | | `same-gvc` | Allow access from all workloads in the same GVC | | `same-org` | Allow access from all workloads in the same organization | | `workload-list` | Allow access only from specific workloads listed in `workloads` | ### Accessing Ollama **Open WebUI** (browser interface) is available externally at: ```text theme={null} https://RELEASE_NAME-ollama.GVC_NAME.cpln.app ``` **Ollama API** is available internally to other workloads in the same GVC at: ```text theme={null} http://RELEASE_NAME-ollama.GVC_NAME.cpln.local:11434 ``` ## External References Official Ollama repository and documentation Open WebUI repository and documentation Browse available models for Ollama View the source files, default values, and chart definition # Open WebUI Source: https://docs.controlplane.com/template-catalog/templates/open-webui Deploy Open WebUI on Control Plane using the Template Catalog. Self-hosted, ChatGPT-style chat interface for LLMs, backed by an in-GVC Ollama server and any OpenAI-compatible API, with users, RAG, and model management on a persistent volume served over HTTPS. ## Overview Open WebUI is a self-hosted, ChatGPT-style chat interface for large language models with user accounts, RAG (chat grounded in your uploaded documents), and model management. This template deploys a single stateful workload that keeps all of its state on a persistent volume and connects to your models through an in-GVC [Ollama](/template-catalog/templates/ollama) server and/or any OpenAI-compatible endpoint, served over HTTPS on the canonical `*.cpln.app` endpoint. Sign-ups are open on a fresh install so you can onboard immediately — the first account you register becomes the admin, and you [lock registration down](#important-notes) afterward. ### Architecture * **Open WebUI** — A single-replica stateful workload serving the web UI and API on port `8080`. Its public `WEBUI_URL` is derived from the canonical endpoint at boot, so links resolve correctly with no manual configuration. * **Embedded SQLite on a persistent volume** — All durable state (the `webui.db` database, uploaded files, the default Chroma vector store used for RAG, and cache) lives on the workload's volume set at `/app/backend/data`. SQLite is single-writer, so there is no external database dependency and the workload runs as exactly one replica. * **Model backends** — Chat completions are served by an existing Ollama workload in the same GVC (default), an OpenAI-compatible API (optional), or both. Neither backend is bundled by this template — you point it at model providers you run or subscribe to. ### What Gets Created * **Stateful Open WebUI Workload** — A single replica serving the web UI and API on port `8080`. * **Volume Set** — A 10 GiB persistent volume mounted at `/app/backend/data` holding the SQLite database, uploaded files, the RAG vector store, and cache. Scheduled snapshots protect the data, and a final snapshot is kept on delete. * **Config Secret** — Holds the stable `WEBUI_SECRET_KEY` that signs sessions and JWTs. * **Start-Script Secret** — An opaque secret whose boot script sets `WEBUI_URL` from the canonical endpoint. * **Identity & Policy** — An identity bound to the workload with a least-privilege policy granting `reveal` access to exactly the secrets it mounts (the config and start-script secrets, plus your OpenAI-key secret only when you configure one). This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites None for a default install — no secrets, cloud accounts, or external resources are required to bring the UI up. The items below are optional and only needed for the corresponding feature. * **License awareness** — Open WebUI ships under the "Open WebUI License" (BSD-3-Clause plus a branding-protection clause). It is free to self-host and run in production at any scale, but you must keep the "Open WebUI" branding visible in the UI **unless** your deployment serves 50 or fewer users, or you obtain enterprise permission. See [Important Notes](#important-notes). * **Ollama backend (recommended)** — An existing [Ollama](/template-catalog/templates/ollama) workload in the same GVC. Deploy the Ollama template first, then set its workload name in `ollama.workloadName`. Enabled by default. * **OpenAI-compatible backend (optional)** — Create, **before installing**, an [opaque secret](https://docs.controlplane.com/reference/secret#opaque) (`encoding: plain`) in your org holding your API key, and set its name in `openai.apiKeySecretName`. An empty value leaves this backend off. Install the template using your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} image: ghcr.io/open-webui/open-webui:v0.11.0 resources: cpu: 1 # max vCPU memory: 2Gi # max memory (RAG loads a local embedding model into RAM on first use) minCpu: 500m minMemory: 1Gi # ─── Persistence ────────────────────────────────────────────────────────────── # One per-replica volume holds everything durable: SQLite webui.db, uploaded # files, the default Chroma vector store (RAG), and cache. Mounts /app/backend/data. volumeset: capacity: 10 # GiB (platform minimum 10) # ─── Backup ─────────────────────────────────────────────────────────────────── # Platform-managed crash-consistent snapshots (no cloud account/bucket needed). backup: enabled: true schedule: "0 3 * * *" # cron in UTC — daily 03:00 (hourly is the platform max) retention: 7d # how long each snapshot is kept (e.g. 7d, 720h, 30d) # ─── Authentication ─────────────────────────────────────────────────────────── auth: # Signs sessions/JWTs. Override ONCE at install (openssl rand -base64 32). # Must stay STABLE across upgrades — changing it logs every user out. webuiSecretKey: "CHANGE-ME-openssl-rand-base64-32-abcdEFGH1234" enableSignup: true # first registered user becomes ADMIN — turn OFF after onboarding # ─── Model backends ─────────────────────────────────────────────────────────── # Point at an existing `ollama` template deployment in this same GVC. ollama: enabled: true workloadName: ollama # the ollama workload's name in this GVC port: 11434 # → OLLAMA_BASE_URL = http://{workloadName}.{gvc}.cpln.local:{port} # Optional OpenAI-compatible provider. The API key is a PREREQUISITE opaque # secret (encoding: plain) you create BEFORE install; reference it by name here. # Empty apiKeySecretName = this backend is off. openai: baseUrl: https://api.openai.com/v1 # any OpenAI-compatible endpoint apiKeySecretName: "" # e.g. my-openwebui-openai-key ; empty = OpenAI backend off # ─── Access ─────────────────────────────────────────────────────────────────── customDomain: "" # full URL, e.g. https://chat.example.com ; empty = canonical *.cpln.app publicAccess: enabled: true # serve the UI over public HTTPS on the canonical *.cpln.app endpoint internalAccess: # inbound firewall scope for in-GVC callers of the Open WebUI API type: none # options: none, same-gvc, same-org, workload-list workloads: [] # used with workload-list ``` ### Instance & Resources * `image` — The Open WebUI container image. * `resources` — CPU and memory for the container. RAG loads a local embedding model into RAM on first use, so keep memory at or above the default. * `volumeset.capacity` — Volume size in GiB (minimum 10) for the SQLite database, uploads, RAG vector store, and cache. ### Backup Scheduled, crash-consistent snapshots of the data volume, managed by the platform — no cloud account or bucket is required. See [Backing Up](#backing-up) for how snapshots and restores work. * `backup.enabled` — Take periodic snapshots of the data volume (default `true`). * `backup.schedule` — Cron expression in UTC (default `0 3 * * *`, daily at 03:00). The platform does not accept schedules more frequent than hourly. * `backup.retention` — How long each snapshot is kept, e.g. `7d`, `720h`, `30d`. ### Authentication * `auth.webuiSecretKey` — Signs session tokens and JWTs. Override it **once** at install with `openssl rand -base64 32`, then keep it stable forever — see [Important Notes](#important-notes). * `auth.enableSignup` — Open registration on the public endpoint (default `true` so a fresh install is usable). The first account registered becomes the admin; **turn this off after onboarding** — see [Important Notes](#important-notes). ### Model backends At least one backend is needed to actually chat. The Ollama backend is on by default; the OpenAI-compatible backend is off until you supply a key secret. You can enable both. * `ollama.enabled` — Connect to an existing Ollama workload in this GVC (default `true`). * `ollama.workloadName` — The Ollama workload's name in this GVC. The base URL is derived as `http://{workloadName}.{gvc}.cpln.local:{port}`. * `ollama.port` — The Ollama API port (default `11434`). * `openai.baseUrl` — Any OpenAI-compatible endpoint (default `https://api.openai.com/v1`). * `openai.apiKeySecretName` — Name of your pre-created opaque secret holding the API key (see [Prerequisites](#prerequisites)). Empty (default) leaves the OpenAI backend off. Model-backend settings are read from the environment only on the **first** boot and are then stored in the app database. Changing `ollama.workloadName` or `openai.*` via a later `helm upgrade` is ignored — update model connections afterward from the admin UI under **Settings → Connections**. See [Important Notes](#important-notes). ### Access * `customDomain` — Full URL of a custom domain, e.g. `https://chat.example.com`. Empty (default) uses the canonical `*.cpln.app` endpoint. * `publicAccess.enabled` — Serve the UI over public HTTPS on the canonical `*.cpln.app` endpoint (default). When disabled, external requests are blocked at the edge and only in-GVC callers reach it per `internalAccess`. * `internalAccess.type` — Internal firewall scope for in-GVC callers of the Open WebUI API: | Type | Description | | --------------- | ---------------------------------------------------------------------- | | `none` | No internal access (default). | | `same-gvc` | Allow access from all workloads in the same GVC. | | `same-org` | Allow access from all workloads in the same organization. | | `workload-list` | Allow access only from workloads listed in `internalAccess.workloads`. | ## Connecting | What | Value | | -------------------- | ------------------------------------------------------------------------------------- | | Web UI (public) | `https://.cpln.app` — `status.canonicalEndpoint` of `{release}-open-webui` | | Internal (if opened) | `http://{release}-open-webui.{gvc}.cpln.local:8080` | | Ollama backend | `http://{ollama.workloadName}.{gvc}.cpln.local:11434` (the existing Ollama workload) | | Login | The account you register in the UI — the first registration becomes the admin | ### Using a Model Backend Deploy the [Ollama](/template-catalog/templates/ollama) template into the same GVC and pull at least one model, then install Open WebUI with `ollama.workloadName` set to that workload's name — its models appear in the model picker automatically. To use a hosted provider instead, create the API-key secret (see [Prerequisites](#prerequisites)), set `openai.apiKeySecretName`, and Open WebUI lists that provider's models. If a configured Ollama backend is unreachable the UI still boots and simply shows no Ollama models — confirm `ollama.workloadName` names a `ready` Ollama workload in the same GVC. ## Backing Up Backups are **scheduled volume snapshots** managed by the platform — no cloud account or bucket is required. On the configured cron schedule the platform takes a crash-consistent snapshot of the data volume; SQLite recovers cleanly from one. Snapshots are pruned automatically after `backup.retention`, and a final snapshot is always taken when the release is uninstalled. * **Default schedule** — Daily at 03:00 UTC (`backup.schedule: "0 3 * * *"`), kept for 7 days (`backup.retention: 7d`). * **Minimum interval** — The platform does not accept schedules more frequent than hourly. List the snapshots for the release's volume set: ```bash theme={null} cpln volumeset snapshot get {release}-open-webui-data --gvc {gvc} -o yaml ``` Snapshots live in the platform storage layer alongside the volume, not off-site. They protect against data corruption and accidental changes, but losing the whole GVC would lose them too. ## Restoring a Backup Restore is **in-place** on the release's own volume set: the platform provisions a fresh volume from the chosen snapshot and swaps it in, then the workload restarts to remount it. Restoring reverts the volume to the exact snapshot state — any changes made after that snapshot are lost — and restarts the single-replica workload, so the UI is briefly unavailable during the swap. List snapshots and note the `name`, `location`, and `volumeIndex` of the one you want: ```bash theme={null} cpln volumeset snapshot get {release}-open-webui-data --gvc {gvc} -o yaml ``` Provision a fresh volume from the snapshot and swap it in. The workload restarts to remount it: ```bash theme={null} cpln volumeset snapshot restore {release}-open-webui-data \ --snapshot-name \ --location \ --volume-index 0 \ --gvc {gvc} ``` Once the workload is ready again, log in and confirm your chats and settings are present. ## Important Notes * **License / branding clause** — You must keep the "Open WebUI" branding visible in the UI **unless** your deployment serves 50 or fewer users, or you have enterprise permission. Removing the branding outside those cases violates the license; it is not a template setting. * **The first user to register becomes the admin.** Sign-ups are open by default so the install is immediately usable, which means anyone reaching the public endpoint can register while it is open. Register your admin account first, then set `auth.enableSignup=false` to lock it down. * **`auth.webuiSecretKey` must never change after first install.** It is the key that signs session tokens and JWTs. Rotating it — or letting a fresh volume regenerate one — logs every user out. Override it once with `openssl rand -base64 32` and keep it stable. * **Single replica, by design.** The embedded SQLite is single-writer and the volume set is per-replica, so the workload is pinned to one replica with no `replicas` knob. A restart or upgrade is a brief full outage (about a minute). * **Model-backend settings apply at install, then persist in the app database.** `ollama.workloadName` and `openai.*` are read from the environment only on the first boot and then stored in `webui.db`. Changing them via a later `helm upgrade` is silently ignored — update model connections afterward from the admin UI (**Settings → Connections**). * **Data lives only on the volume set.** Uninstall deletes it (a final snapshot is taken first); a reinstall starts empty. Changing the secret and redeploying does not re-key existing data. * **Backups are in-platform, not off-site.** Scheduled snapshots and the final snapshot on uninstall live in the platform storage layer next to the volume; losing the whole GVC would lose them too. See [Backing Up](#backing-up). ## External References Upstream source repository Official documentation and getting-started guides Full list of supported configuration variables The Open WebUI License and its branding clause Deploy the in-GVC Ollama model backend View the source files, default values, and chart definition # OpenBao Source: https://docs.controlplane.com/template-catalog/templates/openbao Deploy OpenBao on Control Plane using the Template Catalog. The Linux Foundation's open-source fork of HashiCorp Vault — an identity-based secrets engine with a Vault-compatible API and CLI, integrated raft storage, and hands-free auto-unseal from a platform secret, AWS KMS, or GCP Cloud KMS. ## Overview OpenBao is the Linux Foundation's open-source (MPL-2.0) fork of HashiCorp Vault: an identity-based secrets engine with a Vault-compatible API and CLI, covering KV v2 storage, dynamic secrets, PKI, and transit encryption. This template deploys a single-node OpenBao server with integrated raft storage on a persistent volume set, and auto-unseal so the server comes back on its own after every restart. If you are choosing between catalog templates: OpenBao is the Vault-compatible engine for workloads that need dynamic secrets, PKI, transit, or existing Vault tooling, while [Infisical](/template-catalog/templates/infisical) is an application-secrets workflow platform with a team-oriented web UI. The two solve different problems and can be run side by side. ### Architecture * **OpenBao server** — A single `stateful` workload serving the HTTP API and web UI on port `8200`, configured from a rendered HCL config file. Storage is OpenBao's integrated raft backend, written to a persistent volume set at `/openbao/data`. * **Auto-unseal** — The encryption barrier is unwrapped automatically on every boot in one of three seal modes: `static` (a 32-byte key held in a Control Plane secret you create), `awskms`, or `gcpckms`. Both KMS modes are keyless — the workload identity reaches the KMS key through a Control Plane [cloud account](https://docs.controlplane.com/guides/create-cloud-account), with no static cloud credentials in the container. * **Private by default** — External access is off (`publicAccess.enabled: false`); the server is reachable only from inside the GVC until you turn it on. ### What Gets Created * **Stateful OpenBao Workload** — One replica serving the API and UI on port `8200`. * **Volume Set** — Persistent raft storage for all secrets, auth configuration, and cluster state. A final snapshot is retained for 7 days when the volume set is deleted. * **Config Secret** — An opaque secret holding the rendered HCL server config, file-mounted into the container. * **Identity & Policy** — An identity bound to the workload with `reveal` on exactly the secrets it mounts. In the KMS seal modes the identity also carries the keyless cloud access used to reach the KMS key. This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites Prerequisites depend on the seal mode you choose. **Static seal (the default): create the unseal-key secret before installing.** The workload references it by name (`seal.static.secretName`, default `my-openbao-unseal-key`) and the deployment wedges until the secret exists. Create it as an **opaque** secret holding a 32-byte key: ```bash theme={null} printf '%s' "$(openssl rand -hex 32)" | cpln secret create-opaque --name my-openbao-unseal-key --encoding plain -f - ``` The unseal key is **write-once — never change or delete this secret.** It wraps OpenBao's encryption barrier; losing or rotating the key makes every stored secret permanently unrecoverable. Back the key up somewhere safe outside this instance. **KMS seal modes (`awskms` / `gcpckms`)** need a KMS key, a Control Plane cloud account, and a key-scoped grant instead — see [Auto-unseal setup](#auto-unseal-setup-kms-modes). No unseal-key secret is required in these modes. Install the template using your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} openbao: # Official image; 2.6.x pinned deliberately (2.7 moves KMS seals to external plugins) image: openbao/openbao:2.6.1 resources: minCpu: 250m maxCpu: 1000m minMemory: 256Mi maxMemory: 1Gi # How the encryption barrier is unwrapped on boot. One mode only. seal: type: static # static | awskms | gcpckms # static: auto-unseal from a key in a platform secret. PREREQUISITE — create the # opaque secret BEFORE install (a missing secret wedges the deployment): # printf '%s' "$(openssl rand -hex 32)" | cpln secret create-opaque --name my-openbao-unseal-key --encoding plain -f - # WRITE-ONCE: losing or changing this key makes all stored data unrecoverable. static: secretName: my-openbao-unseal-key # awskms: keyless auto-unseal via AWS KMS through a Control Plane cloud account. # Create the KMS key + a scoped IAM policy before install. aws: kmsKeyId: arn:aws:kms:us-east-1:111111111111:key/my-openbao-kms-key # key ARN (or ID/alias) region: us-east-1 cloudAccountName: my-aws-cloud-account policyName: my-openbao-kms-policy # customer-managed IAM policy granting Encrypt/Decrypt/DescribeKey on the key # gcpckms: keyless auto-unseal via GCP Cloud KMS through a Control Plane cloud account. gcp: project: my-gcp-project region: global # key ring location keyRing: my-openbao-keyring cryptoKey: my-openbao-key cloudAccountName: my-gcs-cloud-account volumeset: capacity: 10 # GiB (minimum 10) — raft data: all secrets, auth config, cluster state publicAccess: enabled: false # true = HTTPS API + web UI on the auto *.cpln.app endpoint internalAccess: type: same-gvc # none | same-gvc | same-org | workload-list workloads: [] # used only with workload-list ``` ### OpenBao Server * `openbao.image` — The official OpenBao image. The template pins `2.6.x` deliberately: OpenBao 2.7 moves the KMS auto-unseal mechanisms into external plugins, so a blind bump would break the `awskms` and `gcpckms` seal modes. * `openbao.resources` — CPU and memory for the server container. The defaults run a single node comfortably; raise `maxMemory` for large secret sets or heavy PKI use. ### Seal and Auto-unseal `seal.type` selects exactly one mode, and only that mode's sub-block is used: | Mode | How the barrier is unwrapped | What you must provide | | ------------------ | --------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | | `static` (default) | From a 32-byte key held in a Control Plane opaque secret | The prerequisite secret, named in `seal.static.secretName` | | `awskms` | Keyless AWS KMS encrypt/decrypt through a Control Plane cloud account | KMS key, cloud account, and a scoped IAM policy — see [AWS KMS setup](#auto-unseal-setup-kms-modes) | | `gcpckms` | Keyless GCP Cloud KMS encrypt/decrypt through a Control Plane cloud account | Key ring, crypto key, cloud account, and a post-install key grant — see [GCP Cloud KMS setup](#auto-unseal-setup-kms-modes) | In every mode the server unseals itself on boot with no operator action. In the KMS modes there is no static credential anywhere: the container is issued no cloud access keys, and no unseal key is mounted. **Do not change `seal.type` after the server has been initialized.** The seal wraps the existing data, so switching modes requires an OpenBao seal migration — it is not a values change. ### Storage * `volumeset.capacity` — Initial volume size in GiB (minimum 10). This volume holds the raft store: every secret, auth mount, policy, and the cluster state. Data survives restarts, redeployments, and `helm upgrade`. Uninstalling deletes the volume set (a final snapshot is kept for 7 days), so a reinstall comes up uninitialized. ### Access * `publicAccess.enabled` — `false` by default. When set to `true`, the API and web UI are served over HTTPS on the automatically assigned `*.cpln.app` canonical endpoint. While it is `false`, external requests are rejected at the edge. * `internalAccess.type` — Internal firewall scope of the server workload: | Type | Description | | --------------- | -------------------------------------------------------------------------------------------------------------------- | | `none` | No internal access. | | `same-gvc` | Allow access from all workloads in the same GVC (default). | | `same-org` | Allow access from all workloads in the same organization. | | `workload-list` | Allow access only from workloads listed in `internalAccess.workloads`, e.g. `//gvc/GVC_NAME/workload/WORKLOAD_NAME`. | ## Auto-unseal Setup (KMS Modes) Skip this section if you are using the default `static` seal. Access is keyless: the workload identity federates into your AWS account through a Control Plane cloud account, so no credentials are stored in the container or in your values. Set `seal.aws.kmsKeyId` to the key ARN (an ID or alias also works) and `seal.aws.region` to the key's region. If you do not have one, [create an AWS Cloud Account](https://docs.controlplane.com/guides/create-cloud-account). Set `seal.aws.cloudAccountName` to its name. Create an IAM policy with the JSON below, replacing the resource with your key ARN, and set `seal.aws.policyName` to the policy name. ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "kms:Encrypt", "kms:Decrypt", "kms:DescribeKey" ], "Resource": "arn:aws:kms:us-east-1:111111111111:key/YOUR_KEY_ID" } ] } ``` No post-install step is needed — the identity carries the policy from the moment it is created, and the server boots ready (uninitialized) as usual. GCP access is keyless as well, but the key grant is applied **after** install, because the service account name only exists once the identity has been created. Create a symmetric crypto key in Cloud KMS and fill in `seal.gcp.project`, `seal.gcp.region` (the key ring location, e.g. `us-east1`), `seal.gcp.keyRing`, and `seal.gcp.cryptoKey`. If you do not have one, [create a GCP Cloud Account](https://docs.controlplane.com/guides/create-cloud-account). Set `seal.gcp.cloudAccountName` to its name. Read the generated service account name from the identity, then grant both required roles on the crypto key: ```bash theme={null} cpln identity get {release}-openbao-identity --gvc {gvc} -o json | jq -r '.status.objectName' # the service account is {objectName}@{project}.iam.gserviceaccount.com SA={objectName}@{project}.iam.gserviceaccount.com for ROLE in roles/cloudkms.cryptoKeyEncrypterDecrypter roles/cloudkms.viewer; do gcloud kms keys add-iam-policy-binding {cryptoKey} \ --keyring {keyRing} --location {region} --project {project} \ --member "serviceAccount:$SA" --role "$ROLE" done ``` Both roles are required: `cryptoKeyEncrypterDecrypter` performs the seal and unseal, and `cloudkms.viewer` supplies the `cloudkms.cryptoKeys.get` call OpenBao makes at startup. Grant them on the key itself, never project-wide. **Until the grant exists, the workload crash-loops** with `PermissionDenied … cloudkms.cryptoKeys.get`. This is expected before the grant, and it heals itself roughly 30 seconds afterwards — no redeploy or reinstall needed. To confirm the identity reached GCP, check `status.gcp.usable` in `cpln identity get {release}-openbao-identity --gvc {gvc} -o yaml`. ## Initializing OpenBao The server boots reachable but **uninitialized** — that is the expected state right after install. Initialize it exactly once: ```bash theme={null} cpln workload exec {release}-openbao --gvc {gvc} --container openbao -- bao operator init ``` **Save the printed recovery keys and initial root token immediately.** They are shown once, only in your terminal, and are stored nowhere — the template never sees them and cannot recover them. From then on every restart auto-unseals with zero manual steps; a replaced replica is back unsealed and serving in roughly two minutes. Log in with `bao login ` or through the web UI, create scoped auth (for example userpass or OIDC), then revoke the root token per standard practice. The `bao` CLI is present in the container with `BAO_ADDR` preset, so any `cpln workload exec … -- bao …` command works without extra flags — for example `bao status` to check the seal state. ## Connecting | Target | Address | Credentials | | ---------------------------- | ------------------------------------------------ | --------------------------- | | Internal (same GVC) | `http://{release}-openbao.{gvc}.cpln.local:8200` | OpenBao token / auth method | | Public API + UI (if enabled) | `https://.cpln.app` (UI at `/ui/`) | OpenBao token / auth method | | Health | `GET /v1/sys/health` | none | External clients use `https://` — the platform edge terminates TLS. Same-GVC clients use plain `http://` over the mesh's mTLS. The canonical hostname appears under `status.canonicalEndpoint` in `cpln workload get {release}-openbao --gvc {gvc} -o yaml`. Because the API is Vault-compatible, existing Vault clients, SDKs, and the `vault` CLI can talk to this endpoint by pointing their address at it. ## Important Notes * **Static mode: the unseal-key secret must exist before install, and it is write-once.** A missing secret wedges the deployment; a lost or changed key makes all stored data unrecoverable. * **Run `bao operator init` once after install and save the output** — the recovery keys and root token are printed a single time, to your terminal only. * **Do not switch `seal.type` after initialization** — changing modes requires an OpenBao seal migration, not a values change. * **`gcpckms` needs its key grant applied after install** — the workload crash-loops on `cloudkms.cryptoKeys.get` until both roles are granted on the key, then recovers on its own. * **Private by default** — set `publicAccess.enabled: true` to expose the API and web UI on the canonical endpoint. * **Data survives restarts and upgrades; uninstall deletes the volume set** (a final snapshot is kept for 7 days), and a reinstall starts uninitialized. * **Uninstall does not delete your unseal-key secret** — it is your resource, created outside the release, and it stays until you remove it. * **The template deploys a single node** — raft runs with one member, so plan maintenance windows around the short restart gap rather than expecting failover. ## External References Official OpenBao documentation Reference for the static, awskms, and gcpckms seal stanzas The integrated storage backend this template uses The one-time initialization command and its options Source, releases, and issue tracker View the source files, default values, and chart definition # OpenSearch Source: https://docs.controlplane.com/template-catalog/templates/opensearch Deploy OpenSearch on Control Plane using the Template Catalog. Covers configuration, volumes, scaling, and distributed search clustering with optional Dashboards and S3/GCS backups. ## Overview OpenSearch is an open-source distributed search and analytics engine. This template deploys a production-ready OpenSearch cluster with automated node configuration, an optional Dashboards visualization UI, an optional demo log pipeline, and optional scheduled snapshot backups to AWS S3 or GCS. ### What Gets Created * **Stateful Workload** — An OpenSearch cluster with a configurable number of replicas (must be odd). Installs S3 or GCS repository plugins at startup when backup is enabled. * **Volume Set** — Persistent storage per replica with optional autoscaling. * **Secret** — An opaque startup script secret that generates the OpenSearch node configuration and starts the process. * **Identity & Policy** — An identity bound to the OpenSearch workload with `reveal` access to the startup script secret. When backup is enabled, the identity also grants cloud storage access to the cluster. * **OpenSearch Dashboards Workload** (optional) — A web-based visualization UI for logs and data. Enabled when `dashboard.enabled: true`. Not exposed externally — access via `cpln port-forward`. * **Demo Logs Workload** (optional) — A Python log generator with a Fluent Bit sidecar that ships sample logs to OpenSearch, plus a one-time setup job that creates index templates and dashboard patterns. Creates an additional Fluent Bit config secret with its own identity and policy. Enabled when `demoLogs.enabled: true`. * **Backup Setup Workload** (optional) — A one-time job that registers the snapshot repository and creates an automated snapshot policy via the OpenSearch API. Enabled when `backup.enabled: true`. This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites Prerequisites are only required if you plan to enable automated backups (`backup.enabled: true`). Skip this section if backups are not needed. ### AWS S3 1. Create an S3 bucket. Set `backup.aws.bucket` and `backup.aws.region` in your values file. 2. If you do not have a Control Plane Cloud Account set up, follow the [Create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) guide. Set `backup.aws.cloudAccountName` to the name of your Cloud Account. 3. Create an IAM policy with the following JSON, replacing `YOUR_BUCKET_NAME`: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket", "s3:GetObjectVersion", "s3:DeleteObjectVersion" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET_NAME/*" ] } ] } ``` 4. Set `backup.aws.policyName` to the name of the policy created in step 3. ### GCS 1. Create a GCS bucket. Set `backup.gcp.bucket` in your values file. 2. If you do not have a Control Plane Cloud Account set up, follow the [Create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) guide. Set `backup.gcp.cloudAccountName` to the name of your Cloud Account. 3. Add the **Storage Admin** role to the GCP service account associated with the Cloud Account. ## Installation To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} image: opensearchproject/opensearch:3.4.0 replicas: 3 # Must be odd clusterName: my-opensearch-cluster resources: minCpu: 500m minMemory: 2Gi maxCpu: 1 maxMemory: 4Gi volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) autoscaling: enabled: false maxCapacity: 100 # Maximum capacity in GiB minFreePercentage: 10 # Trigger scaling when free space drops below this percentage scalingFactor: 1.2 # Multiply current capacity by this factor when scaling up internal_access: type: same-gvc # options: same-gvc, same-org, workload-list workloads: # Note: can only be used if type is same-gvc or workload-list #- //gvc/GVC_NAME/workload/WORKLOAD_NAME # Dashboard Configuration (recommended) dashboard: enabled: true image: opensearchproject/opensearch-dashboards:3.4.0 resources: cpu: 100m memory: 512Mi # Demo Logs Configuration (optional) demoLogs: enabled: false remove_setup_workload: false # Set to true after setup completes to reduce resource usage # Backup Configuration (optional) backup: enabled: false remove_setup_workload: false # Set to true after setup completes to reduce resource usage provider: aws # Options: aws or gcp schedule: "0 2 * * *" # Daily at 2am UTC retention: maxAge: 30d # Delete snapshots older than 30 days maxCount: 30 # Keep maximum 30 snapshots aws: bucket: my-s3-bucket region: us-east-1 prefix: opensearch-snapshots cloudAccountName: my-cloud-account policyName: my-backup-policy gcp: bucket: my-gcs-bucket prefix: opensearch-snapshots cloudAccountName: my-cloud-account ``` ### Cluster * `replicas` — Number of OpenSearch nodes. Must be an odd number (3, 5, 7) to ensure quorum. * `clusterName` — Name used for internal cluster coordination. Use a minimum of 3 replicas for high availability. For production workloads, plan resources based on log volume: 1 CPU / 4Gi handles 10–50 GB/day; scale to 2 CPU / 8Gi for 50–100 GB/day. ### Resources * `resources.minCpu` / `resources.minMemory` — Minimum CPU and memory guaranteed per node. * `resources.maxCpu` / `resources.maxMemory` — Maximum CPU and memory per node. ### Storage * `volumeset.capacity` — Initial volume size per node in GiB (minimum 10). * `volumeset.autoscaling.enabled` — Automatically expand each volume as it fills. When enabled: * `maxCapacity` — Maximum volume size in GiB. * `minFreePercentage` — Trigger a scale-up when free space drops below this percentage. * `scalingFactor` — Multiply the current capacity by this factor when scaling up. ### Internal Access * `internal_access.type` — Controls which workloads can connect to OpenSearch on port `9200`: | Type | Description | | --------------- | --------------------------------------------------------------- | | `same-gvc` | Allow access from all workloads in the same GVC | | `same-org` | Allow access from all workloads in the same organization | | `workload-list` | Allow access only from specific workloads listed in `workloads` | External access to OpenSearch is blocked by default. The Dashboards UI is also not exposed externally — use `cpln port-forward` to access it. ### OpenSearch Dashboards * `dashboard.enabled` — When `true`, deploys an OpenSearch Dashboards workload for log visualization and search. Recommended for most deployments. * `dashboard.resources.cpu` / `dashboard.resources.memory` — CPU and memory for the Dashboards workload. To access the Dashboards UI, use port-forwarding: ```bash theme={null} cpln port-forward WORKLOAD_NAME --location LOCATION --org ORG_NAME 5601:5601 ``` Then open `http://localhost:5601` in your browser. ### Demo Logs * `demoLogs.enabled` — When `true`, deploys a sample log generator with a Fluent Bit sidecar that ships logs to OpenSearch, along with a setup job that creates the index template and dashboard pattern. Useful for testing the pipeline end-to-end. * `demoLogs.remove_setup_workload` — Set to `true` after the setup job completes (\~1–2 minutes) to remove the one-time setup workload and reduce resource usage. The log pipeline continues running. ### Backup Set `backup.enabled: true` to enable automated OpenSearch snapshot backups. The backup setup workload registers the snapshot repository and creates the snapshot policy via the OpenSearch API. Once it completes successfully, set `backup.remove_setup_workload: true` and run `cpln helm upgrade` to remove it — snapshots will continue on schedule. * `backup.provider` — `aws` or `gcp`. * `backup.schedule` — Cron schedule for automated snapshots (UTC). * `backup.retention.maxAge` — Delete snapshots older than this duration. * `backup.retention.maxCount` — Maximum number of snapshots to retain. Set `backup.provider` to `aws` or `gcp` and fill in the corresponding provider section. ### Restoring a Snapshot Restore snapshots from any workload that can reach the cluster on port `9200`. **Restore to an empty cluster:** ```bash theme={null} curl -X POST "http://WORKLOAD_NAME:9200/_snapshot/backup-repo/SNAPSHOT_NAME/_restore" \ -H 'Content-Type: application/json' \ -d '{ "indices": "*", "ignore_unavailable": true, "include_global_state": false }' ``` **Restore to an existing cluster (close indices first):** ```bash theme={null} curl -X POST "http://WORKLOAD_NAME:9200/_all/_close" curl -X POST "http://WORKLOAD_NAME:9200/_snapshot/backup-repo/SNAPSHOT_NAME/_restore" \ -H 'Content-Type: application/json' \ -d '{ "indices": "*", "ignore_unavailable": true, "include_global_state": false }' curl -X POST "http://WORKLOAD_NAME:9200/_all/_open" ``` **List available snapshots:** ```bash theme={null} curl "http://WORKLOAD_NAME:9200/_snapshot/backup-repo/_all?pretty" ``` ### Connecting to OpenSearch Once deployed, connect to the cluster from within the same GVC using: ```text theme={null} http://RELEASE_NAME-opensearch.GVC_NAME.cpln.local:9200 ``` ## External References Official OpenSearch documentation OpenSearch Dashboards documentation OpenSearch snapshot and backup guide View the source files, default values, and chart definition # OpenTelemetry Collector Source: https://docs.controlplane.com/template-catalog/templates/otel-collector Deploy the OpenTelemetry Collector on Control Plane using the Template Catalog. OTLP trace and metrics ingestion feeding Control Plane tracing and any Prometheus-remote-write store, with optional bearer-token or mTLS authenticated public endpoints. ## Overview The OpenTelemetry Collector receives, processes, and exports telemetry over OTLP. This template deploys a stateless collector that feeds Control Plane's native tracing, and can additionally ingest OTLP metrics and push them to any Prometheus-remote-write-compatible store — such as this catalog's [Prometheus template](/template-catalog/templates/prometheus) — with optional authenticated public ingestion (bearer token or mTLS). ### Architecture * **Collector** — A standard, stateless workload. `replicas` copies run behind one endpoint; `2` or more forms an HA ingestion pool that keeps accepting telemetry through rolling restarts and replica loss. * **Traces** — Applications send OTLP to the collector; it normalizes spans, derives span metrics, and forwards traces to the Control Plane tracing backend. * **Metrics** *(optional)* — An OTLP-to-`prometheus_remote_write` pipeline pushes ingested metrics to the store you configure. * **Public ingestion** *(optional)* — Bearer-token auth over the canonical HTTPS endpoint, or mutual TLS over a direct TCP load balancer. Public access always requires auth plus an explicit CIDR allowlist. ### What Gets Created * **Standard Collector Workload** — Runs the collector in either a simple (knob-driven) or advanced (full custom config) mode. * **Secret** — An opaque secret holding the rendered collector configuration, mounted as a file. * **Identity & Policy** — An identity bound to the workload with `reveal` access to the config secret and — only when auth is enabled — the auth secret you name. * **Direct Load Balancer** *(optional)* — TCP passthrough on ports `4317`/`4318`, created only for public mTLS ingestion. The workload, secret, identity and policy each carry a description explaining its role in the console (the direct load balancer is a field on the workload, not a resource of its own). The workload's description follows the mode: in `simple` mode it names the ports the collector binds, and in `advanced` mode it says the configuration is user-supplied and does not assert the ingestion ports, since those are then yours to choose. This template does not create a GVC. You will need to deploy it into an existing GVC and enable tracing at the GVC level after installing, specifying the target workload and port. This will trigger a restart of all workloads in the GVC. ## Prerequisites None for a default install. The two auth modes have prerequisites: * **Bearer auth** (`auth.method: bearer`) — an opaque secret holding the token, created **before** install (a missing secret leaves the deployment waiting on it). Set `auth.bearer.secretName` to its name: ```bash theme={null} openssl rand -hex 32 | tr -d '\n' | cpln secret create-opaque --name my-otel-ingest-token --encoding plain -f - ``` * **mTLS auth** (`auth.method: mtls`) — a dictionary secret created **before** install with exactly the keys `cert` (server certificate), `key` (server private key), and `ca` (the CA that signed your client certificates). Set `auth.mtls.secretName` to its name: ```yaml theme={null} kind: secret name: my-otel-mtls-certs type: dictionary data: cert: |- -----BEGIN CERTIFICATE----- ... key: |- -----BEGIN PRIVATE KEY----- ... ca: |- -----BEGIN CERTIFICATE----- ... ``` Install the template using your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `otelCollector.mode` changed from `advanced` to `simple` in template version 1.1.0. If you customized `advanced.config` while relying on the old default, set `mode: advanced` explicitly when upgrading. The default `values.yaml` for this template: ```yaml theme={null} # IMPORTANT: # - Traces: after installing, enable tracing at the GVC level (triggers a restart of all workloads in the GVC). # - Metrics: set metrics.enabled=true and point remoteWrite.endpoint at any Prometheus-remote-write-compatible store. otelCollector: image: otel/opentelemetry-collector-contrib:0.157.0 mode: simple # simple: structured config from the knobs below | advanced: advanced.config is used verbatim replicas: 1 # stateless; 2+ runs an HA ingestion pool behind the same endpoint resources: cpu: 200m memory: 256Mi # Simple Mode simple: processors: transform: traceStatements: # Normalizes dynamic span URLs to a fixed value. Substitute PLACEHOLDER, e.g. 'user/profile' - replace_pattern(span.attributes["http.url"], "^.*(PLACEHOLDER).*$", "/PLACEHOLDER") spanmetrics: histogram: # Span-duration histogram buckets — tune to your SLO thresholds # Buckets MUST be duration strings ("250ms", "1s"). Bare numbers are # parsed as NANOSECONDS by the collector, silently breaking the histogram. buckets: ["1ms", "5ms", "10ms", "25ms", "50ms", "100ms", "250ms", "500ms", "1s", "2.5s", "5s"] unit: ms # ms or s (output unit of the recorded histogram) # Advanced Mode — full collector config, used verbatim when mode: advanced. # Public ingestion contract in advanced mode: bind your authed receiver to # 0.0.0.0:4318 (HTTP) / 0.0.0.0:4319 (gRPC); keep health_check on 0.0.0.0:13133 for probes. advanced: config: | extensions: health_check: endpoint: 0.0.0.0:13133 pprof: endpoint: 0.0.0.0:8180 receivers: otlp: protocols: grpc: endpoint: 0.0.0.0:4317 http: endpoint: 0.0.0.0:4318 processors: batch: resource: attributes: - key: workload from_attribute: service.name action: insert transform: trace_statements: - context: span statements: - replace_pattern(span.attributes["http.url"], "^.*(PLACEHOLDER).*$", "/PLACEHOLDER") connectors: spanmetrics: dimensions: - name: http.url - name: http.method - name: http.status_code histogram: explicit: buckets: ["1ms", "5ms", "10ms", "25ms", "50ms", "100ms", "250ms", "500ms", "1s", "2.5s", "5s"] unit: ms exporters: otlp: endpoint: http://tracing.controlplane:80 tls: insecure: true prometheus: endpoint: 0.0.0.0:8889 service: pipelines: traces: receivers: [otlp] processors: [resource, transform, batch] exporters: [otlp, spanmetrics] metrics: receivers: [spanmetrics] processors: [batch] exporters: [prometheus] extensions: [pprof, health_check] telemetry: logs: level: INFO # ─── Metrics Ingestion (simple mode) ───────────────────────────────────────── metrics: enabled: false # adds an OTLP→remote-write metrics pipeline (simple mode; in advanced mode define it in advanced.config) remoteWrite: endpoint: http://YOUR_WORKLOAD.YOUR_GVC.cpln.local:9095/api/v1/write # any Prometheus-remote-write-compatible URL # ─── Ingestion Auth ────────────────────────────────────────────────────────── auth: method: none # none | bearer | mtls — must not be none when publicAccess is enabled bearer: secretName: "" # opaque secret holding the token (e.g. my-otel-ingest-token) — create BEFORE install mtls: secretName: "" # dictionary secret with keys cert, key, ca (server cert/key + client CA) — create BEFORE install # ─── Access ────────────────────────────────────────────────────────────────── publicAccess: enabled: false # public ingestion: bearer → canonical https endpoint; mtls → direct TCP 4317/4318 allowedCidrs: [] # REQUIRED when enabled — sender CIDRs, e.g. ["203.0.113.0/24"]; use ["0.0.0.0/0"] to explicitly allow all internalAccess: type: same-gvc # who inside the org can reach the collector internally: none | same-gvc | same-org ``` ### Mode and Replicas * `otelCollector.mode` — Set to `simple` (the default) for a structured configuration generated from the knobs below, or `advanced` to provide a fully customized configuration. In `advanced` mode, the entire `otelCollector.advanced.config` is passed to the collector verbatim. * `otelCollector.replicas` — The collector is stateless, so replicas need no coordination. Set `2` or more for an HA ingestion pool behind the same endpoint; ingestion keeps succeeding through rolling restarts and replica loss. ### Simple Mode * `simple.processors.transform.traceStatements` — A list of `replace_pattern` statements that normalize dynamic URL segments in `http.url` span attributes. Replace `PLACEHOLDER` with the path segment you want to collapse, e.g. `user/profile`. * `simple.spanmetrics.histogram.buckets` — Span-duration histogram bucket boundaries. Tune these to align with your SLO thresholds. * `simple.spanmetrics.histogram.unit` — Output unit of the recorded histogram. Choose `ms` (milliseconds) or `s` (seconds). Histogram buckets must be **duration strings** (`"250ms"`, `"1s"`). Bare numbers are parsed as **nanoseconds** by the collector, silently collapsing every span into the `+Inf` bucket. If you carried numeric buckets over from a 1.0.x install, convert them (e.g. `250` → `"250ms"`) when upgrading. ### Metrics Ingestion ```yaml theme={null} metrics: enabled: true # adds an OTLP → prometheus_remote_write pipeline (simple mode only) remoteWrite: endpoint: http://YOUR_WORKLOAD.YOUR_GVC.cpln.local:9095/api/v1/write # any Prometheus-remote-write-compatible URL ``` With `metrics.enabled: true`, OTLP metrics posted to the collector (internally, or publicly with auth) are pushed to the store at `metrics.remoteWrite.endpoint`. Any Prometheus-remote-write-compatible URL works — for example, this catalog's [Prometheus template](/template-catalog/templates/prometheus) receives remote write at `http://RELEASE_NAME-prometheus.GVC_NAME.cpln.local:9095/api/v1/write`, and a [Thanos](/template-catalog/templates/thanos) query tier on top gives you a global view of what lands there. One collector pushes to one store; run multiple installs for multiple targets. **Substitute both halves of the endpoint — the workload name *and* the GVC.** Since template version 1.2.2 a leftover `YOUR_WORKLOAD`, `YOUR_GVC` or `my-gvc.cpln.local` is rejected at install, in `metrics.remoteWrite.endpoint` when `metrics.enabled: true` (simple mode) or anywhere in `otelCollector.advanced.config` (advanced mode); the message names the placeholder it found and nothing is created, because the check runs before any resource is applied. A default install is unaffected — `metrics.enabled` is `false`, so the shipped placeholder endpoint installs as-is. The check exists because the alternative is worse. **An endpoint that does not resolve fails silently at the last hop:** the sender receives HTTP `200`, the collector accepts and batches the data, and then the exporter logs `Exporting failed. Dropping data.` with a `dropped_items` count and discards it. The workload stays `ready`, no status surface reports a problem, and Grafana shows an empty metric picker with no error. If metrics never arrive and nothing looks wrong, check the exporter endpoint first: ```bash theme={null} cpln logs '{gvc="GVC_NAME", workload="WORKLOAD_NAME"}' | grep -i 'dropping data' ``` A GVC genuinely named `my-gvc` also trips the check — a deliberate trade of one loud, legible rejection against a failure mode that drops every metric in silence. **Remote write also renames your metrics.** The exporter appends a unit and type suffix following the OTLP-to-Prometheus translation rules, so a metric your application emits as `my_metric` is stored as `my_metric_ratio`, `my_metric_seconds` and so on according to its OTLP unit, and a counter sent as `demo_counter` arrives as `demo_counter_total`. Query the suffixed name in Prometheus, not the one your app emits. This is the second thing that hides metrics after an unsubstituted endpoint, and the symptom is identical — you search the Grafana metric picker for the name your app sends and find nothing — so check it next when the endpoint turns out to be fine. ### Ingestion Auth ```yaml theme={null} auth: method: bearer # none | bearer | mtls — required (not none) for public ingestion bearer: secretName: my-otel-ingest-token # opaque secret created BEFORE install (see Prerequisites) mtls: secretName: my-otel-mtls-certs # dictionary secret (cert/key/ca) created BEFORE install ``` Auth applies to a dedicated `otlp/ingest` receiver on `4318` (HTTP) / `4319` (gRPC). The plain gRPC `:4317` receiver stays unauthenticated for the GVC tracing integration and is never exposed publicly. * **`bearer`** — Senders present `Authorization: Bearer `; requests without the token (or with the wrong one) are rejected with `401`. Public bearer ingestion runs over the canonical HTTPS endpoint and is **HTTP-only** — OTLP/gRPC does not pass the canonical endpoint; use mTLS for public gRPC. * **`mtls`** — The collector serves your certificate and requires a client certificate signed by your CA; the TLS handshake itself rejects unauthenticated clients. Public mTLS ingestion runs over a direct TCP load balancer on `4318` (HTTP) and `4317` (gRPC); the canonical `https://` endpoint intentionally stops accepting traffic in this mode. ### Access ```yaml theme={null} publicAccess: enabled: true # bearer → canonical https endpoint; mtls → direct TCP 4317/4318 allowedCidrs: ["203.0.113.0/24"] # REQUIRED when enabled; use ["0.0.0.0/0"] to explicitly allow all internalAccess: type: same-gvc # none | same-gvc | same-org ``` Public ingestion always requires auth: enabling `publicAccess` with `auth.method: none` or an empty `allowedCidrs` fails at install — opening to the world requires an explicit `["0.0.0.0/0"]`. `internalAccess.type` controls which workloads inside the org can reach the collector internally (`none`, `same-gvc`, or `same-org`). ### Advanced Mode The `advanced.config` block accepts a full [OpenTelemetry Collector configuration](https://opentelemetry.io/docs/collector/configuration/) and is fully authoritative. The shipped default config provides: | Component | Description | | ------------------------ | ------------------------------------------------------------------------------------ | | `receivers.otlp` | Accepts telemetry from your applications over gRPC on port `4317` and HTTP on `4318` | | `processors.resource` | Copies `service.name` into a `workload` attribute for easier filtering | | `processors.transform` | Normalizes dynamic URL segments in span attributes | | `connectors.spanmetrics` | Derives duration histograms and request metrics from trace spans | | `exporters.otlp` | Forwards traces to the Control Plane tracing backend | | `exporters.prometheus` | Exposes span-derived metrics for scraping on port `8889` | The `metrics.*` knobs are refused in advanced mode (the install fails rather than silently ignoring them). To ingest metrics, add the pipeline to your config yourself — substituting both the workload name and the GVC in the exporter endpoint, which is checked at install (see [Metrics Ingestion](#metrics-ingestion)): ```yaml theme={null} exporters: prometheus_remote_write: endpoint: http://YOUR_WORKLOAD.YOUR_GVC.cpln.local:9095/api/v1/write service: pipelines: metrics/otlp: receivers: [otlp] processors: [resource, batch] exporters: [prometheus_remote_write] ``` The `auth.*` and `publicAccess.*` knobs still wire the secret mounts, reveal policy, firewall, and load balancer in advanced mode, but your config must bind the authed receiver to `0.0.0.0:4318` (HTTP) / `0.0.0.0:4319` (gRPC) — certificate and token files are mounted at `/etc/otel-collector/tls/{server.crt,server.key,ca.crt}` and `/etc/otel-collector/auth/token`. Keep `health_check` on `0.0.0.0:13133` or readiness probes are skipped. ## Setting Up Tracing **1. Normalize URL patterns (recommended)** If your application has dynamic URL segments, use the `transform` processor to normalize them into fixed patterns. This keeps span cardinality manageable and makes your traces queryable. In either Simple or Advanced config, replace each occurrence of `PLACEHOLDER` with each path segment you want to collapse: ```yaml theme={null} transform: trace_statements: - context: span statements: # Static path - replace_pattern(span.attributes["http.url"], "^.*(/user/profile).*$", "/user/profile") # Wildcard path - replace_pattern(span.attributes["http.url"], "^.*(/user/settings/.*).*$", "/user/settings/.*") ``` Add one statement per pattern you want to normalize. **2. Install the OpenTelemetry SDK in your application** Your application must emit traces using the OpenTelemetry SDK. Install the SDK and configure it to export via OTLP/gRPC to: ``` http://..cpln.local:4317 ``` **3. Enable tracing at the GVC level** Navigate to the GVC where the OpenTelemetry Collector is installed and enable tracing. Set the target workload on port `4317`. This triggers a restart of all workloads in the GVC so they begin emitting traces to the collector. **4. Query span metrics in Grafana** Span metrics are exposed via Prometheus on port `8889` of the collector workload. Use the `spanmetrics` connector's output to build dashboards and alerts. The metric name suffix automatically changes based on the unit defined in your configuration: ``` traces_span_metrics_duration_milliseconds_bucket{http_url="/your-endpoint"} ``` If you changed the unit to `s` in the histogram, the metric name changes to: ``` traces_span_metrics_duration_seconds_bucket{http_url="/your-endpoint"} ``` Span-metric series first appear roughly 60–90 seconds after the first span — the spanmetrics connector flushes on a 60-second interval, and `:8889` serves an empty response until then. ## Connecting | Endpoint | Address | Auth | | ----------------------------------- | ------------------------------------------------------ | ----------------------------------------------------------------------------- | | In-GVC OTLP gRPC (traces + metrics) | `RELEASE_NAME.GVC_NAME.cpln.local:4317` | None (GVC tracing target) | | In-GVC OTLP HTTP | `http://RELEASE_NAME.GVC_NAME.cpln.local:4318` | None, or bearer when auth is on (in mTLS mode use plain gRPC `:4317` instead) | | Public OTLP HTTP (bearer) | `https://CANONICAL_ENDPOINT/v1/traces`, `/v1/metrics` | `Authorization: Bearer ` | | Public OTLP (mTLS) | `DIRECT_LB_ENDPOINT:4318` (HTTP), `:4317` (gRPC) | Client certificate signed by your CA | | Spanmetrics scrape | `http://RELEASE_NAME.GVC_NAME.cpln.local:8889/metrics` | None | The canonical endpoint (and the direct load balancer endpoint in mTLS mode) is in `status.canonicalEndpoint` of `cpln workload get RELEASE_NAME -o yaml`. The bearer token is whatever you stored in your prerequisite secret. Use the full `.cpln.local` hostname for in-GVC senders. ## Important Notes * **The default `mode` changed from `advanced` to `simple` in template version 1.1.0** — if you customized `advanced.config` while relying on the old default, set `mode: advanced` explicitly when upgrading. * **Histogram buckets must be duration strings** (`"250ms"`, `"1s"`) — bare numbers are parsed as nanoseconds, which silently breaks the histogram. * Enable tracing at the GVC level after install (target the collector workload, port `4317`); this restarts all workloads in the GVC. * Public ingestion requires auth: `publicAccess.enabled` with `auth.method: none` or an empty `allowedCidrs` fails at install — opening to the world requires an explicit `["0.0.0.0/0"]`. * Create the auth secret before installing — a missing secret leaves the deployment waiting on it. * mTLS uses the direct load balancer (raw TCP), not the canonical endpoint; in mTLS mode the canonical `https://` endpoint intentionally stops accepting traffic. * **In mTLS mode, in-GVC senders must use the plain internal gRPC port `:4317`** — the TLS-terminating ingest ports (`4318`/`4319`) are reachable only by external clients via the direct load balancer. * **Substitute both the workload name and the GVC in the remote-write endpoint.** A leftover placeholder is rejected at install since version 1.2.2, but the reason matters: an unresolvable endpoint drops metrics silently — the sender gets `200`, the collector logs `Exporting failed. Dropping data.`, and nothing else reports a problem. Diagnose with `cpln logs '{gvc="GVC_NAME", workload="WORKLOAD_NAME"}' | grep -i 'dropping data'`. * **Remote write appends a unit and type suffix to metric names.** A metric sent as `my_metric` is stored as `my_metric_ratio`, `my_metric_seconds` and so on following its OTLP unit, and a counter sent as `demo_counter` becomes `demo_counter_total` — query the suffixed name in Prometheus, not the one your app emits. Like an unsubstituted endpoint, this shows up as an empty Grafana metric picker. * One collector pushes to one remote-write store; run multiple installs for multiple targets. ## External References Official OpenTelemetry Collector documentation Language-specific SDK guides for instrumenting your application Reference for the spanmetrics connector configuration Reference for the prometheusremotewrite exporter The OpenTelemetry protocol specification View the source files, default values, and chart definition # PgDog Source: https://docs.controlplane.com/template-catalog/templates/pgdog Deploy PgDog on Control Plane — a high-performance PostgreSQL connection pooler, load balancer, and sharding proxy written in Rust. Sits transparently in front of any PostgreSQL deployment and provides transaction or session pooling, automatic read/write splitting, and multi-replica load balancing. ## Overview PgDog is a high-performance PostgreSQL connection pooler, load balancer, and sharding proxy written in Rust. It sits transparently in front of one or more PostgreSQL instances and appears to clients as a standard PostgreSQL server — no application code changes required, only a connection string update. PgDog parses SQL queries to automatically route writes (`INSERT`, `UPDATE`, `DELETE`, DDL) to a primary backend and distribute `SELECT` queries across replicas. Works with any PostgreSQL-compatible backend including the Control Plane [PostgreSQL](/template-catalog/templates/postgres) and [PostgreSQL Highly Available](/template-catalog/templates/postgres-highly-available) templates, or any external PostgreSQL endpoint. ### Architecture * **PgDog workload** — Stateless proxy that multiplexes client connections into a smaller pool of real backend connections. Scales horizontally; each replica maintains its own connection pool. * **pgdog.toml** — Main configuration rendered as a secret and mounted at startup. Defines backend databases, pool settings, timeouts, and load balancing strategy. * **users.toml** — Credentials configuration rendered as a separate secret. Defines which users can connect to PgDog and which backend databases they map to. ### What Gets Created * **Standard PgDog Workload** — Stateless proxy workload listening on port 6432. * **Identity & Policy** — Identity for the workload with access to credential secrets. * **Secrets** — Two opaque secrets: one for `pgdog.toml` (database and pooling config) and one for `users.toml` (user credentials). This template does not create a GVC or a PostgreSQL instance. Deploy it into an existing GVC and point it at an existing PostgreSQL backend. ## Prerequisites This template has no external prerequisites. To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} image: ghcr.io/pgdogdev/pgdog:v0.1.45 resources: cpu: 500m memory: 256Mi minCpu: 100m minMemory: 128Mi replicas: 1 pooling: mode: transaction # options: transaction, session defaultPoolSize: 10 # max server connections per pool minPoolSize: 1 # minimum idle connections kept open workers: 2 # async threads; recommend 2× vCPU count timeouts: connect: 5000 # time to establish a backend connection (ms) checkout: 5000 # max time a client waits for a free connection (ms) idle: 60000 # idle server connections closed after this (ms) query: 0 # per-query timeout; 0 = disabled loadBalancing: strategy: least_active_connections # options: random, round_robin, least_active_connections readWriteSplit: include_primary databases: - name: mydb host: postgres.example.com port: 5432 role: primary # options: primary, replica, auto users: - name: myuser password: mypassword database: mydb admin: database: admin user: admin password: changeme auth: type: scram logging: format: text # options: text, json, json_flattened level: info publicAccess: enabled: false # address: pgdog.example.com internalAccess: type: same-gvc # options: none, same-gvc, same-org, workload-list workloads: [] ``` ### Backend Databases The `databases` list defines the PostgreSQL backends PgDog proxies. Each entry maps to a `[[databases]]` block in `pgdog.toml`. Multiple entries sharing the same `name` form a cluster — PgDog routes writes to `primary` backends and distributes reads across `replica` backends. ```yaml theme={null} databases: - name: mydb host: my-postgres.my-gvc.cpln.local port: 5432 role: primary - name: mydb host: replica-1.my-patroni-postgres.aws-us-east-1.my-gvc.cpln.local port: 5432 role: replica - name: mydb host: replica-2.my-patroni-postgres.aws-us-east-1.my-gvc.cpln.local port: 5432 role: replica ``` | `role` | Behavior | | --------- | --------------------------------------------------------------------------- | | `primary` | Receives all write queries (`INSERT`, `UPDATE`, `DELETE`, DDL) | | `replica` | Receives read queries (`SELECT`) distributed by the load balancing strategy | | `auto` | PgDog detects the role via `pg_is_in_recovery()` at connection time | **Using with the PostgreSQL template** — Set `host` to `{release-name}-postgres.{gvc}.cpln.local`. **Using with the PostgreSQL Highly Available template** — Point the `primary` entry at the HA proxy (`{release-name}-postgres-ha-proxy.{gvc}.cpln.local`) and add `replica` entries using the replicaDirect hostnames (`replica-{n}.{release-name}-postgres-ha.{location}.{gvc}.cpln.local`). ### Users The `users` list defines which clients can connect to PgDog. Each entry maps to a `[[users]]` block in `users.toml`. The `database` field must match a `name` from the `databases` list. ```yaml theme={null} users: - name: myuser password: mypassword database: mydb ``` PgDog uses the `name` and `password` to authenticate incoming clients, then uses the same credentials to connect to the backend PostgreSQL server. ### Connection Pooling | Parameter | Default | Description | | ------------------------- | ------------- | ------------------------------------------------- | | `pooling.mode` | `transaction` | Pool mode: `transaction` or `session` | | `pooling.defaultPoolSize` | `10` | Maximum real Postgres connections per pool | | `pooling.minPoolSize` | `1` | Minimum idle connections kept open | | `pooling.workers` | `2` | Async threads; recommended value is 2× vCPU count | | Pool Mode | Description | | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `transaction` | Backend connection held only for the duration of a transaction, then returned to the pool. Best for most web and API workloads. Not compatible with `SET` variables, temporary tables, or advisory locks. | | `session` | Backend connection held for the entire client session. Compatible with all Postgres features. Increase `defaultPoolSize` to match expected concurrent client count. | ### Load Balancing | Strategy | Description | | -------------------------- | ----------------------------------------------------------------------------------------------- | | `least_active_connections` | Routes reads to the replica with the fewest active connections. Recommended for most workloads. | | `round_robin` | Distributes reads evenly across all replicas in rotation. | | `random` | Selects a replica at random for each read query. | `readWriteSplit: include_primary` allows the primary to also serve reads when no replicas are available. ### Timeouts All timeout values are in milliseconds. | Parameter | Default | Description | | ------------------- | ------- | --------------------------------------------------- | | `timeouts.connect` | `5000` | Time to establish a backend connection | | `timeouts.checkout` | `5000` | Max time a client waits for a free pool connection | | `timeouts.idle` | `60000` | Idle backend connections closed after this duration | | `timeouts.query` | `0` | Per-query timeout; `0` disables it | ### Admin Database PgDog exposes an internal admin database for stats and introspection. ```yaml theme={null} admin: database: admin user: admin password: changeme ``` Always set `admin.password` explicitly. If omitted, PgDog generates a random password at each startup, making the admin database inaccessible across restarts. Connect to the admin database with any PostgreSQL client: ```bash theme={null} PGPASSWORD= psql \ -h {release-name}-pgdog.{gvc}.cpln.local \ -p 6432 \ -U admin \ -d admin ``` ### Access * `internalAccess.type` — Controls which workloads can reach PgDog internally: `same-gvc`, `same-org`, `workload-list`, or `none`. * `publicAccess.enabled` — When `true`, Control Plane provisions a public TCP load balancer. The assigned hostname (e.g. `pgdog-name-hash.cpln.app:6432`) is visible in the Control Plane console or via `cpln workload get -o yaml`. * `publicAccess.address` — Optional custom domain to attach when public access is enabled. ### Scaling PgDog is stateless and scales horizontally by increasing `replicas`. Each replica maintains its own connection pool — scale `pooling.defaultPoolSize` down proportionally when adding replicas to avoid overloading the backend with too many open connections. ### Logging ```yaml theme={null} logging: format: text # options: text, json, json_flattened level: info # RUST_LOG syntax, e.g. info, debug, pgdog=debug ``` ## Connecting Applications connect to PgDog exactly as they would connect to PostgreSQL — PgDog implements the full PostgreSQL wire protocol. | Setting | Value | | -------- | ---------------------------------------------- | | Host | `{release-name}-pgdog.{gvc}.cpln.local` | | Port | `6432` | | Database | A `name` from your `databases` list | | Username | A `name` from your `users` list | | Password | The matching `password` from your `users` list | Example connection string: ``` postgresql://myuser:mypassword@{release-name}-pgdog.{gvc}.cpln.local:6432/mydb ``` ## Important Notes * **PgDog does not manage PostgreSQL** — it is a proxy only. Deploy a PostgreSQL backend separately before pointing PgDog at it. * **Port 6432, not 5432** — PgDog listens on port 6432. Update application connection strings accordingly. * **Transaction mode and session features** — If your application uses `SET` variables, prepared statements, temporary tables, or advisory locks, use `pooling.mode: session` instead of `transaction`. * **Admin password** — Always set `admin.password` explicitly. Omitting it causes PgDog to generate a random password at each restart, making the admin database unreachable across restarts. * **Scaling and pool sizing** — Each PgDog replica maintains its own pool. When scaling `replicas`, reduce `pooling.defaultPoolSize` proportionally to avoid opening too many total connections to the backend. ## External References Official PgDog configuration and architecture reference Source code and issue tracker Single-instance PostgreSQL template for use with PgDog HA PostgreSQL with Patroni — primary + replicas for PgDog read/write splitting # pgEdge Distributed PostgreSQL Source: https://docs.controlplane.com/template-catalog/templates/pgedge ## Overview pgEdge is an active-active distributed PostgreSQL cluster using Spock multi-master replication. Every node accepts both reads and writes simultaneously, and data written to any node replicates to all others automatically. The cluster spans multiple geographic locations with configurable replicas per location, providing a globally distributed, fault-tolerant database with no single point of failure. ### Architecture * **pgEdge** — Stateful workload running PostgreSQL 17 with the Spock extension. All nodes are active writers connected in a full-mesh replication ring. Each replica gets its own persistent volume. * **pgcat** — Connection pooler providing a single virtual endpoint for applications. Routes writes to the designated primary and distributes reads across all nodes. * **Spock** — Multi-master logical replication extension included in the pgEdge image. Handles cross-node replication with last-update-wins conflict resolution. ### What Gets Created * **Stateful pgEdge Workload** — PostgreSQL 17 with the Spock extension. One set of replicas per configured location, each with its own persistent volume. * **Standard pgcat Workload** — Connection pooler that routes application traffic to pgEdge nodes. Autoscales on RPS. * **Cron Backup Workload** *(optional)* — Runs `pg_dump` on a schedule and uploads the result to AWS S3 or GCS. Only active in the first configured location. * **Volume Set** — ext4 general-purpose SSD volumes with 7-day snapshots. One volume per replica. * **Identity & Policy** — An identity bound to the workloads with `reveal` permissions on all secrets. When backup is enabled, the identity is also configured with the appropriate cloud account and IAM policy. * **Secrets** — A dictionary secret holding database credentials, an opaque secret containing the pgcat TOML configuration, and an opaque secret containing the node startup script. * **GVC** — A GVC spanning all configured locations. ## Prerequisites * At least one Control Plane [location](/reference/location) to deploy into. * For backup: an AWS or GCP [cloud account](/guides/create-cloud-account) and a storage bucket. ### Installation To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} gvc: name: pgedge-gvc locations: # For replicas: use 1 for dev/testing, 3 for production - name: aws-us-west-2 replicas: 3 - name: aws-us-east-2 replicas: 3 - name: aws-eu-central-1 replicas: 3 image: ghcr.io/pgedge/pgedge-postgres:17-spock5-standard resources: minCpu: 500m minMemory: 1Gi maxCpu: 2 maxMemory: 4Gi postgres: username: postgres password: password database: mydb multiZone: false # Set to true to spread replicas across availability zones within each location volumeset: capacity: 10 # Initial capacity in GiB (minimum is 10) autoscaling: enabled: false # Set to true to enable autoscaling maxCapacity: 100 # Maximum capacity in GiB when autoscaling is enabled minFreePercentage: 10 # Minimum free percentage to trigger scaling scalingFactor: 1.2 # How much to scale up when triggered pgcat: image: ghcr.io/postgresml/pgcat:latest poolMode: transaction # options: session, transaction, statement defaultPoolSize: 25 # Real Postgres connections pgcat maintains per pool maxClientConn: 1000 # Maximum client connections pgcat accepts resources: cpu: 500m memory: 256Mi minReplicas: 2 maxReplicas: 4 internal_access: # Sets both pgedge and pgcat workloads type: same-gvc # options: same-gvc, same-org, workload-list workloads: # Only used when type is workload-list #- //gvc/GVC_NAME/workload/WORKLOAD_NAME backup: enabled: false image: controlplanecorporation/pg-backup:17.1.0 schedule: "0 2 * * *" # daily at 2am UTC resources: cpu: 100m memory: 128Mi provider: aws # Options: aws or gcp aws: bucket: my-backup-bucket region: us-east-1 cloudAccountName: my-backup-cloudaccount policyName: my-backup-policy prefix: pgedge/backups # folder where backups will be stored gcp: bucket: my-backup-bucket cloudAccountName: my-backup-cloudaccount prefix: pgedge/backups # folder where backups will be stored ``` ### GVC & Locations Each independent pgEdge deployment must use a unique `gvc.name`. Configure one or more locations, each with a replica count: | Environment | Replicas per location | | ------------- | --------------------- | | Dev / testing | 1 | | Production | 3 | Set `multiZone: true` to spread replicas across availability zones within each location. Verify your selected locations support multiple availability zones before enabling. ### Volume Set Set the initial storage capacity (minimum 10 GiB). Optionally enable autoscaling to expand as data grows: ```yaml theme={null} volumeset: capacity: 10 autoscaling: enabled: true maxCapacity: 100 minFreePercentage: 10 scalingFactor: 1.2 ``` ### Internal Access Control which workloads can connect to pgEdge and pgcat: ```yaml theme={null} internal_access: type: same-gvc # Options: same-gvc, same-org, workload-list workloads: # Only used when type is workload-list: #- //gvc/GVC_NAME/workload/WORKLOAD_NAME ``` * `same-gvc` — Allow access from all workloads in the same GVC. * `same-org` — Allow access from all workloads in the org. * `workload-list` — Allow access only from the specified workloads. ### pgcat Pool Modes pgcat multiplexes application connections into a smaller pool of real database connections, reducing overhead and protecting Postgres from connection exhaustion. | Mode | Behavior | | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `transaction` | Connection held only for the duration of a transaction. Best for most web and API workloads. Not compatible with `SET` variables, temporary tables, or advisory locks. | | `session` | Connection held for the entire client session. Compatible with all Postgres features but provides less connection reuse. | | `statement` | Connection returned after every statement. Transactions are not supported. Rarely used. | ## Connecting Connect through pgcat for all application traffic. Each application only needs this single endpoint — pgcat handles routing to the appropriate pgEdge node: ``` Host: {release-name}-pgcat.{gvc-name}.cpln.local Port: 5432 Database: {postgres.database} Username: {postgres.username} Password: {postgres.password} ``` ## Schema Changes (DDL) Spock replicates row-level changes (`INSERT`, `UPDATE`, `DELETE`) automatically. DDL (`CREATE TABLE`, `ALTER TABLE`, etc.) must be broadcast using `spock.replicate_ddl()` so it executes on all nodes. ### Creating a table Run on **one node only** — the DDL replicates to all nodes, then add the table to the replication set: ```sql theme={null} -- Step 1: Run on ONE node -- creates the table on all nodes SELECT spock.replicate_ddl('CREATE TABLE orders ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), amount numeric, created_at timestamptz DEFAULT now() );'); -- Step 2: Run on ONE node -- adds the table to the replication set on all nodes SELECT spock.repset_add_table('default', 'orders'::regclass); ``` Step 2 is required because Spock suppresses event triggers during replication apply to prevent loops. The `repset_add_table` call itself replicates to all other nodes automatically. ### Other DDL ```sql theme={null} SELECT spock.replicate_ddl('ALTER TABLE orders ADD COLUMN status text DEFAULT ''pending'';'); SELECT spock.replicate_ddl('DROP TABLE orders;'); ``` ### Primary Keys Use `uuid` primary keys instead of `serial`/`bigserial`. Each node maintains its own sequence, so auto-increment integers will collide when the same ID is generated on multiple nodes simultaneously: ```sql theme={null} -- Good: globally unique, no conflicts id uuid PRIMARY KEY DEFAULT gen_random_uuid() -- Avoid: causes duplicate key conflicts under concurrent multi-node writes id serial PRIMARY KEY ``` ## Backup When `backup.enabled` is `true`, a cron workload runs `pg_dump` on the configured schedule. Because every pgEdge node holds a full copy of the data, the backup job connects to `replica-0` of the first configured location. Backup runs only in the first location — other locations have the backup workload suspended. ### AWS S3 Prerequisites 1. Create your S3 bucket and note its name and region. 2. If you do not have a Cloud Account set up, refer to the docs to [Create a Cloud Account](/guides/create-cloud-account). Note the cloud account name. 3. Create a new AWS IAM policy with the following JSON (replace `YOUR_BUCKET_NAME`): ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket", "s3:GetObjectVersion", "s3:DeleteObjectVersion" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET_NAME", "arn:aws:s3:::YOUR_BUCKET_NAME/*" ] } ] } ``` 4. Set `backup.aws.cloudAccountName` to your cloud account name. 5. Set `backup.aws.policyName` to the policy created in step 3. ### GCS Prerequisites 1. Create your GCS bucket and note its name. 2. If you do not have a Cloud Account set up, refer to the docs to [Create a Cloud Account](/guides/create-cloud-account). Note the cloud account name. 3. Add the `Storage Admin` role to the GCP service account created by the cloud account. 4. Set `backup.gcp.cloudAccountName` to your cloud account name. ### Restoring a Backup **AWS S3:** ```sh theme={null} export PGPASSWORD="PASSWORD" aws s3 cp "s3://BUCKET_NAME/PREFIX/BACKUP_FILE.sql.gz" - \ | gunzip \ | psql \ --host=RELEASE_NAME-pgcat.GVC_NAME.cpln.local \ --port=5432 \ --username=USERNAME \ --dbname=DATABASE unset PGPASSWORD ``` **GCS:** ```sh theme={null} export PGPASSWORD="PASSWORD" gsutil cp "gs://BUCKET_NAME/PREFIX/BACKUP_FILE.sql.gz" - \ | gunzip \ | psql \ --host=RELEASE_NAME-pgcat.GVC_NAME.cpln.local \ --port=5432 \ --username=USERNAME \ --dbname=DATABASE unset PGPASSWORD ``` ## Important Notes * **GVC naming** — Each independent pgEdge deployment must use a unique GVC name. * **Minimum replicas** — Use at least 3 replicas per location for production to survive a node loss within a location. * **Conflict resolution** — Concurrent writes to the same row from different nodes are resolved by last-update-wins based on commit timestamp. For workloads requiring stronger consistency, route writes for a given entity to a single node using application-level logic. * **multiZone** — Verify your selected locations support multiple availability zones before enabling. ## External References Official pgEdge documentation Spock multi-master replication extension pgcat connection pooler View the source files, default values, and chart definition # Polaris Source: https://docs.controlplane.com/template-catalog/templates/polaris Deploy Apache Polaris on Control Plane using the Template Catalog. An Apache Iceberg REST catalog backed by PostgreSQL that lets Trino, Spark and any Iceberg client read and write tables in an S3-compatible bucket. Covers prerequisite secrets, catalog creation, and the metastore modes. ## Overview Apache Polaris is an [Apache Iceberg](https://iceberg.apache.org/) **REST catalog** — the service that tells a query engine which tables exist, where their metadata lives in object storage, and who may read them. It is the piece that turns a bucket of Parquet files into a lakehouse. This template completes a stack the catalog already has the other two thirds of: **[seaweedfs](/template-catalog/templates/seaweedfs) (or any S3-compatible bucket) stores the data, Polaris is the catalog, and [trino](/template-catalog/templates/trino) runs the queries** — all wired over internal GVC DNS, with no data leaving your organization. That exact path was verified end to end: Trino created schemas and tables through Polaris, inserted and read rows back, and the resulting Parquet data files and Iceberg metadata were confirmed in the SeaweedFS bucket from SeaweedFS's own side. See [Building a Lakehouse](#building-a-lakehouse). Polaris itself is stateless. Every catalog, namespace, table pointer, principal and grant lives in a PostgreSQL metastore that the template deploys for you, so a `replicas` knob scales the catalog horizontally with no coordination to configure. ### Architecture * **Polaris server** — A `standard` workload serving the Iceberg REST API and the Polaris management API on port `8181`, and Quarkus health and metrics on port `8182`. Stateless, with a `replicas` knob. * **Bootstrap workload** — Runs the official `polaris-admin-tool` image once to create the realm schema and the root principal, then idles. Always one replica, and idempotent — a restart or upgrade re-runs it harmlessly. * **Metastore** — A single-instance [postgres](/template-catalog/templates/postgres) by default, or [postgres-highly-available](/template-catalog/templates/postgres-highly-available) with one flag. * **Realm** — Polaris isolates tenants by realm. This template ships exactly one and does not require the `Polaris-Realm` header, which is what makes Trino's Iceberg REST connector work against it. ### What Gets Created * **Standard Polaris Workload** — `replicas` interchangeable replicas serving `8181` and `8182`. * **Standard Bootstrap Workload** — One replica, no ports, unreachable from anywhere. It initializes the realm and then sleeps. * **Bootstrap Script Secret** — An [opaque secret](/guides/create-secret/opaque) holding the shell script the bootstrap workload runs, mounted as a file. * **Two Identities & Two Policies** — Split for least privilege: only the bootstrap principal can reveal your root credentials, and only the server principal can reveal the token signing key and the object-storage credentials. * **PostgreSQL Metastore** — The `postgres` or `postgres-highly-available` subchart, with its own workload, volume set and config secret. * **No Volume Set for Polaris** — The server writes nothing to local disk that must survive a restart. This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites **Two Control Plane secrets must exist before you install.** They are referenced by name, never passed through values, and the deployment waits indefinitely on a secret that does not exist — an install that looks stuck is almost always a missing one of these. A [dictionary secret](/guides/create-secret/dictionary) with `CLIENT_ID` and `CLIENT_SECRET`. These become the realm's root principal at bootstrap, and are what Trino, Spark and any other Iceberg REST client authenticate with. Neither value may contain a comma. ```bash theme={null} cpln secret create-dictionary --name my-polaris-root-credentials \ --entry CLIENT_ID=root --entry CLIENT_SECRET="$(openssl rand -hex 24)" ``` An [opaque secret](/guides/create-secret/opaque) whose payload is a random string of 32 or more characters. Every replica signs and validates access tokens with it, so tokens survive restarts and are accepted across replicas. ```bash theme={null} printf '%s' "$(openssl rand -hex 32)" | \ cpln secret create-opaque --name my-polaris-signing-key --encoding plain -f - ``` Both secrets are yours, not the release's — `helm uninstall` leaves them in place. Optional: * **Object-storage credentials** for the bucket holding your Iceberg data — a dictionary secret with `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`. See [Object Storage](#object-storage). Polaris runs without it; catalogs can be added later. * **A cloud account and bucket** only if you turn on the metastore's backup pass-through. See [Backing Up](#backing-up). ## Installation Install the template using your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs **A default install takes about 90 seconds, and part of it looks like a failure.** While PostgreSQL is still starting, the Polaris server restarts a couple of times logging `Failed to start quarkus`, and the bootstrap workload logs `attempt failed (metastore not ready yet?) - retrying in 10s`. Both self-heal: the measured warm-up was **2 restarts within 32 seconds**, after which the server started cleanly. Do not interrupt it. On the `postgresHA` metastore the same sequence takes about **6 minutes** (measured 348 seconds), because Patroni and etcd have to converge first. ## Configuration The default `values.yaml` for this template: ```yaml theme={null} image: apache/polaris:1.7.0 # Stateless replicas behind one metastore. All replicas share the token signing # key below, so a token minted by one is accepted by every other. replicas: 1 resources: minCpu: 500m maxCpu: 1000m minMemory: 1Gi maxMemory: 2Gi # JVM heap is a percentage OF THIS — see jvm.maxRAMPercentage jvm: maxRAMPercentage: 70 # max heap as a percentage of resources.maxMemory; allowed range 40–80 # Polaris isolates tenants by realm. PERMANENT after first install. realm: POLARIS rootCredentials: secretName: my-polaris-root-credentials # dictionary secret with CLIENT_ID and CLIENT_SECRET tokenSigningKey: secretName: my-polaris-signing-key # opaque secret (encoding: plain), payload = 32+ random chars # Credentials Polaris uses for its OWN metadata I/O against S3-compatible storage. storage: credentialsSecretName: "" # e.g. my-seaweedfs-s3-credentials region: us-east-1 # AWS_REGION; S3-compatible servers ignore the value but the SDK requires one # Runs the official admin tool once against the metastore, then idles. bootstrap: image: apache/polaris-admin-tool:1.7.0 # keep this tag in lockstep with `image` resources: minCpu: 100m maxCpu: 400m minMemory: 128Mi maxMemory: 1Gi publicAccess: enabled: false # true = Iceberg REST + management API on the auto *.cpln.app HTTPS endpoint internalAccess: # who may reach Polaris from inside Control Plane type: same-gvc # options: none, same-gvc, same-org, workload-list workloads: [] # used with workload-list, e.g. //gvc/GVC_NAME/workload/WORKLOAD_NAME # ─── Metastore: single-instance PostgreSQL (default) ────────────── postgres: enabled: true config: username: polaris password: change-me-polaris-db # change before installing database: polaris resources: minCpu: 200m maxCpu: 500m minMemory: 256Mi maxMemory: 512Mi volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) backup: enabled: false # true = scheduled DB backups to object storage provider: aws # options: aws, gcp, minio # plus aws / gcp / minio blocks — see Backing Up # ─── Metastore: Highly Available PostgreSQL ─────────────────────── # set postgresHA.enabled: true (and postgres.enabled: false) for near-zero-downtime # upgrades and automatic failover postgresHA: enabled: false postgres: username: polaris password: change-me-polaris-db # change before installing database: polaris replicas: 3 volumeset: capacity: 10 # initial capacity in GiB per replica (minimum is 10) backup: enabled: false mode: logical # logical or wal-g provider: aws # options: aws, gcp, minio ``` ### Server and Replicas * `image` — The official upstream image. The chart is shipped and tested on Polaris 1.7.0. * `replicas` — Number of Polaris replicas (default `1`, minimum `1`). Replicas are interchangeable: they share the metastore and the token signing key, so a token minted by one is accepted by every other and a catalog created through one is immediately visible on the rest. Raise it for capacity and to keep serving through a rolling restart — see [Availability](#availability). * `resources.minCpu` / `maxCpu` / `minMemory` / `maxMemory` — CPU and memory per replica. `minMemory` may not exceed `maxMemory`, and `maxCpu:minCpu` may not exceed 4:1 (a Control Plane limit); the chart refuses to render otherwise, naming the value to fix. ### JVM and Memory `jvm.maxRAMPercentage` (default `70`, allowed range 40–80) sets the JVM heap as a percentage of `resources.maxMemory`, so `maxMemory` is normally the only number you change. Capacity AI is disabled on both workloads: the JVM sizes its heap from the container limit at startup, so shrinking the container afterwards would be an out-of-memory kill with no diagnostic. ### Realm `realm` (default `POLARIS`) is the tenant namespace the bootstrap workload creates. This template ships exactly one realm and does **not** require the `Polaris-Realm` header — a header-less request resolves to this realm, which is what Trino's Iceberg REST connector needs, since it cannot send an arbitrary header. Requests naming a different realm are rejected with `404`. **`realm` is permanent after the first install.** Renaming it bootstraps a new, empty realm and hides the existing catalogs — they are not deleted, and changing the name back makes them visible again, but nothing is migrated between realms. ### Credentials * `rootCredentials.secretName` — Name of the prerequisite dictionary secret holding `CLIENT_ID` and `CLIENT_SECRET`. Required. * `tokenSigningKey.secretName` — Name of the prerequisite opaque secret whose payload is the shared HMAC signing key. Required. The signing key is mounted unconditionally, not only when `replicas` is above 1: Polaris's own default generates a key per JVM process, so even a single replica would hand out tokens that its own restart rejects with `401`. **Root credentials are write-once.** They are applied when the realm is first bootstrapped; changing the secret afterwards has no effect on the existing realm. Rotate by creating a new principal through the management API instead. Rotating `tokenSigningKey` *does* take effect and invalidates every outstanding token — clients must request a new one. ### Object Storage * `storage.credentialsSecretName` — Name of a dictionary secret with `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`, used for Polaris's **own** Iceberg metadata reads and writes. Empty by default, which is a valid install: the server runs and answers, but catalogs backed by object storage cannot read or write. * `storage.region` — Value of `AWS_REGION`. S3-compatible servers ignore it, but the AWS SDK requires one. With [seaweedfs](/template-catalog/templates/seaweedfs) or [minio](/template-catalog/templates/minio) in the same GVC, point this at the *same* dictionary secret that deployment already uses (`s3.credentialsSecretName` for SeaweedFS) — there is nothing else to create. For AWS S3, create an access key scoped to the bucket: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["s3:ListBucket", "s3:GetBucketLocation"], "Resource": "arn:aws:s3:::my-polaris-bucket" }, { "Effect": "Allow", "Action": ["s3:PutObject", "s3:GetObject", "s3:DeleteObject"], "Resource": "arn:aws:s3:::my-polaris-bucket/*" } ] } ``` **This version does not vend STS credentials.** Polaris does not hand short-lived, scoped credentials to query engines, so **each engine needs its own object-storage credentials** in addition to these. Keep `iceberg.rest-catalog.vended-credentials-enabled=false` in Trino, and set `stsUnavailable: true` when you create a catalog on an S3-compatible server. ### Bootstrap `bootstrap.image` is the official admin tool image; keep its tag in lockstep with `image`. `bootstrap.resources` sizes the one-shot container. The bootstrap workload runs the admin tool against the metastore, creates the realm schema and the root principal, and then idles — it exits nothing and serves nothing, so it stays `ready` after its work is done. The operation is idempotent: on any restart or upgrade it logs `Realm 'POLARIS' is already bootstrapped; skipping` and idles again. Nothing calls it, and its internal firewall is set to `none`. ### Access * `publicAccess.enabled` — `false` by default. When `true`, the Iceberg REST API and the Polaris management API are served over HTTPS on the automatically assigned `*.cpln.app` canonical endpoint. * `internalAccess.type` — Which workloads inside Control Plane may reach Polaris: | Type | Description | | --------------- | -------------------------------------------------------------------------------------------------------------------- | | `none` | No internal access — this hides Polaris from query engines in the same GVC. | | `same-gvc` | Allow access from all workloads in the same GVC (default). | | `same-org` | Allow access from all workloads in the same organization, including other GVCs. | | `workload-list` | Allow access only from workloads listed in `internalAccess.workloads`, e.g. `//gvc/GVC_NAME/workload/WORKLOAD_NAME`. | **Port `8182` is never published on the public endpoint.** The canonical endpoint routes to the workload's first declared container port, which is `8181`. Polaris serves the unauthenticated Quarkus health, metrics and info endpoints only on `8182`, so with public access on, `/q/metrics`, `/q/health` and `/q/info` all return `404` from the internet while `:8182/q/metrics` answers normally inside the GVC. Firewall changes take up to a couple of minutes to propagate. ### Metastore Exactly one of `postgres` (default) and `postgresHA` must be enabled — the chart enforces this at render and fails with a message naming the fix. | | `postgres` (default) | `postgresHA` | | ----------------- | ---------------------------------------------------------------- | ---------------------------------------------------------- | | What runs | One single-replica PostgreSQL workload | 3× Patroni PostgreSQL, 3× etcd, an HAProxy leader endpoint | | Database failover | None — a node failure reschedules and reattaches the same volume | Automatic (Patroni leader election) | | Footprint | 1 workload | 8 replicas across 3 workloads | | Time to ready | \~90 s | \~6 min | Switch by setting `postgres.enabled: false` and `postgresHA.enabled: true`. Polaris is wired to whichever is active automatically — the single instance directly, or the HAProxy leader endpoint in HA mode. **Change the database password before installing** in either mode (`postgres.config.password` or `postgresHA.postgres.password`); the default is an obvious placeholder. Each Polaris replica opens up to 20 JDBC connections, plus the bootstrap workload — about 21 of PostgreSQL's default 100 at `replicas: 1`, and about 61 at `replicas: 3`. **Template version `1.0.0` did not compact the etcd cluster inside the bundled highly available metastore**, so etcd's backend grows with time alone and goes read-only once it reaches its 2 GiB quota — after roughly 110 days — taking PostgreSQL failover with it. Only installs running the HA metastore are affected (`postgresHA.enabled`, which is off by default here); see [etcd History Compaction](/template-catalog/templates/postgres-highly-available#etcd-history-compaction) for the mechanism and the symptoms. Upgrade to `1.0.1` or later to turn compaction on: that stops further growth but cannot shrink a backend that has already grown, and a cluster that has already raised a `NOSPACE` alarm needs operator recovery rather than an upgrade. ## Building a Lakehouse Storage, catalog, and engine are three separate templates. This is the whole path, verified end to end. Install [seaweedfs](/template-catalog/templates/seaweedfs) with a bucket for your warehouse (`s3.buckets: [lakehouse]`), or use [minio](/template-catalog/templates/minio) or AWS S3. Note the dictionary secret holding its `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY`. Set `storage.credentialsSecretName` to that same secret, and create the two prerequisite secrets from [Prerequisites](#prerequisites) first. Every API call needs a bearer token, obtained with the root credentials: ```bash theme={null} curl -X POST https://.cpln.app/api/catalog/v1/oauth/tokens \ --user "$CLIENT_ID:$CLIENT_SECRET" \ -d grant_type=client_credentials -d scope=PRINCIPAL_ROLE:ALL ``` Catalogs are a day-2 API call, not an install-time value: ```bash theme={null} curl -X POST https://.cpln.app/api/management/v1/catalogs \ -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \ -d '{ "catalog": { "name": "lakehouse", "type": "INTERNAL", "properties": { "default-base-location": "s3://my-bucket/warehouse" }, "storageConfigInfo": { "storageType": "S3", "allowedLocations": ["s3://my-bucket/warehouse"], "endpoint": "http://my-seaweedfs.my-gvc.cpln.local:8333", "endpointInternal": "http://my-seaweedfs.my-gvc.cpln.local:8333", "pathStyleAccess": true, "stsUnavailable": true, "region": "us-east-1" } } }' # Let the root principal administer the new catalog curl -X PUT https://.cpln.app/api/management/v1/principal-roles/service_admin/catalog-roles/lakehouse \ -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \ -d '{"catalogRole":{"name":"catalog_admin"}}' ``` `pathStyleAccess: true` and `stsUnavailable: true` are what an S3-compatible server needs. Drop `endpoint`, `endpointInternal` and `pathStyleAccess` for AWS S3 itself. Add this catalog to the [trino](/template-catalog/templates/trino) template's values. Trino authenticates to Polaris with the root credentials and talks to the bucket with its own S3 keys — credential vending is off, so both sides hold static credentials: ```yaml theme={null} catalogs: - name: iceberg properties: | connector.name=iceberg iceberg.catalog.type=rest iceberg.rest-catalog.uri=http://my-polaris.my-gvc.cpln.local:8181/api/catalog iceberg.rest-catalog.security=OAUTH2 iceberg.rest-catalog.oauth2.credential=${ENV:POLARIS_CLIENT_ID}:${ENV:POLARIS_CLIENT_SECRET} iceberg.rest-catalog.oauth2.scope=PRINCIPAL_ROLE:ALL iceberg.rest-catalog.warehouse=lakehouse iceberg.rest-catalog.vended-credentials-enabled=false fs.native-s3.enabled=true s3.endpoint=http://my-seaweedfs.my-gvc.cpln.local:8333 s3.region=us-east-1 s3.path-style-access=true s3.aws-access-key=${ENV:S3_ACCESS_KEY} s3.aws-secret-key=${ENV:S3_SECRET_KEY} secrets: - env: POLARIS_CLIENT_ID secretName: my-polaris-root-credentials secretKey: CLIENT_ID - env: POLARIS_CLIENT_SECRET secretName: my-polaris-root-credentials secretKey: CLIENT_SECRET - env: S3_ACCESS_KEY secretName: my-seaweedfs-s3-credentials secretKey: AWS_ACCESS_KEY_ID - env: S3_SECRET_KEY secretName: my-seaweedfs-s3-credentials secretKey: AWS_SECRET_ACCESS_KEY ``` `CREATE SCHEMA`, `CREATE TABLE`, `INSERT` and `SELECT` now work against tables stored in the bucket: ```sql theme={null} CREATE SCHEMA iceberg.demo; CREATE TABLE iceberg.demo.t (a integer, b varchar); INSERT INTO iceberg.demo.t VALUES (1,'alpha'), (2,'beta'), (3,'gamma'); SELECT count(*) FROM iceberg.demo.t; -- where the data actually landed SELECT file_path FROM iceberg.demo."t$files"; ``` Any other Iceberg REST client — Spark, PyIceberg, Flink — connects the same way: the REST URI, OAuth2 client credentials, and the warehouse name. ## Connecting | What | Value | | ----------------------- | -------------------------------------------------------------------------------------------------------------------------- | | Base URL (public) | `https://.cpln.app` — `status.canonicalEndpoint` of `{release}-polaris`. Available when `publicAccess.enabled`. | | Base URL (internal) | `http://{release}-polaris.{gvc}.cpln.local:8181` | | Iceberg REST API | `{base}/api/catalog` | | Polaris management API | `{base}/api/management/v1` | | OAuth2 token endpoint | `{base}/api/catalog/v1/oauth/tokens` | | Health and metrics | `http://{release}-polaris.{gvc}.cpln.local:8182/q/health` and `/q/metrics` — **in-GVC only** | | Credentials | `CLIENT_ID` / `CLIENT_SECRET` from your `rootCredentials` secret | | Metastore (single mode) | `{release}-postgres.{gvc}.cpln.local:5432`, credentials in the `{release}-pg-config` secret | | Metastore (HA mode) | `{release}-postgres-ha-proxy.{gvc}.cpln.local:5432`, credentials in the `{release}-postgres-config` secret | Same-GVC clients use plain `http://` over the mesh's mTLS; external clients use `https://` with TLS terminated at the platform edge. A request with no bearer token, or a wrong client secret, is rejected with `401`. ## Availability Polaris is stateless and every replica shares both the metastore and the token signing key, so `replicas` is a real availability knob. Measured at `replicas: 2` with an authenticated request every 0.5 s from another workload, through the service DNS name: | Event | Impact | | ------------------------------- | ---------------------------------------------------------------------------------------- | | Rolling upgrade (`replicas: 2`) | **403 of 403 requests returned HTTP 200** — zero non-2xx. The rollout converged in 95 s. | | Scaling down 2 → 1 | **384 of 384 requests returned HTTP 200** — zero non-2xx. | Catalog state survived both events. At the default `replicas: 1` a rolling upgrade replaces the only replica, so requests fail for the duration of the restart; raise `replicas` to keep serving through one. The metastore is the other half of the picture: with the default single-instance `postgres`, a database restart takes Polaris down with it for as long as PostgreSQL takes to come back. `postgresHA` removes that with Patroni failover, at the cost of a larger footprint and a slower first install. ## Backing Up Metastore backups are off by default and need no cloud account. Turn them on with `postgres.backup.enabled: true` (or `postgresHA.backup.enabled: true`) and configure a provider — the backup runs inside the PostgreSQL subchart, so this is that template's mechanism, passed straight through. Create the bucket, a Control Plane [cloud account](/guides/create-cloud-account), and an IAM policy scoped to the bucket: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["s3:ListBucket"], "Resource": "arn:aws:s3:::my-polaris-backup-bucket" }, { "Effect": "Allow", "Action": ["s3:PutObject","s3:GetObject","s3:DeleteObject"], "Resource": "arn:aws:s3:::my-polaris-backup-bucket/*" } ] } ``` Then set `provider: aws` and the `aws.bucket`, `aws.region`, `aws.cloudAccountName` and `aws.policyName` values. Create the bucket and a Control Plane [cloud account](/guides/create-cloud-account), grant its service account **Storage Object Admin** (`roles/storage.objectAdmin`) on the bucket, then set `provider: gcp` and the `gcp.bucket` and `gcp.cloudAccountName` values. No cloud account is needed — the keys authenticate directly. Set `provider: minio` and the `minio.endpoint`, `minio.bucket`, `minio.accessKey` and `minio.secretKey` values. A [seaweedfs](/template-catalog/templates/seaweedfs) or [minio](/template-catalog/templates/minio) deployment in the same GVC works as the target. In HA mode, `postgresHA.backup.mode` selects `logical` (scheduled `pg_dump`) or `wal-g` (continuous WAL archiving). ## Important Notes * **Create both prerequisite secrets before installing.** Without them the workloads wait on a secret that does not exist, and the install looks broken with no useful error. * **Expect a warm-up and do not interrupt it.** A default install is ready in about **90 seconds**, including a **2-restart, \~32-second** crash loop while PostgreSQL boots. The `postgresHA` metastore takes about **6 minutes** before Polaris answers. Both are normal. * **Root credentials are write-once**; they are applied only at first bootstrap. Rotate by creating a new principal through the management API. * **Rotating the token signing key invalidates every outstanding token** — clients must request a new one. * **`realm` is permanent.** Renaming it bootstraps a new, empty realm and hides the existing catalogs. * **Change the metastore password before installing** (`postgres.config.password` or `postgresHA.postgres.password`) — the default is a placeholder. * **No credential vending in this version.** Polaris and each query engine hold their own static object-storage credentials; keep `iceberg.rest-catalog.vended-credentials-enabled=false` in Trino. * **Health and metrics are in-GVC only.** They are served on port `8182`, which the public canonical endpoint does not route to. * **Polaris does not migrate its own database schema.** Treat a future Polaris version bump as an explicit schema step, not something startup handles. * **Metastore volumes survive restarts, redeploys and upgrades; uninstalling deletes them** — and every catalog definition with them. The Iceberg data in your bucket is untouched, but the catalog that indexed it is gone. Use `postgresHA` and/or backups for anything you care about. * **Uninstall does not delete your prerequisite secrets** — they are your resources, created outside the release. ## External References Official documentation for the shipped release Every server setting and its environment-variable name The PostgreSQL persistence backend this template uses The bootstrap tool the init workload runs Storage configuration for S3 and S3-compatible servers Principals, roles, and grants beyond the root principal The connector properties used to point Trino at Polaris View the source files, default values, and chart definition # PostGIS Source: https://docs.controlplane.com/template-catalog/templates/postgis Deploy PostGIS on Control Plane using the Template Catalog. Covers configuration, volumes, scaling, and PostgreSQL with geographic object support and spatial queries. ## Overview PostGIS extends PostgreSQL with support for geographic objects and spatial queries, making it the standard choice for location-aware applications. This template deploys a single-replica PostGIS instance with persistent storage. PostGIS on Control Plane operates as a single-replica deployment. Do not scale up the replica count, as this would result in multiple isolated instances rather than a replicated cluster. ### What Gets Created * **Stateful PostGIS Workload** — A single-replica PostGIS database container with configurable resources. * **Volume Set** — Persistent storage for database data, with optional autoscaling. * **Secret** — A dictionary secret storing the database username and password, injected into the container at startup. * **Identity & Policy** — An identity bound to the workload with `reveal` access to the database credentials secret, and cloud storage access when backup is enabled. * **Backup Cron Workload** *(optional)* — A scheduled `pg_dump` backup job that writes compressed SQL dumps to AWS S3 or GCS. This template does not create a GVC. You must deploy it into an existing GVC. ## Installation This template has no external prerequisites unless backup is enabled. To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} image: postgis/postgis:18-3.6 resources: minCpu: 200m minMemory: 528Mi maxCpu: 500m maxMemory: 1024Mi config: username: username password: password database: test volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) autoscaling: enabled: false maxCapacity: 100 # Maximum capacity in GiB minFreePercentage: 10 # Trigger scaling when free space drops below this percentage scalingFactor: 1.2 # Multiply current capacity by this factor when scaling up backup: # compatible with PostGIS 17+ enabled: false image: controlplanecorporation/pg-backup:18.1.0 # tag 18.1.0 = Postgres 18, 17.1.0 = Postgres 17 schedule: "0 2 * * *" # daily at 2am UTC resources: cpu: 100m memory: 128Mi provider: aws # Options: aws or gcp aws: bucket: my-backup-bucket region: us-east-1 cloudAccountName: my-backup-cloudaccount policyName: my-backup-policy prefix: postgis/backups gcp: bucket: my-backup-bucket cloudAccountName: my-backup-cloudaccount prefix: postgis/backups internalAccess: type: same-gvc # options: none, same-gvc, same-org, workload-list workloads: # Note: can only be used if type is same-gvc or workload-list #- //gvc/GVC_NAME/workload/WORKLOAD_NAME ``` ### Credentials * `config.username` — PostgreSQL username. **Change before deploying to production.** * `config.password` — PostgreSQL password. **Change before deploying to production.** * `config.database` — Name of the database created on startup. These values are only applied on first startup when the data directory is empty. Updating them after the initial deployment will have no effect on the running database. To change credentials or the database name on an existing instance, use PostgreSQL's native commands (e.g. `ALTER USER`, `ALTER DATABASE`). ### Resources * `resources.minCpu` / `resources.minMemory` — Minimum CPU and memory guaranteed to the workload. * `resources.maxCpu` / `resources.maxMemory` — Maximum CPU and memory the workload can use. ### Storage * `volumeset.capacity` — Initial volume size in GiB (minimum 10). * `volumeset.autoscaling.enabled` — Automatically expand the volume as it fills. When enabled: * `maxCapacity` — Maximum volume size in GiB. * `minFreePercentage` — Trigger a scale-up when free space drops below this percentage. * `scalingFactor` — Multiply the current capacity by this factor when scaling up. ### Internal Access * `internalAccess.type` — Controls which workloads can connect to PostGIS on port `5432`: | Type | Description | | --------------- | --------------------------------------------------------------- | | `none` | No internal access allowed | | `same-gvc` | Allow access from all workloads in the same GVC | | `same-org` | Allow access from all workloads in the same organization | | `workload-list` | Allow access only from specific workloads listed in `workloads` | ### Connecting to PostGIS Once deployed, connect to the database from within the same GVC using: ```text theme={null} RELEASE_NAME-postgis.GVC_NAME.cpln.local:5432 ``` ## Backup Backup is disabled by default. When enabled, a cron workload runs `pg_dump` on the configured schedule and uploads compressed SQL dumps to AWS S3 or GCS. Backup requires PostGIS 17 or later. Set `backup.image` to match your PostGIS version: `controlplanecorporation/pg-backup:18.1.0` for PostGIS 18, or `controlplanecorporation/pg-backup:17.1.0` for PostGIS 17. * `backup.enabled` — Enable scheduled backups. * `backup.schedule` — Cron expression for backup frequency (default: daily at 2am UTC). * `backup.provider` — `aws` or `gcp`. * `backup.resources.cpu` / `backup.resources.memory` — Resources for the backup cron container. ### AWS S3 Before enabling backup with `provider: aws`, complete the following in your AWS account: 1. Create an S3 bucket. Set `backup.aws.bucket` to its name and `backup.aws.region` to its region. 2. If you do not have a Cloud Account set up, refer to the docs to [Create a Cloud Account](/guides/create-cloud-account). Set `backup.aws.cloudAccountName` to its name. 3. Create an IAM policy with the following JSON, replacing `YOUR_BUCKET_NAME`: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket", "s3:GetObjectVersion", "s3:DeleteObjectVersion" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET_NAME", "arn:aws:s3:::YOUR_BUCKET_NAME/*" ] } ] } ``` 4. Set `backup.aws.policyName` to the name of the policy created in step 3. 5. Set `backup.aws.prefix` to the folder path where backups will be stored. ### GCS Before enabling backup with `provider: gcp`, complete the following in your GCP account: 1. Create a GCS bucket. Set `backup.gcp.bucket` to its name. 2. If you do not have a Cloud Account set up, refer to the docs to [Create a Cloud Account](/guides/create-cloud-account). Set `backup.gcp.cloudAccountName` to its name. 3. Add the **Storage Admin** role to the GCP service account associated with the Cloud Account. 4. Set `backup.gcp.prefix` to the folder path where backups will be stored. ## Restoring a Backup Run the following from a client with access to the backup bucket: **AWS S3:** ```sh theme={null} export PGPASSWORD="PASSWORD" aws s3 cp "s3://BUCKET_NAME/PREFIX/BACKUP_FILE.sql.gz" - \ | gunzip \ | psql \ --host=RELEASE_NAME-postgis.GVC_NAME.cpln.local \ --port=5432 \ --username=USERNAME \ --dbname=postgres unset PGPASSWORD ``` **GCS:** ```sh theme={null} export PGPASSWORD="PASSWORD" gsutil cp "gs://BUCKET_NAME/PREFIX/BACKUP_FILE.sql.gz" - \ | gunzip \ | psql \ --host=RELEASE_NAME-postgis.GVC_NAME.cpln.local \ --port=5432 \ --username=USERNAME \ --dbname=postgres unset PGPASSWORD ``` ## External References Official PostGIS documentation Official PostgreSQL documentation Source code for the PostgreSQL backup container image View the source files, default values, and chart definition # PostgreSQL Source: https://docs.controlplane.com/template-catalog/templates/postgres Deploy PostgreSQL on Control Plane using the Template Catalog. Covers configuration, volumes, scaling, and single-replica setup with optional scheduled S3, GCS, or MinIO backups. ## Overview PostgreSQL is a powerful open-source relational database. This template deploys a single-replica PostgreSQL instance with persistent storage and optional scheduled backups to AWS S3, GCS, or a self-hosted MinIO instance. PostgreSQL on Control Plane operates as a single-replica deployment. Do not scale up the replica count, as this would result in multiple isolated instances rather than a replicated cluster. For a highly available setup, use the [PostgreSQL Highly Available](/template-catalog/templates/postgres-highly-available) template instead. ### What Gets Created * **Stateful Postgres Workload** — A single-replica PostgreSQL container with configurable resources. * **Volume Set** — Persistent storage for database data, with optional autoscaling. * **Secret** — A dictionary secret storing the database username and password, injected into the container at startup. * **Identity & Policy** — An identity bound to the workload with `reveal` access to the database credentials secret, and cloud storage access when backup is enabled. * **PgBouncer Workload** *(optional)* — A PgBouncer connection pooler deployed as a separate workload in front of PostgreSQL. * **Backup Cron Workload** *(optional)* — A scheduled `pg_dump` backup job that writes compressed SQL dumps to AWS S3, GCS, or MinIO. This template does not create a GVC. You must deploy it into an existing GVC. ## Installation This template has no external prerequisites unless backup is enabled. To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} image: postgres:18 # versions before postgres:17 do not support the backup feature resources: minCpu: 200m minMemory: 128Mi maxCpu: 500m maxMemory: 256Mi config: username: username password: password database: test volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) autoscaling: enabled: false maxCapacity: 100 # Maximum capacity in GiB minFreePercentage: 10 # Trigger scaling when free space drops below this percentage scalingFactor: 1.2 # Multiply current capacity by this factor when scaling up internalAccess: type: same-gvc # options: none, same-gvc, same-org, workload-list workloads: # Note: can only be used if type is same-gvc or workload-list #- //gvc/GVC_NAME/workload/WORKLOAD_NAME pgbouncer: enabled: false image: edoburu/pgbouncer:v1.25.1-p0 poolMode: transaction # options: session, transaction, statement defaultPoolSize: 25 # number of real Postgres connections PgBouncer maintains maxClientConn: 1000 # maximum number of client connections PgBouncer accepts replicas: 1 resources: cpu: 200m memory: 128Mi backup: # compatible with Postgres 17+ only enabled: false image: ghcr.io/controlplane-com/backup-images/postgres-backup:18.1.0 # tag 18.1.0 = Postgres 18, 17.1.0 = Postgres 17 schedule: "0 2 * * *" # cron schedule, default is daily at 2am UTC resources: cpu: 100m memory: 128Mi provider: aws # Options: aws, gcp, or minio aws: bucket: my-backup-bucket region: us-east-1 cloudAccountName: my-backup-cloudaccount policyName: my-backup-policy prefix: postgres/backups gcp: bucket: my-backup-bucket cloudAccountName: my-backup-cloudaccount prefix: postgres/backups minio: # Backup to a self-hosted MinIO workload (or any S3-compatible endpoint) endpoint: http://my-minio-workload:9000 # e.g. http://WORKLOAD_NAME:9000 for an internal MinIO template deployment bucket: my-backup-bucket accessKey: my-minio-username # matches the MinIO template's admin.username secretKey: my-minio-password # matches the MinIO template's admin.password prefix: postgres/backups ``` ### Credentials * `config.username` — PostgreSQL username. **Change before deploying to production.** * `config.password` — PostgreSQL password. **Change before deploying to production.** * `config.database` — Name of the database created on startup. These values are only applied on first startup when the data directory is empty. Updating them after the initial deployment will have no effect on the running database. To change credentials or the database name on an existing instance, use PostgreSQL's native commands (e.g. `ALTER USER`, `ALTER DATABASE`). ### Resources * `resources.minCpu` / `resources.minMemory` — Minimum CPU and memory guaranteed to the workload. * `resources.maxCpu` / `resources.maxMemory` — Maximum CPU and memory the workload can use. ### Storage * `volumeset.capacity` — Initial volume size in GiB (minimum 10). * `volumeset.autoscaling.enabled` — Automatically expand the volume as it fills. When enabled: * `maxCapacity` — Maximum volume size in GiB. * `minFreePercentage` — Trigger a scale-up when free space drops below this percentage. * `scalingFactor` — Multiply the current capacity by this factor when scaling up. ### Internal Access * `internalAccess.type` — Controls which workloads can connect to PostgreSQL on port `5432`: | Type | Description | | --------------- | --------------------------------------------------------------- | | `none` | No internal access allowed | | `same-gvc` | Allow access from all workloads in the same GVC | | `same-org` | Allow access from all workloads in the same organization | | `workload-list` | Allow access only from specific workloads listed in `workloads` | ### Connecting to PostgreSQL Once deployed, connect to the database from within the same GVC using: ```text theme={null} RELEASE_NAME-postgres.GVC_NAME.cpln.local:5432 ``` ### PgBouncer Connection Pooling PgBouncer is an optional connection pooler that sits in front of PostgreSQL and multiplexes application connections into a smaller pool of real database connections. This reduces connection overhead and protects PostgreSQL from exhaustion under high concurrency. When enabled, PgBouncer is deployed as a separate workload and becomes the primary connection endpoint for your applications: ``` RELEASE_NAME-pgbouncer.GVC_NAME.cpln.local:5432 ``` * `pgbouncer.enabled` — Enable or disable PgBouncer. * `pgbouncer.poolMode` — Controls how connections are reused: | Mode | Description | | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `transaction` | Connection held only for the duration of a transaction, then returned to the pool. Best for most web and API workloads. Not compatible with session-level features (`SET` variables, temporary tables, advisory locks). | | `session` | Connection held for the entire client session. Compatible with all PostgreSQL features but provides less reuse. Increase `defaultPoolSize` to match your expected concurrent client count. | | `statement` | Connection returned after every statement. Transactions are not supported. Rarely used. | * `pgbouncer.defaultPoolSize` — Number of real PostgreSQL connections PgBouncer maintains per pool (default: `25`). * `pgbouncer.maxClientConn` — Maximum number of client connections PgBouncer accepts (default: `1000`). * `pgbouncer.replicas` — Number of PgBouncer instances. PgBouncer is stateless and can be scaled horizontally for high-throughput workloads. * `pgbouncer.resources.cpu` / `pgbouncer.resources.memory` — Resources allocated to each PgBouncer replica. PgBouncer shares the same credentials and identity as the PostgreSQL workload — no additional secrets or IAM configuration is required. The `userlist.txt` and `pgbouncer.ini` are generated automatically from your `config.username`, `config.password`, and `config.database` values at startup. ### Backup Backup is disabled by default. When enabled, a cron workload runs `pg_dump` on the configured schedule and uploads compressed SQL dumps to AWS S3 or GCS. Backup requires PostgreSQL 17 or later. Set `backup.image` to match your PostgreSQL version: `ghcr.io/controlplane-com/backup-images/postgres-backup:18.1.0` for Postgres 18, or `ghcr.io/controlplane-com/backup-images/postgres-backup:17.1.0` for Postgres 17. * `backup.enabled` — Enable scheduled backups. * `backup.schedule` — Cron expression for backup frequency (default: daily at 2am UTC). * `backup.provider` — `aws`, `gcp`, or `minio`. * `backup.resources.cpu` / `backup.resources.memory` — Resources for the backup cron container. ## Backup Prerequisites ### AWS S3 Before enabling backup with `provider: aws`, complete the following in your AWS account: 1. Create an S3 bucket. Set `backup.aws.bucket` to its name and `backup.aws.region` to its region. 2. If you do not have a Cloud Account set up, refer to the docs to [Create a Cloud Account](/guides/create-cloud-account). Set `backup.aws.cloudAccountName` to its name. 3. Create an IAM policy with the following JSON, replacing `YOUR_BUCKET_NAME`: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket", "s3:GetObjectVersion", "s3:DeleteObjectVersion" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET_NAME", "arn:aws:s3:::YOUR_BUCKET_NAME/*" ] } ] } ``` 4. Set `backup.aws.policyName` to the name of the policy created in step 3. 5. Set `backup.aws.prefix` to the folder path where backups will be stored. ### GCS Before enabling backup with `provider: gcp`, complete the following in your GCP account: 1. Create a GCS bucket. Set `backup.gcp.bucket` to its name. 2. If you do not have a Cloud Account set up, refer to the docs to [Create a Cloud Account](/guides/create-cloud-account). Set `backup.gcp.cloudAccountName` to its name. 3. Add the **Storage Admin** role to the GCP service account associated with the Cloud Account. 4. Set `backup.gcp.prefix` to the folder path where backups will be stored. ### MinIO Before enabling backup with `provider: minio`, ensure your MinIO instance is accessible: 1. Create a bucket in MinIO. Set `backup.minio.bucket` to its name. 2. Set `backup.minio.endpoint` to the MinIO S3 API address including the port. For the `minio` marketplace template deployed in the same GVC, use `http://WORKLOAD_NAME:9000`. 3. Set `backup.minio.accessKey` and `backup.minio.secretKey` to the MinIO root credentials — these match the `admin.username` and `admin.password` values from the MinIO template installation. 4. Set `backup.minio.prefix` to the folder path where backups will be stored. MinIO backup requires no Control Plane Cloud Account — credentials are passed directly to the backup job. ## Restoring a Backup Run the following from a client with access to the backup bucket: **AWS S3:** ```sh theme={null} export PGPASSWORD="PASSWORD" aws s3 cp "s3://BUCKET_NAME/PREFIX/BACKUP_FILE.sql.gz" - \ | gunzip \ | psql \ --host=RELEASE_NAME-postgres.GVC_NAME.cpln.local \ --port=5432 \ --username=USERNAME \ --dbname=postgres unset PGPASSWORD ``` **GCS:** ```sh theme={null} export PGPASSWORD="PASSWORD" gsutil cp "gs://BUCKET_NAME/PREFIX/BACKUP_FILE.sql.gz" - \ | gunzip \ | psql \ --host=RELEASE_NAME-postgres.GVC_NAME.cpln.local \ --port=5432 \ --username=USERNAME \ --dbname=postgres unset PGPASSWORD ``` **MinIO:** ```sh theme={null} export PGPASSWORD="PASSWORD" export AWS_ACCESS_KEY_ID="MINIO_ACCESS_KEY" export AWS_SECRET_ACCESS_KEY="MINIO_SECRET_KEY" aws configure set default.s3.addressing_style path aws s3 cp "s3://BUCKET_NAME/PREFIX/BACKUP_FILE.sql.gz" - --endpoint-url "http://MINIO_ENDPOINT:9000" \ | gunzip \ | psql \ --host=RELEASE_NAME-postgres.GVC_NAME.cpln.local \ --port=5432 \ --username=USERNAME \ --dbname=postgres unset PGPASSWORD AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY ``` ## External References Official PostgreSQL documentation PgBouncer configuration reference Source code for the PostgreSQL backup container image View the source files, default values, and chart definition # PostgreSQL Highly Available Source: https://docs.controlplane.com/template-catalog/templates/postgres-highly-available Deploy PostgreSQL Highly Available on Control Plane using the Template Catalog. Covers configuration, volumes, scaling, Patroni clustering with etcd consensus and HAProxy, and optional backups to S3, GCS, or MinIO. ## Overview PostgreSQL Highly Available deploys a production-ready PostgreSQL cluster using Patroni for automatic leader election and failover, with etcd providing distributed consensus. An optional HAProxy workload routes all write traffic to the current primary replica, providing a stable connection endpoint regardless of which replica holds the leader role. For production use, maintain at least 3 PostgreSQL replicas and 3 etcd replicas. etcd requires an odd number of replicas (3, 5, 7) for quorum. ### What Gets Created * **Stateful Patroni PostgreSQL Workload** — A Patroni-managed PostgreSQL cluster with configurable replica count and resources. Each replica has its own volume. Leadership is gracefully handed off before any replica is shut down, ensuring write availability is maintained during deployments and restarts. * **etcd Workload** — A dedicated etcd cluster providing distributed consensus for Patroni leader election. * **HAProxy Leader-Routing Workload** *(optional, enabled by default)* — Routes write traffic to the current primary replica, providing a stable connection endpoint during failover. * **PgBouncer Workload** *(optional)* — A connection pooler deployed in front of HAProxy, multiplexing application connections into a smaller pool of real database connections. * **Volume Set** — Persistent storage for PostgreSQL data, with optional autoscaling. * **Secrets** — A dictionary secret with database credentials; opaque secrets for the Patroni startup script, HAProxy startup script, and WAL-G backup script (created as needed). * **Identity & Policy** — An identity bound to the workload with `reveal` access to all required secrets, and cloud storage access when backup is enabled. This template does not create a GVC. You must deploy it into an existing GVC. ## Installation This template has no external prerequisites unless backup is enabled. To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} replicas: 3 resources: minCpu: 500m minMemory: 1Gi maxCpu: 1 maxMemory: 2Gi image: controlplanecorporation/patroni-postgres:0.7 postgres: username: username password: password database: test multiZone: false volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) autoscaling: enabled: false maxCapacity: 100 # Maximum capacity in GiB minFreePercentage: 10 # Trigger scaling when free space drops below this percentage scalingFactor: 1.2 # Multiply current capacity by this factor when scaling up internal_access: type: same-gvc # options: same-gvc, same-org, workload-list workloads: # Note: can only be used if type is same-gvc or workload-list #- //gvc/GVC_NAME/workload/WORKLOAD_NAME etcd: replicas: 3 resources: cpu: 500m memory: 512Mi multiZone: false tuning: # Passed through to the bundled etcd. Without compaction its backend grows # with time alone — Patroni renews its lease every ~10s and every renewal is # a revision — until it hits the quota and etcd goes READ-ONLY. autoCompactionMode: periodic # periodic (retention is a duration) or revision (a revision count) autoCompactionRetention: 1h # periodic needs an explicit unit (1h, 30m, 24h); revision takes a count quotaBackendBytes: 0 # backend size limit in bytes; 0 = etcd's own default of 2 GiB volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) internal_access: type: same-gvc # options: same-gvc, same-org, workload-list workloads: #- //gvc/GVC_NAME/workload/WORKLOAD_NAME pgbouncer: enabled: false image: edoburu/pgbouncer:v1.25.1-p0 poolMode: transaction # options: session, transaction, statement defaultPoolSize: 25 # number of real Postgres connections PgBouncer maintains per pod maxClientConn: 1000 # maximum number of client connections PgBouncer accepts per pod maxDbConnections: 100 # hard cap on total Postgres connections regardless of how many PgBouncer pods are running minReplicas: 2 maxReplicas: 4 resources: cpu: 200m memory: 128Mi proxy: # HAProxy endpoint to write to leader replica. Automatically enabled when pgbouncer is enabled. enabled: true image: haproxy:2.9 resources: cpu: 100m memory: 128Mi minReplicas: 2 maxReplicas: 2 backup: enabled: false mode: logical # logical or wal-g resources: cpu: 100m memory: 128Mi logical: image: ghcr.io/controlplane-com/backup-images/postgres-backup:17.1.0 # tag 17.1.0 = Postgres 17, 18.1.0 = Postgres 18 schedule: "0 2 * * *" # cron schedule, default is daily at 2am UTC walg: intervalSeconds: 21600 # interval in seconds between backups, default is every 6 hours # storage settings are applied to whichever mode is enabled provider: aws # Options: aws, gcp, or minio aws: bucket: pg-ha-backup-bucket region: us-east-1 cloudAccountName: my-s3-cloud-account policyName: pg-ha-backup-policy prefix: postgres/backups gcp: bucket: pg-ha-backup-bucket cloudAccountName: my-gcs-cloud-account prefix: postgres/backups minio: # Backup to a self-hosted MinIO workload (or any S3-compatible endpoint) endpoint: http://my-minio-workload:9000 # e.g. http://WORKLOAD_NAME:9000 for an internal MinIO template deployment bucket: pg-ha-backup-bucket accessKey: my-minio-username # matches the MinIO template's admin.username secretKey: my-minio-password # matches the MinIO template's admin.password prefix: postgres/backups ``` ### Credentials * `postgres.username` — PostgreSQL superuser username. **Change before deploying to production.** * `postgres.password` — PostgreSQL superuser password. **Change before deploying to production.** * `postgres.database` — Name of the database created on first startup. These values are only applied on first startup when the data directory is empty. Updating them after the initial deployment will have no effect on the running database. To change credentials or the database name on an existing instance, use PostgreSQL's native commands (e.g. `ALTER USER`, `ALTER DATABASE`). ### PostgreSQL Cluster * `replicas` — Number of PostgreSQL replicas. Minimum 3 recommended for production. * `resources.minCpu` / `resources.minMemory` — Minimum CPU and memory guaranteed per replica. * `resources.maxCpu` / `resources.maxMemory` — Maximum CPU and memory per replica. * `multiZone` — Spread replicas across availability zones within the location. ### Storage * `volumeset.capacity` — Initial volume size in GiB (minimum 10). Each replica gets its own volume. * `volumeset.autoscaling.enabled` — Automatically expand the volume as it fills. When enabled: * `maxCapacity` — Maximum volume size in GiB. * `minFreePercentage` — Trigger a scale-up when free space drops below this percentage. * `scalingFactor` — Multiply the current capacity by this factor when scaling up. ### etcd Cluster * `etcd.replicas` — Number of etcd replicas. Must be an **odd number** (3, 5, 7) for quorum. * `etcd.resources.cpu` / `etcd.resources.memory` — CPU and memory per etcd replica. * `etcd.multiZone` — Spread etcd replicas across availability zones. * `etcd.volumeset.capacity` — Initial volume size for etcd data in GiB. * `etcd.internal_access.type` — Controls which workloads can reach the etcd cluster. #### etcd History Compaction `etcd.tuning.autoCompactionMode`, `etcd.tuning.autoCompactionRetention` and `etcd.tuning.quotaBackendBytes` control how much revision history the bundled etcd cluster keeps and how large its backend may grow. The defaults — `periodic`, `1h` and `0` (etcd's own 2 GiB limit) — are the right settings for a Patroni consensus store and should be left alone. The [etcd template](/template-catalog/templates/etcd#compaction-and-backend-growth) documents the mechanism, the accepted value formats, and the read-only commands for inspecting a cluster. **Template versions before `2.4.2` did not compact the bundled etcd cluster.** Patroni renews its leader lease about every 10 seconds and every renewal creates a revision, so etcd grows with time alone — roughly 19 MB per day on an idle cluster — and reaches its 2 GiB backend quota in about 110 days. etcd then goes read-only across the whole cluster, and the PostgreSQL replicas restart-loop with `exitCode: 0` and `reason: Completed`, which reads as healthy and is easily mistaken for a database fault. Upgrade to `2.4.2` or later to turn compaction on. Upgrading prevents further growth but cannot shrink a backend that has already grown, and a cluster that has already raised a `NOSPACE` alarm needs operator recovery rather than an upgrade — see [If the Backend Quota Is Already Full](/template-catalog/templates/etcd#if-the-backend-quota-is-already-full). ### HAProxy (Strongly Recommended) In a Patroni cluster, only the leader replica accepts writes — other replicas are read-only. HAProxy automatically routes write traffic to the current leader, providing a stable connection endpoint even during failover. HAProxy exposes two HTTP endpoints on the proxy workload for observability: | Endpoint | Description | | --------------- | ----------------------------------------------------------------------------------- | | `:8404/healthz` | Returns healthy when at least one primary backend is reachable, unhealthy otherwise | | `:8405/stats` | Live stats page showing connection counts and the health status of each replica | * `proxy.enabled` — Deploy the HAProxy leader-routing workload (default: `true`). * `proxy.resources.cpu` / `proxy.resources.memory` — CPU and memory per HAProxy replica. * `proxy.minReplicas` / `proxy.maxReplicas` — Replica count for the proxy workload. HAProxy must be enabled (`proxy.enabled: true`) for logical backups to function correctly. WAL-G backups do not require the proxy. ### Internal Access * `internal_access.type` — Controls which workloads can connect to PostgreSQL on port `5432`: | Type | Description | | --------------- | --------------------------------------------------------------- | | `same-gvc` | Allow access from all workloads in the same GVC | | `same-org` | Allow access from all workloads in the same organization | | `workload-list` | Allow access only from specific workloads listed in `workloads` | ### PgBouncer Connection Pooling PgBouncer is an optional connection pooler that sits in front of HAProxy, multiplexing application connections into a smaller pool of real database connections. HAProxy handles leader routing and failover transparently — PgBouncer is unaware of which PostgreSQL replica is the primary. Enabling PgBouncer **automatically enables HAProxy**, as it is required for leader-aware routing in the HA cluster. When enabled, PgBouncer becomes the primary connection endpoint: ```text theme={null} RELEASE_NAME-pgbouncer.GVC_NAME.cpln.local:5432 ``` * `pgbouncer.poolMode` — Controls how connections are reused: | Mode | Description | | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `transaction` | Connection held only for the duration of a transaction, then returned to the pool. Best for most web and API workloads. Not compatible with session-level features (`SET` variables, temporary tables, advisory locks). | | `session` | Connection held for the entire client session. Compatible with all PostgreSQL features but provides less reuse. Increase `defaultPoolSize` to match your expected concurrent client count. | | `statement` | Connection returned after every statement. Transactions are not supported. Rarely used. | * `pgbouncer.defaultPoolSize` — Number of real PostgreSQL connections PgBouncer maintains per pod (default: `25`). * `pgbouncer.maxClientConn` — Maximum number of client connections PgBouncer accepts per pod (default: `1000`). * `pgbouncer.maxDbConnections` — Hard cap on total PostgreSQL connections across **all** PgBouncer pods. Prevents connection blowout when PgBouncer scales out — set this to a value the PostgreSQL primary can safely handle (default: `100`). * `pgbouncer.minReplicas` / `pgbouncer.maxReplicas` — PgBouncer autoscales on RPS between these bounds. Increase `maxReplicas` for high-throughput workloads. * `pgbouncer.resources.cpu` / `pgbouncer.resources.memory` — Resources allocated to each PgBouncer pod. ### Application Retry Logic Your application should implement retry logic on database connections. Several normal cluster events surface transient errors to the client: * **Patroni failover** — During a leader election, the old primary steps down and a new one is promoted. HAProxy detects the change and re-routes writes, but connections in flight during the transition may receive a connection reset or brief refusal. * **Rolling restarts and deployments** — Each replica is restarted in turn. Leadership is handed off gracefully before the leader shuts down, but applications may see a momentary connection disruption while the new leader is established and HAProxy updates its routing. * **Scale down** — Removing replicas triggers a shutdown sequence. If the departing replica held the leader role, a new election occurs and writes are unavailable until the new leader is ready. These events are expected and short-lived. Implementing exponential backoff with retry on connection errors ensures your application recovers automatically without surfacing errors to end users. ### Connecting to PostgreSQL Connect to PostgreSQL through the HAProxy workload, which always routes to the current leader: ``` RELEASE_NAME-postgres-ha-proxy.GVC_NAME.cpln.local:5432 ``` ### Backup Two backup modes are available. Set `backup.enabled: true`, choose a `mode`, and configure the storage provider. | Mode | Description | | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `logical` | Scheduled SQL dumps via `pg_dump`. Portable and suitable for smaller databases. Requires HAProxy to be enabled. | | `wal-g` | Continuous WAL archiving with point-in-time recovery. Suitable for larger databases requiring minimal data loss. Runs as a sidecar container alongside PostgreSQL. | * `backup.mode` — `logical` or `wal-g`. * `backup.provider` — `aws`, `gcp`, or `minio`. * `backup.resources.cpu` / `backup.resources.memory` — Resources allocated to the backup container. **Logical backup settings:** * `backup.logical.schedule` — Cron expression for backup frequency (default: daily at 2am UTC). **WAL-G backup settings:** * `backup.walg.intervalSeconds` — Interval between base backups in seconds (default: `21600`, every 6 hours). ## Backup Prerequisites ### AWS S3 Before enabling backup with `provider: aws`, complete the following in your AWS account: 1. Create an S3 bucket. Set `backup.aws.bucket` to the bucket name and `backup.aws.region` to its region. 2. If you do not have a Cloud Account set up, refer to the docs to [Create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account). Set `backup.aws.cloudAccountName` to its name. 3. Create an IAM policy with the following JSON, replacing `YOUR_BUCKET_NAME`: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket", "s3:GetObjectVersion", "s3:DeleteObjectVersion" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET_NAME", "arn:aws:s3:::YOUR_BUCKET_NAME/*" ] } ] } ``` 4. Set `backup.aws.policyName` to the name of the policy created in step 3. 5. Set `backup.aws.prefix` to the folder path where backups will be stored. ### GCS Before enabling backup with `provider: gcp`, complete the following in your GCP account: 1. Create a GCS bucket. Set `backup.gcp.bucket` to the bucket name. 2. If you do not have a Cloud Account set up, refer to the docs to [Create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account). Set `backup.gcp.cloudAccountName` to its name. 3. Add the **Storage Admin** role to the GCP service account associated with the Cloud Account. 4. Set `backup.gcp.prefix` to the folder path where backups will be stored. ### MinIO Before enabling backup with `provider: minio`, ensure your MinIO instance is accessible: 1. Create a bucket in MinIO. Set `backup.minio.bucket` to its name. 2. Set `backup.minio.endpoint` to the MinIO S3 API address including the port. For the `minio` marketplace template deployed in the same GVC, use `http://WORKLOAD_NAME:9000`. 3. Set `backup.minio.accessKey` and `backup.minio.secretKey` to the MinIO root credentials — these match the `admin.username` and `admin.password` values from the MinIO template installation. 4. Set `backup.minio.prefix` to the folder path where backups will be stored. MinIO backup requires no Control Plane Cloud Account — credentials are passed directly. Both `logical` and `wal-g` modes are supported; WAL-G uses its native S3-compatible storage support (`AWS_ENDPOINT` + path-style addressing). ## Restoring a Backup ### Logical Run the following from a client with access to the backup bucket. Connect through the proxy workload so the restore targets the current leader. **AWS S3:** ```sh theme={null} export PGPASSWORD="PASSWORD" aws s3 cp "s3://BUCKET_NAME/PREFIX/BACKUP_FILE.sql.gz" - \ | gunzip \ | psql \ --host=RELEASE_NAME-postgres-ha-proxy.GVC_NAME.cpln.local \ --port=5432 \ --username=USERNAME \ --dbname=postgres unset PGPASSWORD ``` **GCS:** ```sh theme={null} export PGPASSWORD="PASSWORD" gsutil cp "gs://BUCKET_NAME/PREFIX/BACKUP_FILE.sql.gz" - \ | gunzip \ | psql \ --host=RELEASE_NAME-postgres-ha-proxy.GVC_NAME.cpln.local \ --port=5432 \ --username=USERNAME \ --dbname=postgres unset PGPASSWORD ``` **MinIO:** ```sh theme={null} export PGPASSWORD="PASSWORD" export AWS_ACCESS_KEY_ID="MINIO_ACCESS_KEY" export AWS_SECRET_ACCESS_KEY="MINIO_SECRET_KEY" aws configure set default.s3.addressing_style path aws s3 cp "s3://BUCKET_NAME/PREFIX/BACKUP_FILE.sql.gz" - --endpoint-url "http://MINIO_ENDPOINT:9000" \ | gunzip \ | psql \ --host=RELEASE_NAME-postgres-ha-proxy.GVC_NAME.cpln.local \ --port=5432 \ --username=USERNAME \ --dbname=postgres unset PGPASSWORD AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY ``` ### WAL-G WAL-G point-in-time restore requires an empty data directory. Follow these steps: 1. Run `wal-g backup-list` to identify the desired backup. 2. Stop the PostgreSQL workload. 3. Create a new Volume Set for the restored data. 4. Run a one-off restore workload with the new Volume Set mounted at `/var/lib/postgresql/data` and run: ```sh theme={null} wal-g backup-fetch /var/lib/postgresql/data/pgdata ``` 5. Re-point the PostgreSQL workload to the restored Volume Set and restart. 6. After the restore, change `backup.walg.prefix` before re-enabling backups to avoid system identifier conflicts. ## External References Patroni automatic failover documentation Official PostgreSQL documentation Official etcd documentation PgBouncer configuration reference Source code for the PostgreSQL backup container image View the source files, default values, and chart definition # Postgres Multi-Location Source: https://docs.controlplane.com/template-catalog/templates/postgres-multi-location Deploy a single PostgreSQL 17 Patroni cluster stretched across Control Plane locations, with one primary, asynchronous streaming replicas elsewhere, automatic cross-location promotion, leader-routing HAProxy, optional PgBouncer pooling, and optional backups to object storage. ## Overview Postgres Multi-Location deploys **one** PostgreSQL 17 Patroni cluster whose members span several Control Plane locations: a single primary that takes all traffic, asynchronous streaming replicas in the other locations, and automatic promotion of a replica in a surviving location when the primary's location is lost. An HAProxy tier in every location routes connections to whichever member currently holds the leader lock, so applications connect to a stable name in their own region and never need to know where the primary is. Consensus is provided by a bundled [`etcd-multi-location`](/template-catalog/templates/etcd-multi-location) cluster with one member per location — that quorum, not PostgreSQL, decides what the cluster survives. For a cluster inside a single location, use [PostgreSQL Highly Available](/template-catalog/templates/postgres-highly-available) instead. ### How Many Locations You Need The consensus store commits a write only when a **majority** of its members agree, and it runs exactly one member per location. | Locations | Majority | Location losses survived | What happens when one location is lost | | --------- | -------- | ------------------------ | ------------------------------------------------------------------------------------------------------- | | **2** | 2 | **0** | The surviving replica holds current data but stays **read-only**. Promotion is **manual**. | | **3** | 2 | **1** | **Automatic failover** — a replica in a surviving location is promoted and every proxy re-routes to it. | | **5** | 3 | **2** | Survives losing **two** locations. | With N locations you survive `floor((N-1)/2)` losses, so an even count buys nothing over the odd count below it. Two locations cannot form a symmetric quorum, which is why that topology is a warm standby rather than an automatic-failover cluster. ### What Gets Created * **GVC** — A new GVC pinned to the configured locations. This template always creates it. * **Stateful Patroni PostgreSQL Workload** — `{release}-postgres`, running PostgreSQL 17 with Patroni. Each configured location runs `replicas` members, every member gets its own volume, and every member is individually addressable. PostgreSQL listens on `5432` and the Patroni REST API on `8008`. * **Stateful etcd Workload** — `{release}-etcd`, the bundled `etcd-multi-location` chart providing consensus, one member per location. * **HAProxy Leader-Routing Workload** *(optional, enabled by default)* — `{release}-postgres-proxy`, one tier per location, each routing to the single current primary. * **PgBouncer Workload** *(optional)* — `{release}-postgres-pgbouncer`, a connection pooler, one tier per location, pooling into that location's HAProxy. * **Cron Backup Workload** *(optional)* — `{release}-postgres-backup`, a scheduled `pg_dumpall` to object storage, running in exactly one location. * **WAL-G Sidecar** *(optional)* — A sidecar on every Patroni member; only the member holding the leader lock archives WAL and pushes base backups. * **Volume Sets** — `{release}-postgres-vs` for the PostgreSQL data directory (`ext4`, final snapshot with 7-day retention), plus the etcd cluster's own volume set. * **Secrets** — Opaque secrets holding the Patroni startup script, the HAProxy startup script, and the WAL-G backup script. The **database credentials secret is not created by this template** — see [Prerequisites](#prerequisites). * **Identity & Policy** — An identity per workload group with `reveal` on exactly the secrets in play, plus a bucket-scoped cloud binding when backups are enabled. This template creates its own GVC. You do not need to create one before installing. ## Prerequisites The GVC named in `global.gvc.name` **must not already exist**. Helm adopts a GVC that does, and `helm uninstall` then deletes it along with every unrelated workload in it. Always point this template at a fresh GVC name. PostgreSQL credentials are supplied through a [dictionary secret](/guides/create-secret/dictionary) that you create **before** installing. They are never passed through Helm values. The secret must hold exactly three keys — `username`, `password` and `database`: ```bash theme={null} cpln secret create-dictionary --name my-postgres-credentials \ --entry username=postgres \ --entry password="$(openssl rand -hex 24)" \ --entry database=mydb ``` Use plain identifiers for `username` and `database` — they are used unquoted when the database is created. Secrets are org-level, so no GVC flag is involved. Set `postgres.credentialsSecretName` to the secret's name. ```bash theme={null} cpln secret reveal my-postgres-credentials ``` Create the secret **before** installing. Without it `helm install` still reports success, but the workload sits at zero replicas in every location with `The secret ... no longer exists. Workload updates are paused until the secret is added or the reference to the secret removed.` in `cpln workload get-deployments`. Creating the secret afterwards clears the wedge on its own within a few minutes — no reinstall or upgrade is needed. Backups need additional setup before they can be enabled — see [Backing Up](#backing-up). To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} global: gvc: # This chart CREATES this GVC. It must NOT already exist: Helm adopts a GVC # that does, and `helm uninstall` then DELETES it and everything in it. name: postgres-multi-location-gvc # Minimum 2 locations. 3 gives automatic failover, 5 survives losing two; # 2 gives a warm standby with MANUAL promotion. # `replicas` is Patroni members per location; etcd always runs 1 per location. locations: - name: aws-us-east-1 replicas: 1 - name: aws-eu-central-1 replicas: 1 - name: aws-us-west-2 replicas: 1 image: controlplanecorporation/patroni-postgres:0.7 resources: minCpu: 500m minMemory: 1Gi maxCpu: 1 maxMemory: 2Gi postgres: # REQUIRED PREREQUISITE SECRET — CREATE IT BEFORE YOU INSTALL. # A `dictionary` secret holding exactly three keys: `username`, `password` and # `database`. If it does not exist at install time the deployment WEDGES # waiting on it and looks broken. credentialsSecretName: my-postgres-credentials # Preferred location for the primary. It does three things: # 1. On a FRESH install it decides where the primary starts — members in other # locations wait up to 90s for this one to initialise the cluster first. # If it is down or slow they bootstrap anyway (logged as a WARNING) and the # primary starts elsewhere; move it later with patronictl switchover. # 2. It biases FAILOVER elections toward this location (failover_priority). # 3. CHANGING IT ON A LIVE CLUSTER MOVES THE LEADER: the value is baked into # the startup script, so editing it restarts every member (~2 min of # interrupted writes) and the election that follows picks this location. # To move a primary WITHOUT a restart, use patronictl switchover. # Empty = no preference, and the primary starts wherever a member gets there first. primaryLocation: "" volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) autoscaling: enabled: false maxCapacity: 100 # GiB, when autoscaling is enabled minFreePercentage: 10 # free-space trigger scalingFactor: 1.2 # growth multiplier internalAccess: type: same-gvc # options: same-gvc, same-org, workload-list workloads: [] # only used when type is workload-list #- //gvc/GVC_NAME/workload/WORKLOAD_NAME # ─── Leader-routing proxy ───────────────────────────────────────────────────── # One HAProxy tier per location; every one routes to the single current primary. proxy: enabled: true # automatically enabled when pgbouncer.enabled is true image: haproxy:2.9 resources: cpu: 100m memory: 128Mi minReplicas: 2 maxReplicas: 2 # ─── PgBouncer connection pooler ────────────────────────────────────────────── # One tier per location, each pooling into its location's HAProxy. pgbouncer: enabled: false image: edoburu/pgbouncer:v1.25.1-p0 poolMode: transaction # options: session, transaction, statement defaultPoolSize: 25 # real Postgres connections PgBouncer keeps per replica maxClientConn: 1000 # client connections PgBouncer accepts per replica maxDbConnections: 100 # hard cap on total Postgres connections minReplicas: 2 maxReplicas: 4 resources: cpu: 200m memory: 128Mi # ─── Backups ────────────────────────────────────────────────────────────────── backup: enabled: false mode: logical # logical or wal-g # `logical` mode ONLY. A cron workload runs in EVERY location of its GVC, so # without this the job would fire once per location every night and write N # copies into one bucket. Pick the ONE location it runs in — nearest your # bucket. `wal-g` mode ignores it: that archives from whichever member is # currently the primary, wherever that is. location: aws-us-east-1 resources: # applies to whichever mode is enabled cpu: 100m # 512Mi, not 128Mi: the GCP path OOMs at 128Mi with NO log output — # logical jobs merely report `failed`, and the wal-g sidecar loops on # OOMKilled while WAL archives with no base backup. AWS and MinIO are # fine at 128Mi; a default has to work for every provider. memory: 512Mi logical: image: ghcr.io/controlplane-com/backup-images/postgres-backup:17.1.0 # 17.1.0 = Postgres 17, 18.1.0 = Postgres 18 schedule: "0 2 * * *" # cron schedule, default is daily at 02:00 UTC walg: intervalSeconds: 21600 # seconds between base backups, default is every 6 hours # storage settings are applied to whichever mode is enabled provider: aws # options: aws, gcp, minio aws: bucket: my-postgres-bucket region: us-east-1 cloudAccountName: my-s3-cloud-account policyName: my-postgres-backup-policy # bucket-scoped IAM policy prefix: postgres/backups # folder within the bucket gcp: bucket: my-postgres-bucket cloudAccountName: my-gcs-cloud-account prefix: postgres/backups # folder within the bucket minio: # a self-hosted MinIO workload, or any S3-compatible endpoint # MUST be reachable from EVERY location: wal-g's restore_command runs on # every member, and a member that cannot reach the archive never finishes # starting — its container is recycled every 160s indefinitely. A MinIO # workload in ONE location answers 503 from the others, so for wal-g use # a multi-location endpoint or an external S3-compatible service. endpoint: http://my-minio-workload:9000 # e.g. http://WORKLOAD_NAME:9000 in the same GVC bucket: my-postgres-bucket # REQUIRED PREREQUISITE SECRET when provider is `minio` — a `dictionary` # secret holding `accessKey` and `secretKey`. credentialsSecretName: my-postgres-minio-credentials prefix: postgres/backups # folder within the bucket # ─── etcd (subchart: etcd-multi-location) ───────────────────────────────────── etcd: image: controlplanecorporation/etcd:0.1 resources: cpu: 500m memory: 512Mi tuning: heartbeatIntervalMs: 250 electionTimeoutMs: 5000 # History compaction. Without it etcd's backend grows with time alone — # Patroni renews its lease every ~10s and every renewal is a revision — # until it hits the quota and etcd goes READ-ONLY. autoCompactionMode: periodic # periodic (retention is a duration) or revision (a revision count) autoCompactionRetention: 1h # periodic needs an explicit unit (1h, 30m, 24h) quotaBackendBytes: 0 # backend size limit in bytes; 0 = etcd's own default of 2 GiB volumeset: capacity: 10 internalAccess: type: same-gvc workloads: [] recovery: # EMERGENCY ONLY — see "Recovering From a Lost Location". forceNewClusterInLocation: "" ``` ### GVC and Locations The GVC name and the location list live under `global.gvc` so that Helm passes the same values to the bundled etcd cluster. Never configure the two location lists separately. * `global.gvc.name` — Name of the GVC this chart creates. It must not already exist. * `global.gvc.locations[].name` — A Control Plane location (e.g. `aws-us-east-1`). At least 2 are required; see [How Many Locations You Need](#how-many-locations-you-need). * `global.gvc.locations[].replicas` — Number of Patroni members in that location. etcd always runs exactly one member per location regardless of this value. Members are named `{workload}-{location}-{index}`, so they stay unique when a location runs more than one. Removing a location from the list is the supported way to shrink the cluster. Do not suspend a location instead — see [Important Notes](#important-notes). ### Credentials `postgres.credentialsSecretName` names the dictionary secret described in [Prerequisites](#prerequisites). The template creates no credentials of its own, and PgBouncer reads the same secret. The credentials are written into the data directory when the cluster first bootstraps. Rotating the secret afterwards does not change the database — change the password with `ALTER ROLE` first, then update the secret to match. ### Preferred Primary Location `primaryLocation` names the location you want the primary in. Patroni treats "which member initializes an empty cluster" and "which member wins a failover" as separate questions, and the knob addresses both: * **On a fresh install it biases where the primary starts.** Since template version 1.0.2, a member outside the preferred location whose data directory is empty holds back before bootstrapping: it polls the preferred location's members and proceeds as soon as one answers as the leader — or as a replica, which means the cluster is already initialized elsewhere and waiting cannot move it. * **On a running cluster it biases failover elections** toward that location through Patroni's `failover_priority` tag, a tiebreaker between candidates that have replayed the same amount of WAL. Leaving it empty means no preference: the primary starts wherever a member gets there first. **The head start is a bounded preference, not a guarantee.** The wait is capped at 90 seconds, so if the preferred location is slow to start, down or misconfigured, another member bootstraps the cluster where it is and logs a `WARNING` naming the consequence. Move the primary afterwards with `patronictl switchover --candidate`. The wait cannot split the cluster: the consensus store is the arbiter, so a member that gives up early finds the cluster already initialized and clones as a replica instead. Before version 1.0.2 the knob rendered only the failover tag, so a fresh install raced and the primary could land in any location. **Check where the leader actually landed before judging a slow first install.** Every write goes to the primary, so a primary outside `primaryLocation` puts a cross-region hop in front of your entire write path, and schema migrations are where that shows up first. On [Grafana Multi-Location](/template-catalog/templates/grafana-multi-location), which bundles this template, the same 713 migrations took about **5 seconds** with the primary local and stretched to **minutes** when it bootstrapped in another region. In the pair of test installs measured that way, the cold install took **11 m 41 s** with the primary remote against **4 m 19 s** with it in place, and the Grafana instances waiting on the migrations restarted **15** times against **2** — every figure in that comparison comes from those two runs. `primaryLocation` is baked into the Patroni startup script, so changing it on a live cluster rewrites that script and restarts every member. That costs the full [upgrade write outage](#availability-and-planned-outages) and the election that follows moves the leader. To move a primary without a restart, use `patronictl switchover` instead. The preference biases elections; it does not fail back automatically after an outage. A cluster whose preferred location is restored keeps its current leader until the next election. ### Resources and Storage * `image` — The Patroni + PostgreSQL 17 image. * `resources.minCpu` / `resources.minMemory` — Reserved CPU and memory per member. * `resources.maxCpu` / `resources.maxMemory` — Limits per member. * `volumeset.capacity` — Initial volume size in GiB (minimum 10). Every member gets its own volume. * `volumeset.autoscaling.enabled` — Expand the volume automatically as it fills. When enabled: * `maxCapacity` — Maximum volume size in GiB. * `minFreePercentage` — Trigger a scale-up when free space drops below this percentage. * `scalingFactor` — Multiply the current capacity by this factor when scaling up. ### Internal Access `internalAccess` applies to the PostgreSQL, HAProxy and PgBouncer tiers alike. There is no public access in this version — every endpoint is internal to Control Plane. | Type | Description | | --------------- | ----------------------------------------------------------------------- | | `same-gvc` | Allow access from all workloads in the same GVC (default) | | `same-org` | Allow access from all workloads in the same organization | | `workload-list` | Allow access only from the workload links in `internalAccess.workloads` | A firewall change is not instant. Closing access down was measured at under a minute, while re-opening it took over three minutes. Allow up to about four minutes and re-test before concluding the setting did not apply. ### Leader-Routing Proxy Only the member holding the leader lock accepts writes; the rest are read-only. HAProxy polls each member's Patroni REST API and forwards connections to whichever one currently answers as the primary, so applications get a stable endpoint in their own location. `proxy.minReplicas` / `proxy.maxReplicas` are **per location** — the default of 2 means 2 replicas in each configured location. | Endpoint | Description | | --------------- | ---------------------------------------------------------------- | | `:8404/healthz` | Healthy when a primary backend is reachable, unhealthy otherwise | | `:8405/stats` | Live HAProxy stats, including the health of each member | The proxy is required for `logical` backups, which must dump from the current primary, and the chart refuses to render without it. Enabling PgBouncer enables the proxy implicitly, even if `proxy.enabled` is set to `false`. ### PgBouncer Connection Pooling PgBouncer multiplexes application connections into a smaller pool of real database connections. It pools into its own location's HAProxy rather than into a member, so leader routing and failover stay transparent. When enabled it becomes the connection endpoint applications use. | Mode | Description | | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `transaction` | Connection held for one transaction, then returned to the pool. Best for most web and API workloads. Not compatible with `SET` variables, temporary tables or advisory locks. | | `session` | Connection held for the whole client session. Compatible with everything, less reuse — raise `defaultPoolSize` to your expected concurrency. | | `statement` | Connection returned after every statement. Transactions are not supported. | * `pgbouncer.defaultPoolSize` — Real PostgreSQL connections kept per PgBouncer replica (default `25`). * `pgbouncer.maxClientConn` — Client connections accepted per PgBouncer replica (default `1000`). * `pgbouncer.maxDbConnections` — Hard cap on total PostgreSQL connections, so scaling PgBouncer out cannot exhaust the primary. * `pgbouncer.minReplicas` / `pgbouncer.maxReplicas` — Autoscaling bounds, **per location**. ### etcd Consensus Store The `etcd` block is passed straight through to the bundled [`etcd-multi-location`](/template-catalog/templates/etcd-multi-location) chart, which is where its image, resources, storage, access and emergency recovery settings are documented. `etcd.tuning.autoCompactionMode`, `etcd.tuning.autoCompactionRetention` and `etcd.tuning.quotaBackendBytes` control how much revision history etcd keeps and how large its backend may grow. Compaction has been enabled in every version of the bundled chart; since template version `1.0.3` the values are also configurable here. The defaults — `periodic`, `1h` and `0` (etcd's own 2 GiB limit) — are the right settings for a Patroni consensus store and should be left alone. See [Compaction and Backend Growth](/template-catalog/templates/etcd-multi-location#compaction-and-backend-growth) for the mechanism and the accepted value formats. `etcd.tuning.heartbeatIntervalMs` and `etcd.tuning.electionTimeoutMs` are the raft timers, tuned for cross-region round trips; raise both in proportion if your locations are more than about 250 ms apart. ## Connecting Everything is internal to Control Plane. Substitute your release name and the GVC name from `global.gvc.name`. | What | Where | | --------------------------------------------------- | ---------------------------------------------------------------------------- | | PostgreSQL, pooled (when PgBouncer is enabled) | `RELEASE_NAME-postgres-pgbouncer.GVC_NAME.cpln.local:5432` | | PostgreSQL, current primary (recommended otherwise) | `RELEASE_NAME-postgres-proxy.GVC_NAME.cpln.local:5432` | | PostgreSQL, one specific member | `replica-INDEX.RELEASE_NAME-postgres.LOCATION.GVC_NAME.cpln.local:5432` | | Patroni REST API | Port `8008` on the same per-member names (`/primary`, `/replica`, `/health`) | | HAProxy health and stats | `:8404/healthz` and `:8405/stats` on the proxy workload | | Credentials | The dictionary secret named by `postgres.credentialsSecretName` | The proxy endpoint in every location reaches the same primary, wherever it is. Per-member names are useful for read-only queries against a nearby replica, or as a fallback when the proxy is disabled — but a client using them has to poll the members to discover which one is currently the primary. ## Availability and Planned Outages Measured on a three-location cluster (`aws-us-east-1`, `aws-eu-central-1`, `aws-us-west-2`) with one member per location and a one-second write probe running in each location. | Event | Measured impact | | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ | | Clean install to all three members ready | **169 s** — leader elected, both replicas streaming with no lag | | Graceful loss of the member holding the leader lock | Switchover in **3.19 s**; writes unavailable **4.7–5.8 s** per location; the old member rejoined as a streaming replica after **2 m 16 s** | | Any `helm upgrade`, including one that changes nothing | Writes unavailable in **every** location for about **117 s** | | Restart of the etcd tier | About **19 s** of write unavailability; recovers automatically | **Treat every `helm upgrade` as a planned write outage of roughly two minutes.** Members do not restart one at a time: the field that would serialize the rollout is not retained by the platform, so all members go down together and no location can write during the window. This was measured on an upgrade that changed nothing at all, so it applies to any values change, including enabling a feature. During an etcd outage Patroni's failsafe mode keeps the primary serving writes — it held for about 60 seconds with zero failed writes. Once the replicas' own health checks start failing, the failsafe loses its precondition and the primary demotes itself, which is where the \~19 s of unavailability comes from. The cluster re-promotes and resumes on its own; no operator action is needed. ### Upgrading From 1.0.0 or 1.0.1 **Version 1.0.2 renamed every PostgreSQL-side resource, there is no in-place upgrade path, and the upgrade DELETES the volume set holding your data. Back up before you go anywhere near it.** The `-ml` infix is gone: the workloads, secrets, identity, policy and — critically — the **volume set** are now named `{release}-postgres…` instead of `{release}-postgres-ml…`. Running `helm upgrade` from 1.0.0 or 1.0.1 creates a new, empty volume set and then names the old one in its cleanup phase; it is gone seconds later, with no reachable final snapshot despite `snapshots.createFinalSnapshot`. There is no leftover copy to recover from. | Resource | 1.0.0 / 1.0.1 | 1.0.2 | | -------------------------- | --------------------------------- | ------------------------------ | | Patroni workload | `{release}-postgres-ml` | `{release}-postgres` | | **Volume set (your data)** | `{release}-postgres-ml-vs` | `{release}-postgres-vs` | | HAProxy workload | `{release}-postgres-ml-proxy` | `{release}-postgres-proxy` | | PgBouncer workload | `{release}-postgres-ml-pgbouncer` | `{release}-postgres-pgbouncer` | | Backup workload | `{release}-postgres-ml-backup` | `{release}-postgres-backup` | | Identity, policy, secrets | `{release}-postgres-ml-…` | `{release}-postgres-…` | The bundled etcd cluster's resources were never renamed and are unaffected — which is the only reason a measured upgrade kept its data at all. **Why the data survived a measured upgrade, and why that is not a plan.** Helm creates the new workload before it deletes the old one, and because etcd's volume set kept its name the cluster still had its DCS. All three new members therefore cloned the database from the still-running old primary (`bootstrapped from leader …`). That old primary kept serving for **86 seconds** after Helm reported it deleted, and a 7.5 MB database cloned in **12 seconds**. A database that cannot finish a base backup inside that window has nothing to fall back on, because the volume holding it has already been destroyed — that failure case is *inferred, not observed*, and it is not a race worth running. Two further consequences of the same upgrade, both measured: * **No writable primary for about 2 minutes 22 seconds.** The upgrade bounces the etcd tier too, so Patroni cannot immediately expire the stale leader key. * **`{release}-postgres-ml-proxy` is deleted and never returns.** Every application connection string has to move to `{release}-postgres-proxy`; nothing recovers on its own. Migrate with a backup and restore rather than an upgrade: Use `backup.mode: logical`, or run a `pg_dumpall` against the proxy endpoint of the old release. This dump is the only copy of your data that survives the rename, so verify it before you touch the release. `cpln helm uninstall RELEASE_NAME --gvc GVC_NAME`. Install the new version, then load the dump back through the proxy — see [Restoring a Backup](#restoring-a-backup). Update each application's connection string from `{release}-postgres-ml-proxy` to `{release}-postgres-proxy`. The old hostname no longer exists. Fresh installs of 1.0.2 are unaffected — this applies only to clusters created with an earlier version. ### Application Retry Logic Applications must retry on transient database errors. Two are worth handling explicitly during a failover: * Connection-level failures — `server closed the connection unexpectedly`, `terminating connection due to administrator command`. * `ERROR: cannot execute INSERT in a read-only transaction`, seen briefly when the proxy forwards to a member that has just been demoted and its health check has not yet flipped. Exponential backoff over a window of a few seconds covers a failover; an upgrade needs a window of a couple of minutes or a maintenance pause. ### Data Loss on Failover Replication is **asynchronous**. A promoted replica applies everything it has received, so a failover can lose the transactions that had not yet reached it — bounded by the replication lag at the instant of failure. Check lag with `pg_stat_replication` on the primary; in the service mesh every replication client reports the same `client_addr`, so use `application_name` (the member name) to tell members apart. ## Operating the Cluster Members are named `{workload}-{location}-{index}`, for example `my-db-postgres-aws-us-east-1-0`. `patronictl` reads the config the startup script writes to `/tmp/patroni_config.yml`: ```bash theme={null} # Every member, its location, role and replication lag cpln workload exec RELEASE_NAME-postgres --gvc GVC_NAME --container patroni-postgres \ -- patronictl -c /tmp/patroni_config.yml list # Move a live primary to another location — a planned, near-zero-downtime handover cpln workload exec RELEASE_NAME-postgres --gvc GVC_NAME --container patroni-postgres \ -- patronictl -c /tmp/patroni_config.yml switchover --candidate RELEASE_NAME-postgres-LOCATION-0 --force ``` Consensus-level settings (`ttl`, `loop_wait`, `retry_timeout`, `maximum_lag_on_failover`, failsafe mode) are written once, when the cluster is first initialized, and are not values knobs. Change them on a live cluster with `patronictl edit-config`. ### Recovering From a Lost Location With **3 or more locations** this section does not apply — losing one location is an automatic failover. With **2 locations**, losing one loses consensus quorum permanently: the survivor holds current data but cannot be granted the leader lock, and consensus writes time out. To rebuild from the surviving member, set `etcd.recovery.forceNewClusterInLocation` to that member's location and run a `helm upgrade`. Once writes are accepted again, return the value to `""` and reprovision the failed location's members — their volumes must be reset before they rejoin. ## Backing Up Backups are disabled by default. Set `backup.enabled: true`, choose a mode, and configure a storage provider. | Mode | Shape | Runs where | Good for | | --------- | --------------------------------------------------- | ---------------------------------------- | ----------------------------------------------------------------------- | | `logical` | Scheduled `pg_dumpall` cron workload | Exactly one location — `backup.location` | Portable SQL dumps, cross-version migration, smaller databases | | `wal-g` | Sidecar on every member, active only on the primary | Follows the primary | Continuous WAL archiving and point-in-time recovery on larger databases | `backup.location` exists because **a cron workload runs in every location of its GVC**. Without it a three-location cluster would dump the same database three times a night into one bucket. Pick the location nearest the bucket — that is where the dump is read and uploaded from. It applies to `logical` mode only: the WAL-G sidecar runs on every member but only the one holding the leader lock pushes, so the archive follows the primary across a failover with no selector needed. * `backup.logical.schedule` — Cron expression for the dump (default: daily at 02:00 UTC). The dump runs through the proxy, so it always hits the current primary, and includes roles and all databases. * `backup.walg.intervalSeconds` — Seconds between base backups (default `21600`, every 6 hours). WAL segments are archived continuously in between. * `backup.resources.cpu` / `backup.resources.memory` — Resources for whichever mode is enabled. Switching `backup.mode` to or from `wal-g` restarts PostgreSQL, because it changes `archive_mode`. Enabling `logical` backups does not. After enabling `wal-g`, confirm `SHOW archive_mode` reports `on` in every location before relying on the archive — one location can take up to about ten minutes to pick up the new configuration. Every provider has been exercised in both modes. **AWS S3** and **MinIO / S3-compatible** passed in `logical` and `wal-g` mode alike, at the shipped settings. **Google Cloud Storage** works but is memory-sensitive: at `backup.resources.memory: 128Mi` it failed in both modes — a `logical` job simply reports `failed` with no error line anywhere, and the `wal-g` sidecar is OOM-killed in a loop, which takes its member out of service and triggers leader elections while WAL keeps accumulating with no base backup to restore it against. It passed at 256Mi (`logical`) and 512Mi (`wal-g`), and this template ships **512Mi**, at or above both proven values — which is why the values comment warns against lowering it. Both restores have been verified end to end: a **wal-g restore** (base backup plus WAL replay into an empty data directory, checksum-identical to source) and a **logical restore** (a `pg_dumpall` artifact replayed into an `initdb`-fresh cluster with zero errors, roles, ownership and sequence positions preserved). The **volume-set swap** in the WAL-G procedure below has **not** been exercised — that step is documented, not tested. Rehearse your restore procedure in a scratch environment before you need it. ### AWS S3 Before enabling backup with `provider: aws`, complete the following in your AWS account: 1. Create an S3 bucket. Set `backup.aws.bucket` to its name and `backup.aws.region` to its region. 2. If you do not have a Cloud Account set up, refer to the docs to [Create a Cloud Account](/guides/create-cloud-account). Set `backup.aws.cloudAccountName` to its name. 3. Create an IAM policy with the following JSON, replacing `YOUR_BUCKET_NAME`: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket", "s3:GetObjectVersion", "s3:DeleteObjectVersion" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET_NAME", "arn:aws:s3:::YOUR_BUCKET_NAME/*" ] } ] } ``` 4. Set `backup.aws.policyName` to the name of the policy created in step 3. This bucket-scoped policy is all the workload identity needs — no broad managed policy is required. 5. Set `backup.aws.prefix` to the folder path where backups will be stored. ### GCS Before enabling backup with `provider: gcp`, complete the following in your GCP account: 1. Create a GCS bucket. Set `backup.gcp.bucket` to its name. 2. If you do not have a Cloud Account set up, refer to the docs to [Create a Cloud Account](/guides/create-cloud-account). Set `backup.gcp.cloudAccountName` to its name. 3. Add the **Storage Admin** role to the GCP service account associated with the Cloud Account. The template additionally binds the identity to `roles/storage.objectAdmin` on exactly the bucket named in `backup.gcp.bucket`. 4. Set `backup.gcp.prefix` to the folder path where backups will be stored. ### MinIO **In `wal-g` mode the endpoint must be reachable from every location.** WAL-G's `restore_command` runs on every member, and a member that cannot reach the archive never finishes starting — its container is recycled roughly every 160 seconds, indefinitely. A MinIO workload that runs in only one location answers `503` immediately from the others, which is exactly the shape a same-GVC MinIO invites: measured with two of three members looping for over 20 minutes. Nothing looks wrong from a client, because the leader stays healthy and writes keep succeeding while the cluster quietly loses all of its redundancy. For `wal-g`, point the endpoint at a MinIO deployment present in every location or at an external S3-compatible service. `logical` mode is unaffected — its cron runs only in `backup.location` — and AWS S3 and GCS are global endpoints, so they never hit this. No Cloud Account is needed — credentials are supplied as a secret. 1. Create a bucket in MinIO. Set `backup.minio.bucket` to its name. 2. Set `backup.minio.endpoint` to the MinIO S3 API address including the port. For the [minio](/template-catalog/templates/minio) template deployed in the same GVC, use `http://WORKLOAD_NAME:9000`. 3. Create a dictionary secret holding the MinIO credentials and set `backup.minio.credentialsSecretName` to its name. For the `minio` template these are its `admin.username` and `admin.password`: ```bash theme={null} cpln secret create-dictionary --name my-postgres-minio-credentials \ --entry accessKey=MINIO_ACCESS_KEY \ --entry secretKey=MINIO_SECRET_KEY ``` 4. Set `backup.minio.prefix` to the folder path where backups will be stored. ## Restoring a Backup Both restore paths have been verified end to end, though not by running these exact commands: the `pg_dumpall` artifact was replayed with `psql` into an `initdb`-fresh cluster rather than through the proxy into a live one, and the WAL-G restore was fetched and replayed into an empty directory rather than onto a swapped volume set. The steps below are the operational form of those procedures — rehearse them in a scratch environment before you need them. ### Logical Stream the dump back through the proxy, which writes to the current primary. Run this from a client with access to the bucket: ```sh theme={null} export PGPASSWORD="PASSWORD" aws s3 cp "s3://BUCKET_NAME/PREFIX/BACKUP_FILE.sql.gz" - \ | gunzip \ | psql \ --host=RELEASE_NAME-postgres-proxy.GVC_NAME.cpln.local \ --port=5432 \ --username=USERNAME \ --dbname=postgres unset PGPASSWORD ``` For GCS, replace the `aws s3 cp` with `gsutil cp "gs://BUCKET_NAME/PREFIX/BACKUP_FILE.sql.gz" -`. For MinIO, add `--endpoint-url "http://MINIO_ENDPOINT:9000"` and run `aws configure set default.s3.addressing_style path` first. ### WAL-G A point-in-time restore needs an empty data directory, so restore into a new volume set: 1. Run `wal-g backup-list` to identify the desired backup. 2. Stop the Patroni workload. 3. Create a new volume set and mount it at `/var/lib/postgresql/data` on a one-off restore workload. 4. Run `wal-g backup-fetch /var/lib/postgresql/data/pgdata BACKUP_NAME`. 5. Re-point the Patroni workload at the restored volume set and start it. 6. Change the WAL-G prefix before re-enabling backups, or the new cluster's WAL collides with the old system identifier. ## Important Notes * **Back up before upgrading a 1.0.0 or 1.0.1 cluster to 1.0.2.** Resource names dropped the `-ml` infix in 1.0.2, and the upgrade **deletes** the old volume set — there is no orphaned copy and no reachable final snapshot. Data survived a measured upgrade only because the new members re-cloned from the old primary during an 86-second window before it was torn down. Back up, uninstall, reinstall and restore instead, and move every connection string to `{release}-postgres-proxy`: see [Upgrading From 1.0.0 or 1.0.1](#upgrading-from-1-0-0-or-1-0-1). * **Create the credentials secret before installing.** `postgres.credentialsSecretName` names a secret this template does not create. Without it the deployment waits on a secret that does not exist; creating it afterwards clears the wedge within a few minutes. * **The GVC in `global.gvc.name` must not already exist.** Helm adopts an existing GVC and deletes it on uninstall, taking every unrelated workload with it. * **Every `helm upgrade` interrupts writes in all locations for about two minutes.** Plan changes as maintenance windows. * **Replication is asynchronous**, so a failover can lose the most recent transactions — bounded by the replication lag at the moment of failure. * **Set `primaryLocation` before the first install if you care where the primary is.** Since version 1.0.2 it also biases where the primary bootstraps: members elsewhere wait up to 90 seconds for the preferred location, then bootstrap anyway and log a `WARNING`. Changing it later moves a live primary — that restarts every member, costs the full upgrade outage and triggers an election — and it does not fail back automatically after an outage. * **Never suspend a location.** Suspending and resuming one permanently withdraws its endpoints from the other locations' service discovery while every status surface still reports healthy. Remove a location from `global.gvc.locations` instead. * **Allow about two minutes after a cold install** before believing a member is unreachable — cross-location service discovery takes that long to converge. Firewall changes can take up to about four minutes. * **Consensus-level settings are not values knobs.** They are written once, at first initialization; change them with `patronictl edit-config`. * **Cost scales with write volume multiplied by the members outside the primary's location.** Each of them receives a full copy of the WAL stream, and cross-region traffic is billed. Read-mostly workloads are inexpensive to stretch; write-heavy ones are not. ## External References Patroni clustering and automatic failover documentation Command reference for inspecting and switching over a cluster The consensus-level settings changed with `patronictl edit-config` Official PostgreSQL documentation Continuous archiving and point-in-time recovery Official etcd documentation PgBouncer configuration reference View the source files, default values, and chart definition # Prometheus Source: https://docs.controlplane.com/template-catalog/templates/prometheus Deploy Prometheus on Control Plane using the Template Catalog. A self-hosted metrics database with remote-write ingest, durable TSDB storage, and an optional Thanos sidecar for Store API federation and object-storage block upload. ## Overview Prometheus is the standard open-source metrics database. This template deploys a single Prometheus server with the remote-write receiver enabled and a durable TSDB volume: your own senders push metrics in via Prometheus `remote_write` (or Prometheus scrapes targets you configure), and anything that speaks PromQL — your own Grafana, dashboards, scripts — queries them back. An optional co-located Thanos sidecar (on by default) exposes the Store API for a Thanos Query tier and can upload TSDB blocks to your object bucket for long-term durability. This is a **self-hosted metrics store for your own metrics from your own sources**. It is separate from — and not a replacement for — Control Plane's built-in observability, which continues to collect and dashboard your workloads' metrics natively. ### Architecture * **Prometheus** — A single-replica stateful workload. Scrape and remote-write ingest plus PromQL query, all on port `9095` — Prometheus's usual port `9090` is reserved by the platform, so this template runs it on `9095` instead. High availability is achieved by installing the template twice (see [High Availability](#high-availability)). * **Thanos sidecar** *(optional, default on)* — A second container in the same workload. Serves the Thanos Store API (gRPC) on `10901` and HTTP health/metrics on `10902`, and uploads TSDB blocks to object storage when enabled. * **Object storage** *(optional)* — The sidecar ships completed TSDB blocks to your S3, GCS, or S3-compatible bucket; history beyond local retention lives in the bucket. ### What Gets Created * **Stateful Prometheus Workload** — Serves remote-write ingest and PromQL query on port `9095`; includes the Thanos sidecar container when enabled. * **Volume Set** — 20 GiB at `/prometheus` for the TSDB, shared read-only with the sidecar. * **Secrets** — An opaque secret holding the rendered `prometheus.yml`, mounted as a file; a second secret holds the Thanos bucket configuration when object storage is enabled. * **Identity & Policy** — An identity bound to the workload with `reveal` access to exactly the template's secrets (plus any remote-write password secrets you name), and cloud access scoped to exactly your bucket (AWS and GCP backends) when object storage is enabled. This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites None for a default install. Two optional features have prerequisites: * **Remote write with basic auth** — an opaque secret per endpoint holding the password (set `remoteWrite[].basicAuth.passwordSecretName` to its name). The secret must exist **before** install — the template references it by name only and never stores the password in values. * **Object storage** (`thanos.objectStorage`) — an existing bucket and access setup for it. Complete the steps for your chosen backend below. AWS S3 uses a Control Plane cloud identity — no credentials are stored; the workload's identity vends temporary credentials at runtime. Create an S3 bucket. Set `thanos.objectStorage.aws.bucket` and `thanos.objectStorage.aws.region` to match. If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) for your AWS account. Set `thanos.objectStorage.aws.cloudAccountName` to its name. Create an AWS IAM policy granting `s3:ListBucket`, `s3:GetObject`, `s3:PutObject`, and `s3:DeleteObject` on the bucket and its objects (the template's README includes the full policy JSON). Set `thanos.objectStorage.aws.policyName` to the policy's name (bare name, not ARN). Google Cloud Storage uses a Control Plane cloud identity — no credentials are stored. Create a GCS bucket. Set `thanos.objectStorage.gcp.bucket` to its name. If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) for your GCP project. Set `thanos.objectStorage.gcp.cloudAccountName` to its name. No policy to author — the template grants the workload identity `roles/storage.objectAdmin` scoped to the bucket automatically. Any S3-compatible server uses static access credentials, since these cannot federate with a cloud account. Create your bucket on the server and credentials that can read and write it. Set `thanos.objectStorage.minio.bucket`, `thanos.objectStorage.minio.accessKey`, and `thanos.objectStorage.minio.accessSecret` to match. Set `thanos.objectStorage.minio.endpoint` to the S3 API address as `host:port` with **no scheme** (e.g. `my-minio:9000`), and `thanos.objectStorage.minio.insecure: true` when the endpoint is plain HTTP. Install the template using your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} image: prom/prometheus:v3.13.1 resources: # Prometheus server; memory scales with active series cpu: 1000m memory: 2Gi minCpu: 500m minMemory: 1Gi retention: time: 15d # how long samples are kept on the local volume size: "" # optional local size cap, e.g. 15GB — empty means no cap volumeset: capacity: 20 # GiB — TSDB volume mounted at /prometheus scrapeInterval: 30s # global scrape interval (self-scrape and extraScrapeConfigs) externalLabels: {} # identity labels on all uploaded/forwarded series, e.g. region: us-east-1, replica: a extraScrapeConfigs: "" # raw YAML list of additional scrape_configs entries remoteWrite: [] # entries: url (required) + optional basicAuth # - url: http://my-mimir.my-gvc.cpln.local:8080/api/v1/push # basicAuth: # username: my-user # passwordSecretName: my-remote-write-password # opaque secret, must exist BEFORE install thanos: sidecar: enabled: true # co-located sidecar exposing the Store API (gRPC :10901) for a Thanos Query tier image: quay.io/thanos/thanos:v0.42.2 resources: cpu: 250m memory: 512Mi minCpu: 100m minMemory: 128Mi objectStorage: enabled: false # sidecar uploads TSDB blocks to your bucket; requires thanos.sidecar.enabled type: aws # options: aws, gcp, minio blockDuration: 2h # advanced: TSDB block interval (min=max disables local compaction) aws: # AWS S3 — keyless via Control Plane cloud identity bucket: my-prometheus-bucket # bucket must already exist region: us-east-1 cloudAccountName: my-s3-cloud-account # Control Plane AWS cloud account policyName: my-prometheus-s3-policy # custom bucket-scoped IAM policy (bare name) gcp: # Google Cloud Storage — keyless via Control Plane cloud identity bucket: my-prometheus-bucket # bucket must already exist cloudAccountName: my-gcs-cloud-account # Control Plane GCP cloud account minio: # Any S3-compatible server (MinIO, R2, Wasabi, …) — static keys + endpoint endpoint: my-minio:9000 # host:port, NO scheme insecure: true # true when the endpoint is plain HTTP bucket: my-prometheus-bucket # bucket must already exist region: us-east-1 accessKey: my-minio-username accessSecret: my-minio-password internalAccess: type: same-gvc # options: none, same-gvc, same-org, workload-list workloads: [] # with workload-list, e.g. //gvc/GVC/workload/NAME — cross-GVC callers allowed ``` ### Resources * `image` — The Prometheus container image. * `resources` — CPU and memory for the Prometheus container. Memory scales with the number of active series. ### TSDB and Retention * `retention.time` — How long samples are kept on the local volume (e.g. `15d`, `30d`, `1y`). * `retention.size` — Optional local size cap (e.g. `15GB`); empty means no cap. * `volumeset.capacity` — Volume size in GiB for the TSDB at `/prometheus`. With object storage enabled, local retention is only the near-term window — history beyond it lives in your bucket and is served by your Thanos query tier. ### Metrics * `scrapeInterval` — Global scrape interval, applied to the self-scrape and any `extraScrapeConfigs` jobs. * `externalLabels` — Identity labels attached to all uploaded and forwarded series. The template always injects a baseline `prometheus: ` label; your keys merge over it (overriding `prometheus` is allowed — required for the [HA pair recipe](#high-availability)). * `extraScrapeConfigs` — A raw YAML list of additional Prometheus `scrape_configs` entries, for example: ```yaml theme={null} extraScrapeConfigs: | - job_name: my-app static_configs: - targets: ["my-app.my-gvc.cpln.local:8080"] ``` ### Remote Write `remoteWrite` pushes this server's metrics to any Prometheus-remote-write-compatible store — for example this catalog's Grafana Mimir template — and is the recommended shape when the Thanos sidecar is disabled: ```yaml theme={null} thanos: sidecar: enabled: false remoteWrite: - url: http://my-mimir.my-gvc.cpln.local:8080/api/v1/push basicAuth: # optional username: my-user passwordSecretName: my-remote-write-password # opaque secret, must exist BEFORE install ``` The password is never stored in values — `passwordSecretName` names an opaque secret you create before install; the template mounts it as a `password_file` and grants the workload identity `reveal` on exactly that secret. ### Thanos Sidecar and Object Storage * `thanos.sidecar.enabled` — Runs the Thanos sidecar next to Prometheus, exposing the Store API (gRPC `10901`) so a Thanos Query tier can fan out queries to this server. Disable it when you forward metrics with `remoteWrite` instead. * `thanos.objectStorage.enabled` — The sidecar uploads completed TSDB blocks to your bucket (recommended for production durability). Requires `thanos.sidecar.enabled`. * `thanos.objectStorage.blockDuration` — Advanced: the TSDB block interval; the template pins min and max block duration to this value, which disables local compaction so blocks ship cleanly. Set `thanos.objectStorage.type` to `aws`, `gcp`, or `minio`, and configure that block. AWS and GCP use a Control Plane cloud identity — no credentials are stored. See [Prerequisites](#prerequisites) for the per-backend setup steps. | Field | Backends | Description | | ---------------------------- | -------------- | --------------------------------------------------- | | `bucket` | all | Target bucket. Must already exist. | | `region` | `aws`, `minio` | Bucket region. | | `cloudAccountName` | `aws`, `gcp` | Control Plane cloud account granting bucket access. | | `policyName` | `aws` | Bare name of the custom bucket-scoped IAM policy. | | `endpoint` | `minio` | S3 API address as `host:port` — no scheme. | | `insecure` | `minio` | `true` when the endpoint is plain HTTP. | | `accessKey` / `accessSecret` | `minio` | Static credentials with access to the bucket. | ### Internal Access Prometheus is **internal-only by design** — it has no built-in authentication, so this template never exposes a public endpoint. `internalAccess.type` controls which workloads can reach it: | Type | Description | | --------------- | --------------------------------------------------------------- | | `none` | No internal access allowed | | `same-gvc` | Allow access from all workloads in the same GVC | | `same-org` | Allow access from all workloads in the same organization | | `workload-list` | Allow access only from specific workloads listed in `workloads` | `workloads` entries take the form `//gvc/GVC_NAME/workload/WORKLOAD_NAME` and may name workloads in **other GVCs** — use this (or `same-org`) to allow a Thanos Query tier in another GVC to reach the Store API. To serve clients outside Control Plane, front Prometheus with your own authenticating proxy behind a custom domain — that proxy is yours to build and operate; the template does not provide one. ## Connecting Prometheus serves everything on port `9095` — its usual port `9090` is reserved by the platform, so point senders and datasources at `9095`: | What | Value | | --------------------------------------- | ---------------------------------------------------------------------- | | Remote-write ingest (from your senders) | `http://RELEASE_NAME-prometheus.GVC_NAME.cpln.local:9095/api/v1/write` | | PromQL / Grafana datasource | `http://RELEASE_NAME-prometheus.GVC_NAME.cpln.local:9095` | | Thanos Store API (same GVC) | `RELEASE_NAME-prometheus:10901` | | Thanos Store API (cross-GVC) | `RELEASE_NAME-prometheus.GVC_NAME.cpln.local:10901` | To push metrics here from another Prometheus: ```yaml theme={null} remote_write: - url: http://RELEASE_NAME-prometheus.GVC_NAME.cpln.local:9095/api/v1/write ``` A Thanos Query tier — such as this catalog's Thanos template — adds this server as a store endpoint. From another GVC, use the same service-level address (this workload is single-replica, so it reaches the one replica directly) and allow the caller via `internalAccess` (`same-org`, or `workload-list` naming the query workload). Cross-location internal traffic incurs egress charges — co-locate the query tier with its stores where practical. ## High Availability Prometheus has no cluster mode, so this template deliberately has no replicas knob — a second replica of the same install would just double-count. The upstream HA pattern is two independent, identically-configured instances deduplicated at query time. Install the template twice (e.g. releases `prom-a` and `prom-b`) with identical values except `externalLabels`: ```yaml theme={null} # prom-a # prom-b externalLabels: externalLabels: prometheus: my-prom prometheus: my-prom replica: a replica: b ``` Senders dual-write to both endpoints; a Thanos Query tier dedups via `--query.replica-label=replica`. Overriding `prometheus` to a shared value is required so the two label sets differ only in `replica`. ## Important Notes * **Prometheus has no built-in authentication, so this template never exposes a public endpoint.** To serve clients outside Control Plane, front it with your own authenticating proxy behind a custom domain. * **Remote-write password secrets must exist before install** — a missing `passwordSecretName` secret wedges the deployment waiting on it. * **Config changes ship via `helm upgrade`** (workload redeploy) — there is no hot reload. * **With object storage enabled, keep `retention.time` at least 3× `blockDuration`** (the default `15d`/`2h` satisfies this) — the sidecar needs blocks on the local volume long enough to upload them. * **The TSDB survives reinstall** — the volume is retained with a final snapshot, and local data resumes when a new install binds it. * **After a restart, readiness can take minutes on a large TSDB** — WAL replay holds the readiness endpoint at 503; this is normal recovery, not a failure. ## External References Official Prometheus documentation Configuration reference, including scrape\_configs Remote-write tuning and best practices Store API and block-upload sidecar reference Source code and releases View the source files, default values, and chart definition # Qdrant Source: https://docs.controlplane.com/template-catalog/templates/qdrant Deploy Qdrant on Control Plane using the Template Catalog. An open-source vector database for similarity search and RAG, with REST and gRPC APIs, persistent storage, optional API-key authentication, and scheduled volume snapshots. Covers the prerequisite key secret, access modes, client gotchas, and pairing with other AI templates. ## Overview Qdrant is an open-source (Apache-2.0) vector database for similarity search and retrieval-augmented generation. This template deploys a single Qdrant 1.18 server with persistent storage, REST and gRPC APIs, optional API-key authentication, and scheduled platform volume snapshots. There is no feature gating — everything in the upstream open-source build is available. ### Architecture * **Qdrant server** — A single-replica `stateful` workload (`{release}-qdrant`) serving the REST API and the built-in web dashboard on port `6333`, and the gRPC API on port `6334`. * **Persistent data** — One volume set mounted at `/qdrant/data` holding collections, segments, HNSW indexes, the write-ahead log, **and** Qdrant's own logical snapshots. Both durable directories live on the same volume, so a snapshot you create through the API survives restarts and redeployments. * **Authentication** *(optional)* — API keys come from a dictionary secret you create yourself and reference by name. A read-only key can be wired alongside the primary key. ### What Gets Created * **Stateful Qdrant Workload** — A single replica serving REST on `6333` and gRPC on `6334`. * **Volume Set** — Persistent storage at `/qdrant/data` with scheduled snapshots and a final snapshot on delete. * **Identity** — An identity bound to the workload, used to read the API-key secret. * **Policy** *(only when `auth.secretName` is set)* — `reveal` on exactly that one secret and nothing else. This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites **A default install has no prerequisites** — Qdrant needs no database, cache, or object store, and backups use platform volume snapshots rather than a cloud account or bucket. **If you want authentication** (required before you can enable public access), create a **dictionary** secret first and reference it by name in `auth.secretName`: ```bash theme={null} cpln secret create-dictionary --name my-qdrant-keys \ --entry api-key=$(openssl rand -hex 32) \ --entry read-only-api-key=$(openssl rand -hex 32) ``` * `api-key` — the primary key; full read and write access. * `read-only-api-key` — optional, only needed when `auth.readOnlyKey: true`. Installing with `publicAccess.enabled: true` and an empty `auth.secretName` fails at render time. An internet-reachable vector database without a key would expose every collection to anyone who finds the endpoint, so the chart refuses to render that combination — create the secret first. Install the template using your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} # ─── Image ──────────────────────────────────────────────────────── image: qdrant/qdrant:v1.18.3 # ─── Resources ──────────────────────────────────────────────────── # HNSW vector indexes are RAM-resident by default. Rough sizing: # memory ≈ vectors × dimensions × 4 bytes × 1.5 # (200k vectors × 1536 dims ≈ 1.8 GiB) resources: minCpu: 250m maxCpu: 1000m minMemory: 1Gi maxMemory: 4Gi # ─── Storage ────────────────────────────────────────────────────── volumeset: capacity: 20 # GiB (platform minimum 10) — collections, segments, WAL, Qdrant snapshots # ─── Backup (platform volume snapshots — no cloud account needed) ─ backup: enabled: true schedule: "0 3 * * *" # cron in UTC — daily 03:00 (hourly is the platform max) retention: 7d # how long each snapshot is kept (e.g. 7d, 720h, 30d) # ─── Authentication (optional, strongly recommended) ────────────── # PREREQUISITE dictionary secret — create it BEFORE install: # cpln secret create-dictionary --name my-qdrant-keys \ # --entry api-key=$(openssl rand -hex 32) \ # --entry read-only-api-key=$(openssl rand -hex 32) # Empty = no authentication; allowed ONLY while publicAccess is off. auth: secretName: "" # e.g. my-qdrant-keys — REQUIRED when publicAccess.enabled is true readOnlyKey: false # true = also wire `read-only-api-key` from the same secret # ─── Service ────────────────────────────────────────────────────── service: dashboard: true # serve the built-in web UI at /dashboard (its static shell is unauthenticated) maxRequestSizeMb: 32 # max POST body in MB; an over-size upsert is rejected with HTTP 400 and writes nothing telemetryDisabled: true # true = send no anonymous usage reports upstream # ─── Access ─────────────────────────────────────────────────────── publicAccess: enabled: false # true = REST API (+ dashboard) over HTTPS on the auto *.cpln.app endpoint; gRPC stays internal internalAccess: type: same-gvc # none | same-gvc | same-org | workload-list workloads: [] # used only with workload-list # workloads: # - //gvc/GVC_NAME/workload/WORKLOAD_NAME ``` ### Image and resources * `image` — The Qdrant server image. Pin a concrete tag. * `resources.minCpu` / `resources.maxCpu` / `resources.minMemory` / `resources.maxMemory` — CPU reservation and limit, memory reservation and limit for the Qdrant container. Memory is the sizing constraint. Vectors and their HNSW graphs are held in RAM unless a collection is explicitly created with `on_disk` vectors or index, so size roughly as `vectors × dimensions × 4 bytes × 1.5` — about 1.8 GiB for 200,000 vectors at 1536 dimensions. Raise `maxMemory` before loading a large collection. ### Storage * `volumeset.capacity` — Initial volume size in GiB. The platform minimum is 10, and the chart rejects anything smaller at render time. The single volume at `/qdrant/data` holds `storage/` (collections, segments, HNSW indexes, WAL) and `snapshots/` (Qdrant's own logical snapshots). Keeping both on the volume is deliberate: the upstream defaults place snapshots on the container's ephemeral layer, where they would be lost on every restart. ### Backup Backups are **platform volume snapshots** of the data volume — no cloud account, bucket, or IAM policy is required. * `backup.enabled` — When `true` (default), the volume set takes snapshots on `backup.schedule`. * `backup.schedule` — Cron expression in UTC. Hourly is the platform maximum frequency. * `backup.retention` — How long each snapshot is kept (for example `7d`, `720h`, `30d`). The volume set is also configured to take a final snapshot when it is deleted, retained for `backup.retention`. That final snapshot is taken regardless of `backup.enabled` — setting it to `false` disables *scheduled* snapshots only. These platform snapshots are independent of Qdrant's own collection snapshots, which you create through the API and which live on the same volume. ### Authentication * `auth.secretName` — Name of your prerequisite dictionary secret. Empty (the default) leaves the API unauthenticated, which is permitted **only** while `publicAccess.enabled` is `false`. * `auth.readOnlyKey` — When `true`, also wires the `read-only-api-key` entry from the same secret. Setting it without `auth.secretName` fails at render time. Clients authenticate with an `api-key` header. The read-only key can search, scroll, and read collections, but write operations are rejected with `403 Forbidden: Global manage access is required`. Qdrant's health paths (`/healthz`, `/livez`, `/readyz`) stay reachable without a key so the platform probes keep working with authentication on. `/metrics` is **not** exempt — it returns `401` without a key. ### Service * `service.dashboard` — Serves Qdrant's built-in web UI at `/dashboard`. The static shell of that UI loads without a key even when authentication is on (its API calls do not), so set this to `false` on any publicly exposed instance. * `service.maxRequestSizeMb` — Maximum POST body size in MB. An over-size upsert is rejected immediately with **HTTP 400** and the message `JSON payload (N bytes) is larger than allowed (limit: M bytes).` — nothing is partially written. * `service.telemetryDisabled` — When `true` (default), Qdrant sends no anonymous usage reports upstream. ### Access * `publicAccess.enabled` — When `true`, the REST API (and the dashboard, if enabled) is served over HTTPS on the automatically assigned `*.cpln.app` canonical endpoint. gRPC always stays internal. Requires `auth.secretName`. When `false` (default), external requests to the canonical hostname are refused with `403 RBAC: access denied`. * `internalAccess.type` — Internal firewall scope of the workload: | Type | Description | | --------------- | ---------------------------------------------------------------------- | | `none` | No internal access — in-GVC traffic is refused. | | `same-gvc` | Allow access from all workloads in the same GVC (default). | | `same-org` | Allow access from all workloads in the same organization. | | `workload-list` | Allow access only from workloads listed in `internalAccess.workloads`. | * `internalAccess.workloads` — Workload links (`//gvc/GVC_NAME/workload/WORKLOAD_NAME`), used only with `workload-list`. ## Connecting | Target | Address | Credentials | | -------------------------------------- | --------------------------------------------------- | ---------------------------------------------- | | Internal REST (same GVC) | `http://{release}-qdrant.{gvc}.cpln.local:6333` | `api-key` header when `auth.secretName` is set | | Internal gRPC (same GVC) | `{release}-qdrant.{gvc}.cpln.local:6334` | Same API key | | Public REST + dashboard *(if enabled)* | `https://.cpln.app` (UI at `/dashboard`) | `api-key` header | | Health | `GET /healthz`, `/livez`, `/readyz` on `6333` | None — exempt from the API key | The canonical hostname appears under `status.canonicalEndpoint` (`cpln workload get {release}-qdrant -o yaml`). Public traffic is HTTPS at the platform edge; same-GVC traffic is plain HTTP and gRPC carried over the mesh's own mTLS. ```bash theme={null} curl -H "api-key: YOUR_API_KEY" \ http://{release}-qdrant.{gvc}.cpln.local:6333/collections ``` ### Python client — disable TLS for in-GVC calls `qdrant-client` silently switches to TLS as soon as an `api_key` is supplied. Against the internal endpoint, which speaks plain HTTP and gRPC, the call then hangs instead of failing cleanly — the symptom is a gRPC `DEADLINE_EXCEEDED`. Pass `https=False` (or use explicit `http://` URLs) for any client running inside the GVC. ```python theme={null} from qdrant_client import QdrantClient client = QdrantClient( host="{release}-qdrant.{gvc}.cpln.local", port=6333, grpc_port=6334, prefer_grpc=True, api_key="YOUR_API_KEY", https=False, # REQUIRED: qdrant-client turns TLS on automatically when api_key is set ) ``` ## Using Qdrant with Other Templates Qdrant is the retrieval tier of a RAG stack, and every other component reaches it over internal GVC DNS with no public exposure. Deploy them into the same GVC and wire them by hostname: | Template | Internal address | Role | | ------------------------------------------------ | ----------------------------------------------------- | ---------------------------------------------- | | Qdrant | `http://{release}-qdrant.{gvc}.cpln.local:6333` | Vector store | | [Ollama](/template-catalog/templates/ollama) | `http://{release}-ollama.{gvc}.cpln.local:11434` | Local embedding and chat models | | [LiteLLM](/template-catalog/templates/litellm) | `http://{release}-litellm.{gvc}.cpln.local:4000` | OpenAI-compatible gateway to hosted models | | [Langfuse](/template-catalog/templates/langfuse) | `http://{release}-langfuse-web.{gvc}.cpln.local:3000` | Tracing for the retrieval and generation calls | A retrieval service running in the GVC embeds with Ollama, stores in Qdrant, and generates through LiteLLM: ```python theme={null} import httpx from qdrant_client import QdrantClient from qdrant_client.models import VectorParams, Distance, PointStruct GVC = "my-gvc" qdrant = QdrantClient(host=f"my-qdrant.{GVC}.cpln.local", port=6333, grpc_port=6334, prefer_grpc=True, api_key=API_KEY, https=False) qdrant.create_collection("docs", vectors_config=VectorParams(size=768, distance=Distance.COSINE)) # embed with the in-GVC ollama workload vec = httpx.post(f"http://my-ollama.{GVC}.cpln.local:11434/api/embeddings", json={"model": "nomic-embed-text", "prompt": text}).json()["embedding"] qdrant.upsert("docs", points=[PointStruct(id=1, vector=vec, payload={"text": text})]) # retrieve, then generate through the in-GVC litellm gateway hits = qdrant.query_points("docs", query=vec, limit=4).points httpx.post(f"http://my-litellm.{GVC}.cpln.local:4000/v1/chat/completions", json={...}) ``` [Open WebUI](/template-catalog/templates/open-webui) can also use Qdrant as its vector store instead of the bundled Chroma, via the environment variables `VECTOR_DB=qdrant`, `QDRANT_URI=http://{release}-qdrant.{gvc}.cpln.local:6333`, and `QDRANT_API_KEY`. The `open-webui` template does not expose these as values yet — set them on the deployed workload. ## Important Notes * **Public access requires an API key.** Installing with `publicAccess.enabled: true` and an empty `auth.secretName` fails at render time. Create the dictionary secret first. * **In-GVC clients must disable TLS.** `qdrant-client` turns TLS on automatically when an `api_key` is set and then hangs against the internal endpoint; the symptom is a gRPC `DEADLINE_EXCEEDED`. Pass `https=False` or use plain `http://` URLs. * **The `/dashboard` shell loads without an API key** (its API calls do not). Set `service.dashboard: false` when Qdrant is publicly exposed. * **Single replica by design.** Data survives restarts and upgrades on the volume set, but any upgrade or reschedule is a real outage of roughly 60–90 seconds — measured at 79 seconds with 313 consecutive failed requests — not a blip. Budget about 2.5 minutes for a configuration change to roll fully, and plan writes around it. Qdrant's distributed mode is supported upstream but requires stable per-peer addressing that this version does not implement; there is no `replicas` knob. * **Over-size requests return HTTP 400, not 413.** A body larger than `service.maxRequestSizeMb` is rejected outright and no points are written — batch large upserts or raise the limit. * **Uninstall deletes the volume set.** A final snapshot is retained for `backup.retention`, and your own API-key secret is left untouched. * **Memory is the sizing constraint.** Vectors and HNSW graphs stay in RAM unless a collection is created with `on_disk` vectors or index. Raise `resources.maxMemory` before loading large collections. ## External References Official Qdrant documentation API-key authentication and read-only key behavior Create collections, choose distance metrics, and tune HNSW Qdrant's own collection snapshot and restore API Sizing guidance for vectors, indexes, and on-disk storage View the source files, default values, and chart definition # RabbitMQ Source: https://docs.controlplane.com/template-catalog/templates/rabbitmq Deploy RabbitMQ on Control Plane using the Template Catalog. Covers configuration, volumes, scaling, and single-replica message broker with built-in management UI. ## Overview RabbitMQ is a widely-used open-source message broker supporting AMQP and other messaging protocols. This template deploys a single-replica RabbitMQ instance with persistent storage and a built-in management UI. This template does not create a GVC. You must deploy it into an existing GVC. ### What Gets Created * **Stateful Workload** — A single-replica RabbitMQ container with the management plugin enabled. * **Volume Set** — Persistent storage for RabbitMQ node data at `/var/lib/rabbitmq`. * **Secret** — An opaque secret containing the `rabbitmq.conf` configuration file, mounted into the container at startup. * **Identity & Policy** — An identity bound to the workload with `reveal` access to the config secret. ## Installation To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} image: repository: rabbitmq:3-management memory: 250Mi cpu: 200m firewall: internal_inboundAllowType: same-gvc # Options: same-gvc, same-org # external_inboundAllowCIDR: 0.0.0.0/0 # external_outboundAllowCIDR: 0.0.0.0/0 rabbitmq_conf: listeners_tcp_default: 5672 default_user: user default_pass: changeMe env: RABBITMQ_CONFIG_FILE: /etc/rabbitmq/rabbitmq.conf volumeset: volume: initialCapacity: 10 # In GiB. For high-throughput-ssd minimum is 1000 fileSystemType: ext4 # ext4 / xfs performanceClass: general-purpose-ssd # general-purpose-ssd / high-throughput-ssd ``` ### Credentials * `rabbitmq_conf.default_user` — RabbitMQ admin username. **Change before deploying to production.** * `rabbitmq_conf.default_pass` — RabbitMQ admin password. **Change before deploying to production.** These values are only applied on first startup when there is no existing node data. Updating them after the initial deployment will have no effect on the running instance. To change credentials on an existing instance, use RabbitMQ's management commands (e.g. `rabbitmqctl change_password`). ### Resources * `cpu` — CPU allocated to the RabbitMQ workload. * `memory` — Memory allocated to the RabbitMQ workload. ### Storage * `volumeset.volume.initialCapacity` — Initial volume size in GiB. For `high-throughput-ssd`, the minimum is 1000 GiB. * `volumeset.volume.fileSystemType` — `ext4` or `xfs`. * `volumeset.volume.performanceClass` — `general-purpose-ssd` or `high-throughput-ssd`. ### Firewall * `firewall.internal_inboundAllowType` — Controls which workloads can connect to RabbitMQ internally: | Value | Description | | ---------- | ------------------------------------------------------------- | | `same-gvc` | Allow access from all workloads in the same GVC (recommended) | | `same-org` | Allow access from all workloads in the same organization | * `firewall.external_inboundAllowCIDR` — Optional. Comma-separated list of CIDR ranges allowed to reach RabbitMQ from the internet (e.g. `0.0.0.0/0`). * `firewall.external_outboundAllowCIDR` — Optional. Comma-separated list of CIDR ranges RabbitMQ is allowed to reach externally. ### Connecting to RabbitMQ Once deployed, connect to RabbitMQ from within the same GVC using: ```text theme={null} RELEASE_NAME-rabbitmq.GVC_NAME.cpln.local:5672 ``` The management UI is available on port `15672` via the workload's external endpoint (requires `firewall.external_inboundAllowCIDR` to be set). ### Ports | Port | Protocol | Description | | ------- | -------- | ------------------------------------------------------------------------------- | | `5672` | AMQP | Primary messaging port (configurable via `rabbitmq_conf.listeners_tcp_default`) | | `15672` | HTTP | Management UI | | `15692` | HTTP | Prometheus metrics | ## External References Official RabbitMQ documentation View the source files, default values, and chart definition # Redis Source: https://docs.controlplane.com/template-catalog/templates/redis Deploy Redis on Control Plane using the Template Catalog. Covers configuration, volumes, scaling, and master-replica clustering with Sentinel automatic failover. ## Overview Redis is a high-performance in-memory data store. This template deploys a Redis master-replica cluster with Redis Sentinel for automatic failover and leader election. Both tiers run Redis 8 by default; `redis:7.4` remains a supported value if you need to pin the previous line. This template does not create a GVC. You must deploy it into an existing GVC. ### What Gets Created * **Stateful Redis Workload** — (`RELEASE_NAME-redis`): a master-replica cluster. Replica 0 starts as the primary; all others replicate from it. * **Stateful Sentinel Workload** — (`RELEASE_NAME-sentinel`): monitors the Redis cluster and orchestrates automatic failover when the primary becomes unavailable. * **Secret** — An opaque secret containing the Redis server configuration (`redis.conf`), mounted into each Redis container. * **Secret** — An opaque secret containing the Sentinel configuration (`sentinel.conf`), mounted into each Sentinel container. * **Secret** *(optional)* — A dictionary secret holding the Redis auth password, created when `redis.auth.password.enabled` is true. * **Secret** *(optional)* — A dictionary secret holding the Sentinel auth password, created when `sentinel.auth.password.enabled` is true. * **Identity & Policy** — Separate identities and policies for the Redis and Sentinel workloads, each with `reveal` access to their respective secrets, and cloud storage access when backup is enabled. * **Volume Set** *(optional)* — Persistent storage for Redis data, created when `redis.persistence.enabled` is true. * **Volume Set** *(optional)* — Persistent storage for Sentinel state, created when `sentinel.persistence.enabled` is true. * **Domain** *(optional)* — External domain resources for Redis and/or Sentinel when `publicAccess.enabled` is true. * **Backup Cron Workload** *(optional)* — A scheduled backup job that writes Redis snapshots to AWS S3 or GCS. ## Installation This template has no external prerequisites unless backup or public access is enabled. To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} redis: image: redis:8 resources: cpu: 200m memory: 256Mi minCpu: 80m minMemory: 128Mi replicas: 3 timeoutSeconds: 15 multiZone: false replicaDirect: false auth: fromSecret: enabled: false name: example-redis-auth-password passwordKey: password password: enabled: false value: change-me-redis-password serverCommand: redis-server # extraArgs: "--maxclients 20000 --maxmemory 200mb --maxmemory-policy allkeys-lru" publicAccess: enabled: false address: redis-test.example-cpln.com firewall: internal_inboundAllowType: same-gvc # Options: same-org / same-gvc / workload-list # external_inboundAllowCIDR: 0.0.0.0/0 # inboundAllowWorkload: # - //gvc/GVC_NAME/workload/WORKLOAD_NAME # external_outboundAllowCIDR: 0.0.0.0/0 env: [] tags: {} requestRetryPolicy: {} replication: backlogSize: 1gb timeout: 300 slaveOutputBufferLimit: "2gb 512mb 300" probes: readiness: failureThreshold: 10 initialDelaySeconds: 10 periodSeconds: 5 timeoutSeconds: 4 liveness: failureThreshold: 5 initialDelaySeconds: 30 periodSeconds: 10 timeoutSeconds: 5 exporter: enabled: false # adds a redis_exporter sidecar that exposes metrics at :9121/metrics image: oliver006/redis_exporter:v1.67.0-alpine resources: cpu: 50m memory: 64Mi minCpu: 25m minMemory: 32Mi dropMetrics: [] # e.g., ["redis_command_.*", "redis_keyspace_.*"] dataDir: /data persistence: enabled: false volumes: data: initialCapacity: 10 # In GiB performanceClass: general-purpose-ssd # general-purpose-ssd / high-throughput-ssd (Min 1000 GiB) fileSystemType: ext4 # ext4 / xfs snapshots: retentionDuration: 7d schedule: "0 0 * * *" # UTC autoscaling: maxCapacity: 100 # In GiB minFreePercentage: 20 scalingFactor: 1.2 # customEncryption: # enabled: true # region: aws-us-east-1 # keyId: arn:aws:kms:us-east-1:1234567890:key/YOUR_KEY_ID sentinel: image: redis:8 resources: cpu: 200m memory: 256Mi minCpu: 80m minMemory: 128Mi replicas: 3 timeoutSeconds: 10 multiZone: false replicaDirect: false quorumAutoCalculation: true # Quorum = (replicas / 2) + 1 quorumOverride: null # Only used if quorumAutoCalculation is false auth: fromSecret: enabled: false name: example-redis-auth-password passwordKey: password password: enabled: false value: change-me-sentinel-password publicAccess: enabled: false address: redis-sentinel-test.example-cpln.com firewall: internal_inboundAllowType: same-gvc # Options: same-org / same-gvc # external_inboundAllowCIDR: 0.0.0.0/0 # inboundAllowWorkload: # - //gvc/GVC_NAME/workload/WORKLOAD_NAME # external_outboundAllowCIDR: 0.0.0.0/0 env: [] tags: {} requestRetryPolicy: {} persistence: enabled: false volumes: data: initialCapacity: 10 # In GiB performanceClass: general-purpose-ssd fileSystemType: ext4 snapshots: retentionDuration: 7d schedule: "0 0 * * *" autoscaling: maxCapacity: 50 # In GiB minFreePercentage: 20 scalingFactor: 1.2 # customEncryption: # enabled: true # region: aws-us-east-1 # keyId: arn:aws:kms:us-east-1:1234567890:key/YOUR_KEY_ID backup: enabled: false image: ghcr.io/controlplane-com/backup-images/redis-backup:1.0.0 schedule: "0 2 * * *" # daily at 2am UTC resources: cpu: 100m memory: 128Mi provider: aws # Options: aws or gcp aws: bucket: my-backup-bucket region: us-east-1 cloudAccountName: my-backup-cloudaccount policyName: my-backup-policy prefix: redis/backups gcp: bucket: my-backup-bucket cloudAccountName: my-backup-cloudaccount prefix: redis/backups grafana: dashboard: enabled: false # K8s clusters only — set to true to provision via Grafana Operator folder: Redis # Grafana folder the dashboard is placed in datasource: metrics # name of the Prometheus datasource in your Grafana instance instanceSelector: matchLabels: dashboards: grafana # must match the label on your Grafana CR ``` ### Redis Version Both the data nodes and Sentinel run `redis:8` by default. Redis 8 is the current stable line and adds AGPLv3 as a license option alongside the existing RSALv2/SSPLv1 terms. To stay on the previous line, pin both images: ```yaml theme={null} redis: image: redis:7.4 sentinel: image: redis:7.4 ``` **Upgrading an existing Redis 7.4 deployment to Redis 8 is one-way.** Redis 8 loads a 7.4 data file directly, so upgrading with persistence enabled keeps your data — but once a node has written its data file under Redis 8, a 7.4 image can no longer load it. Snapshot the volume set before upgrading if you want a rollback path. The upgrade rolls the pods one at a time, which replaces the master and triggers a Sentinel failover. Clients must discover the master through Sentinel rather than pinning replica 0 — writing to the old replica 0 after the roll returns `READONLY You can't write against a read only replica.` ### Authentication Authentication is disabled by default. Both the Redis workload and the Sentinel workload support independent auth configuration. Only one method can be enabled at a time per workload. **Option 1 — Inline password** (creates a secret automatically): ```yaml theme={null} redis: auth: password: enabled: true value: change-me-redis-password ``` **Option 2 — Reference an existing secret**: ```yaml theme={null} redis: auth: fromSecret: enabled: true name: my-redis-secret # Name of an existing Control Plane secret passwordKey: password # Key within the secret containing the password ``` Sentinel can be configured with a separate password for Sentinel-to-Sentinel communication using the same options under `sentinel.auth`. **Do not rotate the Redis password on a running cluster.** Changing `redis.auth.password.value` (or the referenced secret) stalls the rolling update: the first restarted node cannot replicate from the not-yet-restarted master, logging `Unable to AUTH to MASTER: -WRONGPASS invalid username-password pair or user is disabled.` — its readiness probe never passes, so the roll never advances. Re-apply the previous password to recover. To change the password, uninstall and reinstall, or accept a planned outage. ### Redis Cluster * `redis.replicas` — Number of Redis replicas. Defaults to `3`. Replica 0 is always the initial primary. * `redis.resources.cpu` / `redis.resources.memory` — Maximum CPU and memory per replica. * `redis.resources.minCpu` / `redis.resources.minMemory` — Minimum CPU and memory guaranteed per replica. * `redis.multiZone` — Spread replicas across availability zones within the location. * `redis.extraArgs` — Additional Redis server arguments (e.g. `--maxmemory 200mb --maxmemory-policy allkeys-lru`). ### Sentinel Sentinel monitors the Redis cluster and automatically promotes a replica when the primary fails. * `sentinel.replicas` — Number of Sentinel replicas. A minimum of 3 is required for a majority quorum. * `sentinel.quorumAutoCalculation` — When `true`, the quorum is calculated automatically as `(replicas / 2) + 1`. Set to `false` to use `sentinel.quorumOverride`. * `sentinel.quorumOverride` — Manual quorum value, used only when `quorumAutoCalculation` is `false`. * `sentinel.resources.cpu` / `sentinel.resources.memory` — Maximum CPU and memory per Sentinel replica. * `sentinel.resources.minCpu` / `sentinel.resources.minMemory` — Minimum CPU and memory guaranteed per Sentinel replica. * `sentinel.multiZone` — Spread Sentinel replicas across availability zones. Sentinel marks a master down after 5 seconds without a response. In testing on Redis 8, a full failover — detection, election, promotion of a replica, and clients seeing the new master — completed 6.7–8.4 seconds after the master stopped responding, and the old master rejoined as a replica once it recovered. ### Replication Tuning The `redis.replication` block controls low-level replication behavior. The defaults are sized for production workloads and typically do not need adjustment. * `replication.backlogSize` — Replication backlog size. The default `1gb` covers \~5 minutes of disconnect at \~3 MB/s of writes. The Redis default of `1mb` will trigger a full RDB resync on any brief disconnect. * `replication.timeout` — Timeout in seconds for full-resync transfers. The default `300s` covers datasets up to \~30 GB. The Redis default of `60s` is too low for large datasets. * `replication.slaveOutputBufferLimit` — Format: ` `. The default `"2gb 512mb 300"` sustains full resyncs of large datasets at high write rates. The Redis default `"256mb 64mb 60"` can cause the master to kill a replica mid-stream. ### Health Checks Probe settings for the Redis workload are configurable under `redis.probes`. This is most useful when persistence is enabled and large datasets require additional time to load before the replica is ready. The startup probe window is derived from the readiness probe settings: ``` startup window = initialDelaySeconds + (30 × periodSeconds) ``` The default (`10 + 30×5 = 160s`) is sufficient for small datasets. If AOF load takes longer, increase `periodSeconds`: ```yaml theme={null} redis: probes: readiness: initialDelaySeconds: 35 # clears the AOF load window periodSeconds: 20 # startup window = 35 + (30×20) = 635s ``` | Field | Default | Description | | ------------------------------- | ------- | ---------------------------------------------------------------------- | | `readiness.initialDelaySeconds` | `10` | Seconds to wait before first readiness check | | `readiness.periodSeconds` | `5` | How often to run the readiness check; also controls the startup window | | `readiness.failureThreshold` | `10` | Number of failures before the replica is marked not ready | | `readiness.timeoutSeconds` | `4` | Timeout per probe attempt | | `liveness.initialDelaySeconds` | `30` | Seconds before the first liveness check | | `liveness.periodSeconds` | `10` | How often to run the liveness check | | `liveness.failureThreshold` | `5` | Number of failures before the container is restarted | | `liveness.timeoutSeconds` | `5` | Timeout per probe attempt | ### Metrics A `redis_exporter` sidecar can be enabled to expose Prometheus metrics. When enabled, Control Plane scrapes `:9121/metrics` automatically every 30 seconds and makes the metrics available in the console. The default exporter image is verified against Redis 8, including against a password-protected server. * `redis.exporter.enabled` — Set to `true` to add the exporter sidecar to each Redis replica. * `redis.exporter.image` — Exporter image (default: `oliver006/redis_exporter:v1.67.0-alpine`). * `redis.exporter.resources` — CPU and memory limits/requests for the sidecar container. * `redis.exporter.dropMetrics` — List of regex patterns matched against metric names. Use to filter high-cardinality series before they are stored. ```yaml theme={null} redis: exporter: enabled: true dropMetrics: - "redis_commands_latencies_usec_bucket" - "redis_latency_percentiles_usec" ``` ### Grafana Dashboard A pre-built Grafana dashboard can be provisioned automatically via the [Grafana Operator](https://grafana.github.io/grafana-operator/). This is opt-in and intended for Kubernetes clusters running the Grafana Operator — on Control Plane's managed platform where no operator is present, leave this disabled. Enabling the dashboard requires `redis.exporter.enabled: true` for the Redis-specific panels. CPU and memory panels are always available. **Included panels:** | Panel | Requires exporter | | ----------------- | ----------------- | | CPU Usage | No | | Memory Usage | No | | Connected Clients | Yes | | Redis Memory | Yes | | Commands/sec | Yes | | Cache Hit Rate | Yes | The dashboard includes template variables for datasource, GVC, workload, and replica so you can filter by deployment without editing the dashboard. **Prerequisites:** 1. The [Grafana Operator](https://grafana.github.io/grafana-operator/) must be installed in your cluster with a Kubernetes secret containing your Grafana service account token: ```bash theme={null} kubectl create secret generic grafana-admin-credentials \ --from-literal=GF_SECURITY_ADMIN_USER=admin \ --from-literal=GF_SECURITY_ADMIN_PASSWORD= \ -n grafana-operator ``` 2. A `Grafana` CR with a label matching `grafana.instanceSelector.matchLabels`: ```yaml theme={null} apiVersion: grafana.integreatly.org/v1beta1 kind: Grafana metadata: name: grafana labels: dashboards: grafana spec: external: url: https://your-org.grafana.cpln.io adminPassword: name: grafana-admin-credentials key: GF_SECURITY_ADMIN_PASSWORD ``` **Configuration:** * `grafana.dashboard.enabled` — Set to `true` to provision the `GrafanaDashboard` CRD. * `grafana.folder` — Grafana folder the dashboard appears in (default: `Redis`). * `grafana.datasource` — Name of the Prometheus datasource configured in your Grafana instance (default: `metrics`). * `grafana.instanceSelector.matchLabels` — Label selector that must match the label on your `Grafana` CR. ### Storage Persistence is disabled by default. When enabled, a Volume Set is created to store data across restarts. **Redis persistence:** * `redis.persistence.enabled` — Enable persistent storage for Redis data. * `redis.persistence.volumes.data.initialCapacity` — Initial volume size in GiB. Minimum 1000 GiB for `high-throughput-ssd`. * `redis.persistence.volumes.data.performanceClass` — `general-purpose-ssd` or `high-throughput-ssd`. * `redis.persistence.volumes.data.fileSystemType` — `ext4` or `xfs`. * `redis.persistence.volumes.data.snapshots.retentionDuration` — How long to retain volume snapshots. * `redis.persistence.volumes.data.snapshots.schedule` — Cron expression for snapshot frequency. * `redis.persistence.volumes.data.autoscaling.maxCapacity` — Maximum volume size in GiB. * `redis.persistence.volumes.data.autoscaling.minFreePercentage` — Trigger scaling when free space drops below this percentage. * `redis.persistence.volumes.data.autoscaling.scalingFactor` — Multiply current capacity by this factor when scaling up. **Sentinel persistence** uses the same options under `sentinel.persistence`. ### Firewall Both Redis and Sentinel firewall settings are configured independently. * `redis.firewall.internal_inboundAllowType` / `sentinel.firewall.internal_inboundAllowType`: | Value | Description | | --------------- | ------------------------------------------------------------- | | `same-gvc` | Allow access from all workloads in the same GVC (recommended) | | `same-org` | Allow access from all workloads in the same organization | | `workload-list` | Allow access only from specific workloads | * `redis.firewall.inboundAllowWorkload` — List of specific workload links when using `workload-list`. * `redis.firewall.external_inboundAllowCIDR` — Comma-separated CIDR ranges for external inbound access. * `redis.firewall.external_outboundAllowCIDR` — Comma-separated CIDR ranges for external outbound access. ### Public Access Redis and Sentinel can be exposed over the internet via TCP using Control Plane's domain resource with per-replica port routing. #### Prerequisites 1. **Enable Dedicated Load Balancer on your GVC** — This is required for arbitrary TCP port routing. Navigate to your GVC settings in the Control Plane console and enable the Dedicated Load Balancer option. 2. **Add DNS records before deploying** — Control Plane verifies domain ownership when the domain resource is first created. If the records are not in place, the deploy will fail with an `Unable to apply domain` error. Add the following records in your DNS provider for each address you configure, and **disable any proxy** (TCP must pass through directly): | Type | Name | Value | | ----- | ------------------- | ------------------------------------- | | TXT | `_cpln-` | Your Control Plane org name or org ID | | CNAME | `` | `.cpln.app` | Your GVC alias is visible under GVC settings in the Control Plane console. #### Configuration Enable public access for Redis and/or Sentinel, set the subdomain address, and open the external firewall: ```yaml theme={null} redis: publicAccess: enabled: true address: redis.your-domain.com firewall: internal_inboundAllowType: same-gvc external_inboundAllowCIDR: "0.0.0.0/0" # or restrict to specific CIDRs sentinel: publicAccess: enabled: true address: redis-sentinel.your-domain.com firewall: internal_inboundAllowType: same-gvc external_inboundAllowCIDR: "0.0.0.0/0" ``` `external_inboundAllowCIDR` must be set when public access is enabled — without it, inbound traffic from the internet will be blocked even though the domain is configured. #### Port Mapping Each replica is assigned its own external port. Port numbers increment per replica starting at: * **Redis**: `6380`, `6381`, `6382`, ... (replica 0, 1, 2, ...) * **Sentinel**: `26380`, `26381`, `26382`, ... (replica 0, 1, 2, ...) #### Connecting Externally ```bash theme={null} # Redis replica 0 (append -a if auth is enabled) redis-cli -h redis.your-domain.com -p 6380 ping # Redis replica 1 redis-cli -h redis.your-domain.com -p 6381 ping # Sentinel replica 0 redis-cli -h redis-sentinel.your-domain.com -p 26380 ping ``` After deploying, verify the domain is resolving correctly: ```bash theme={null} dig redis.your-domain.com CNAME # should return .cpln.app dig .cpln.app # should return an IP address ``` ### Connecting to Redis Connect to Redis internally from within the same GVC: ```text theme={null} RELEASE_NAME-redis.GVC_NAME.cpln.local:6379 ``` For Sentinel-aware clients, connect through Sentinel to always reach the current primary: ```text theme={null} RELEASE_NAME-sentinel.GVC_NAME.cpln.local:26379 ``` The Sentinel master name is `mymaster`. ### Ports | Workload | Port | Description | | ----------------- | --------------------- | -------------------------------------------------- | | Redis | `6379` | Internal Redis data port | | Sentinel | `26379` | Internal Sentinel discovery port | | Redis (public) | `6380`, `6381`, ... | One port per replica when public access is enabled | | Sentinel (public) | `26380`, `26381`, ... | One port per replica when public access is enabled | ## Backup Backup is disabled by default. When enabled, a cron workload runs on the configured schedule and uploads a Redis snapshot to AWS S3 or GCS. The backup image is verified against both Redis 7.4 and Redis 8. * `backup.enabled` — Enable scheduled backups. * `backup.schedule` — Cron expression for backup frequency (default: daily at 2am UTC). Step expressions such as `*/5 * * * *` are supported. * `backup.provider` — `aws` or `gcp`. * `backup.resources.cpu` / `backup.resources.memory` — Resources for the backup cron container. Switching `backup.provider` between `aws` and `gcp` on an existing release leaves the previous provider's cloud binding on the workload identity — the rendered manifest is correct, but identity updates are merged rather than replaced. Inspect the identity (`cpln identity get RELEASE_NAME-redis-identity --gvc GVC_NAME`) after a provider switch and remove the stale binding, or reinstall the release, if least privilege matters to you. ### AWS S3 Before enabling backup with `provider: aws`, complete the following in your AWS account: 1. Create an S3 bucket. Set `backup.aws.bucket` to its name and `backup.aws.region` to its region. 2. If you do not have a Cloud Account set up, refer to the docs to [Create a Cloud Account](/guides/create-cloud-account). Set `backup.aws.cloudAccountName` to its name. 3. Create an IAM policy with the following JSON, replacing `YOUR_BUCKET_NAME`: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket", "s3:GetObjectVersion", "s3:DeleteObjectVersion" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET_NAME", "arn:aws:s3:::YOUR_BUCKET_NAME/*" ] } ] } ``` 4. Set `backup.aws.policyName` to the name of the policy created in step 3. 5. Set `backup.aws.prefix` to the folder path where backups will be stored. ### GCS Before enabling backup with `provider: gcp`, complete the following in your GCP account: 1. Create a GCS bucket. Set `backup.gcp.bucket` to its name. 2. If you do not have a Cloud Account set up, refer to the docs to [Create a Cloud Account](/guides/create-cloud-account). Set `backup.gcp.cloudAccountName` to its name. 3. Add the **Storage Admin** role to the GCP service account associated with the Cloud Account. 4. Set `backup.gcp.prefix` to the folder path where backups will be stored. ## Restoring a Backup Download the `.rdb` backup file from your bucket and restore it into a running Redis instance. For GCS, replace `aws s3 cp s3://...` with `gsutil cp gs://...`. ```sh theme={null} aws s3 cp s3://BUCKET_NAME/PREFIX/BACKUP_FILE.rdb /tmp/dump.rdb redis-cli \ -h RELEASE_NAME-redis.GVC_NAME.cpln.local \ -p 6379 \ --rdb /tmp/dump.rdb ``` ## Important Notes * **The Redis 7.4 to 8 upgrade is one-way.** Data written under Redis 8 cannot be loaded by a 7.4 image. Snapshot the volume set first if you need a rollback path. * **Never rotate the Redis password in place.** It deadlocks the rolling update. Re-apply the previous password to recover; uninstall and reinstall to change it. * **Always discover the master through Sentinel.** The master moves during failovers and rolling upgrades, so a client pinned to replica 0 will hit `READONLY` errors after either. * **Enable `sentinel.persistence` in production.** Persisted Sentinel state lets replicas rejoin the real master after a restart instead of assuming replica 0 is still the master. * **Firewall changes take about 2.5 minutes to propagate.** After tightening `internal_inboundAllowType` or the allowed workload list, previously-allowed clients may keep connecting briefly before the new rule takes effect. ## External References Official Redis documentation Redis Sentinel setup and client configuration Source code for the Redis backup container image View the source files, default values, and chart definition # Redis Cluster Source: https://docs.controlplane.com/template-catalog/templates/redis-cluster Deploy Redis Cluster on Control Plane using the Template Catalog. Covers configuration, volumes, scaling, and native data sharding across multiple primary nodes with replication. ## Overview Redis Cluster is a distributed Redis deployment with automatic data sharding across multiple primary nodes and built-in replication. This template deploys a native Redis Cluster with 3 primary shards and 3 replicas, providing both horizontal scalability and high availability without an external Sentinel process. This template does not create a GVC. You must deploy it into an existing GVC. ### What Gets Created * **Stateful Redis Cluster Workload** — (`RELEASE_NAME-redis-cluster`): all replicas managed together. Replica 0 initializes the cluster once all nodes are healthy. * **Volume Set** — Persistent storage for each Redis node's data directory. * **Secret** — An opaque secret containing the Redis cluster configuration (`redis.conf`), mounted into each container. * **Secret** — An opaque secret containing the cluster initialization script, mounted and executed at startup. * **Secret** *(optional)* — A dictionary secret holding the Redis password, created when `redis.password` is set. * **Identity & Policy** — An identity bound to the workload with `reveal` access to the config, startup script, and auth secrets, and cloud storage access when backup is enabled. * **Backup Cron Workload** *(optional)* — A scheduled backup job that writes one snapshot per primary shard to AWS S3 or GCS. ## Installation This template has no external prerequisites unless backup is enabled. To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} replicas: 6 # minimum value is 6 port: 6379 memory: 250Mi cpu: 200m internalAccess: type: same-gvc # options: none, same-gvc, same-org, workload-list workloads: # Note: can only be used if type is same-gvc or workload-list #- //gvc/GVC_NAME/workload/WORKLOAD_NAME volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) autoscaling: enabled: false maxCapacity: 100 # Maximum capacity in GiB minFreePercentage: 10 # Trigger scaling when free space drops below this percentage scalingFactor: 1.2 # Multiply current capacity by this factor when scaling up # Configure Redis authentication by uncommenting and setting the password field redis: {} # password: your-secure-password backup: enabled: false image: controlplanecorporation/redis-backup:1.0 # compatible with all Redis versions schedule: "0 2 * * *" # daily at 2am UTC resources: cpu: 100m memory: 128Mi provider: aws # Options: aws or gcp aws: bucket: my-backup-bucket region: us-east-1 cloudAccountName: my-backup-cloudaccount policyName: my-backup-policy prefix: redis-cluster/backups gcp: bucket: my-backup-bucket cloudAccountName: my-backup-cloudaccount prefix: redis-cluster/backups ``` ### Authentication Authentication is disabled by default. To enable it, set a password: ```yaml theme={null} redis: password: your-secure-password ``` When set, the password is stored in a dictionary secret and injected into both `requirepass` and `masterauth` in `redis.conf`, ensuring all nodes authenticate with each other. ### Cluster Size * `replicas` — Total number of Redis nodes. **Minimum is 6** (3 primaries + 3 replicas). The cluster is always created with `--cluster-replicas 1`, meaning each primary has exactly one replica. Do not scale the replica count below 6. The cluster requires at least 3 primary nodes and 3 replica nodes to initialize. Replica 0 waits for all nodes to be healthy before running `redis-cli --cluster create`. ### Resources * `cpu` — CPU allocated to each Redis node. * `memory` — Memory allocated to each Redis node. ### Storage A Volume Set is always created to persist cluster data. The file system is `ext4` and the performance class is `general-purpose-ssd`. * `volumeset.capacity` — Initial volume size in GiB per node (minimum 10). * `volumeset.autoscaling.enabled` — Automatically expand volumes as they fill. When enabled: * `maxCapacity` — Maximum volume size in GiB. * `minFreePercentage` — Trigger a scale-up when free space drops below this percentage. * `scalingFactor` — Multiply current capacity by this factor when scaling up. ### Internal Access Redis Cluster nodes must be able to communicate with each other on both the data port and the cluster bus port. Setting `internalAccess.type` to `none` will prevent inter-node communication and break the cluster. * `internalAccess.type` — Controls which workloads can connect to the cluster: | Value | Description | | --------------- | --------------------------------------------------------------- | | `same-gvc` | Allow access from all workloads in the same GVC (recommended) | | `same-org` | Allow access from all workloads in the same organization | | `workload-list` | Allow access only from specific workloads listed in `workloads` | * `internalAccess.workloads` — List of specific workload links, used when `type` is `workload-list`. ### Connecting to Redis Cluster Redis Cluster requires a cluster-aware client. Connect to any node as a seed address — the client will discover the rest of the cluster automatically: ```text theme={null} RELEASE_NAME-redis-cluster.GVC_NAME.cpln.local:6379 ``` Each individual node is also accessible directly: ```text theme={null} RELEASE_NAME-redis-cluster-N.RELEASE_NAME-redis-cluster.GVC_NAME.cpln.local:6379 ``` ### Ports | Port | Protocol | Description | | ------- | -------- | ------------------------------------------------------- | | `6379` | TCP | Redis data port | | `16379` | TCP | Redis cluster bus (internal node-to-node communication) | ## Backup Backup is disabled by default. When enabled, a cron workload runs on the configured schedule and produces one compressed `.rdb.gz` file per primary shard, uploaded to AWS S3 or GCS. The backup image is compatible with all Redis versions. * `backup.enabled` — Enable scheduled backups. * `backup.schedule` — Cron expression for backup frequency (default: daily at 2am UTC). * `backup.provider` — `aws` or `gcp`. * `backup.resources.cpu` / `backup.resources.memory` — Resources for the backup cron container. ### AWS S3 Before enabling backup with `provider: aws`, complete the following in your AWS account: 1. Create an S3 bucket. Set `backup.aws.bucket` to its name and `backup.aws.region` to its region. 2. If you do not have a Cloud Account set up, refer to the docs to [Create a Cloud Account](/guides/create-cloud-account). Set `backup.aws.cloudAccountName` to its name. 3. Create an IAM policy with the following JSON, replacing `YOUR_BUCKET_NAME`: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket", "s3:GetObjectVersion", "s3:DeleteObjectVersion" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET_NAME", "arn:aws:s3:::YOUR_BUCKET_NAME/*" ] } ] } ``` 4. Set `backup.aws.policyName` to the name of the policy created in step 3. 5. Set `backup.aws.prefix` to the folder path where backups will be stored. ### GCS Before enabling backup with `provider: gcp`, complete the following in your GCP account: 1. Create a GCS bucket. Set `backup.gcp.bucket` to its name. 2. If you do not have a Cloud Account set up, refer to the docs to [Create a Cloud Account](/guides/create-cloud-account). Set `backup.gcp.cloudAccountName` to its name. 3. Add the **Storage Admin** role to the GCP service account associated with the Cloud Account. 4. Set `backup.gcp.prefix` to the folder path where backups will be stored. ## Restoring a Backup Each primary shard produces its own backup file (e.g. `redis--node-0.rdb.gz`). Download and decompress the file for the shard you want to restore, then copy it to `/data/dump.rdb` on the corresponding replica and restart that replica. For GCS, replace `aws s3 cp s3://...` with `gsutil cp gs://...`. ```sh theme={null} aws s3 cp s3://BUCKET_NAME/PREFIX/BACKUP_FILE.rdb.gz - \ | gunzip > /tmp/dump.rdb ``` ## External References Official Redis Cluster setup and client configuration guide Source code for the Redis backup container image View the source files, default values, and chart definition # Redis Multi-Location Source: https://docs.controlplane.com/template-catalog/templates/redis-multi-location Deploy Redis Multi-Location on Control Plane using the Template Catalog. Covers configuration, volumes, scaling, and geographically distributed master-replica clustering with Sentinel. ## Overview Redis Multi-Location deploys a single Redis Sentinel cluster spanning multiple Control Plane locations within one GVC. All Redis replicas across all locations belong to the same cluster — replica 0 in the first location starts as the primary, and Sentinel automatically elects a new primary from any location on failure. This template creates a new GVC. At least 2 locations are required. ### What Gets Created * **GVC** — A new GVC spanning the configured locations. * **Stateful Redis Workload** — A single cross-location Redis cluster. Replica 0 in the first location starts as the primary; all others replicate from it. Each replica gets its own persistent volume. * **Stateful Sentinel Workload** — 1 Sentinel replica per location (not configurable). Monitors the cluster and orchestrates automatic failover across locations. * **Volume Set** — Persistent storage for Redis data (configurable capacity and autoscaling). * **Secrets** — Opaque secrets for the Redis and Sentinel configurations, mounted into each container. * **Identity & Policy** — Separate identities and policies for the Redis and Sentinel workloads, each with `reveal` access to their respective secrets. * **Domain** *(optional)* — One domain per workload when public access is enabled, exposing Redis and Sentinel at the configured addresses. * **Backup Cron Workload** *(optional)* — A scheduled backup job that writes Redis snapshots to AWS S3 or GCS. Runs in the first configured location only. ## Installation To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} gvc: name: my-redis-gvc locations: # Replica count applies to Redis workload only; Sentinel always runs 1 per location - name: aws-eu-central-1 replicas: 2 - name: aws-us-west-2 replicas: 2 - name: aws-us-east-1 replicas: 2 redis: image: redis:7.4 resources: cpu: 200m memory: 256Mi serverCommand: redis-server # Can be overridden based on the version of redis/valkey image # extraArgs: "--maxmemory 200mb --maxmemory-policy allkeys-lru" # password: your-redis-password publicAccess: enabled: false address: redis.example-cpln.com volumeset: initialCapacity: 20 # GiB autoscaling: enabled: false maxCapacity: 100 # GiB minFreePercentage: 10 scalingFactor: 1.2 sentinel: image: redis:7.4 resources: cpu: 200m memory: 256Mi # extraArgs: "--sentinel down-after-milliseconds mymaster 5000 --sentinel failover-timeout mymaster 10000" # password: your-sentinel-password publicAccess: enabled: false address: redis-sentinel.example-cpln.com # Applies to both Redis and Sentinel workloads firewall: internalAllowType: same-gvc # options: same-gvc, same-org, workload-list # externalInboundAllowCIDR: "0.0.0.0/0" # Required when publicAccess is enabled. Comma-separated list. # externalOutboundAllowCIDR: "0.0.0.0/0" # Comma-separated list. # workloads: # - //gvc/GVC_NAME/workload/WORKLOAD_NAME backup: enabled: false image: controlplanecorporation/redis-backup:1.0 schedule: "0 2 * * *" # daily at 2am UTC resources: cpu: 100m memory: 128Mi provider: aws # Options: aws or gcp aws: bucket: my-backup-bucket region: us-east-1 cloudAccountName: my-backup-cloudaccount policyName: my-backup-policy prefix: "redis/backups" gcp: bucket: my-backup-bucket cloudAccountName: my-backup-cloudaccount prefix: "redis/backups" ``` ### Locations * `gvc.name` — Name of the GVC to create. **Must be unique within your organization** if deploying multiple instances. * `gvc.locations` — List of Control Plane locations to deploy into. At least 2 locations are required. * `locations[].name` — Location identifier (e.g. `aws-us-east-1`). * `locations[].replicas` — Number of Redis replicas to run in that location. Sentinel always runs exactly 1 replica per location regardless of this setting. ### Authentication Authentication is disabled by default. Redis and Sentinel can be configured with independent passwords. * `redis.password` — Password applied as both `requirepass` and `masterauth` in the Redis configuration. * `sentinel.password` — Optional separate password for Sentinel-to-Sentinel communication (`requirepass` on the Sentinel process). ### Redis * `redis.image` — Redis (or Valkey) image to use. * `redis.resources.cpu` / `redis.resources.memory` — CPU and memory allocated per Redis replica. * `redis.serverCommand` — Override the server command (e.g. `valkey-server` for Valkey images). * `redis.extraArgs` — Additional server arguments appended to the startup command. ### Sentinel Sentinel monitors the Redis primary across all locations and promotes a replica on failure. One Sentinel replica is deployed per location — this is fixed and not configurable. Quorum is calculated automatically based on the number of locations. * `sentinel.image` — Redis (or Valkey) image to use for Sentinel. * `sentinel.resources.cpu` / `sentinel.resources.memory` — CPU and memory allocated per Sentinel replica. * `sentinel.extraArgs` — Additional Sentinel arguments (e.g. custom `down-after-milliseconds` or `failover-timeout`). ### Storage * `redis.volumeset.initialCapacity` — Initial volume size in GiB for Redis data (default 20). * `redis.volumeset.autoscaling.enabled` — Automatically expand the volume as it fills. When enabled: * `maxCapacity` — Maximum volume size in GiB. * `minFreePercentage` — Trigger a scale-up when free space drops below this percentage. * `scalingFactor` — Multiply current capacity by this factor when scaling up. ### Public Access Redis and Sentinel can be exposed over the internet via TCP using Control Plane's domain resource with per-replica port routing. When `publicAccess` is enabled, a Dedicated Load Balancer is automatically enabled on the GVC. This is required for TCP port routing and is a paid Control Plane feature. * `redis.publicAccess.enabled` — Expose Redis publicly (default: `false`). * `redis.publicAccess.address` — A domain you control to expose Redis (e.g. `redis.your-domain.com`). * `sentinel.publicAccess.enabled` — Expose Sentinel publicly (default: `false`). * `sentinel.publicAccess.address` — A domain you control to expose Sentinel (e.g. `redis-sentinel.your-domain.com`). DNS records must be added before deploying. If ownership has not been proven, the first deploy will fail and the error message will list the exact TXT and CNAME records required. Add those records in your DNS provider, then upgrade the release to complete the deployment. **Disable DNS proxying** (e.g. Cloudflare's orange cloud) — TCP traffic must pass through directly. When enabled, a Control Plane domain resource is created for each address. Port mapping is one port per replica: | Workload | Ports | | -------- | ---------------------------------------------------------- | | Redis | `6380`, `6381`, ... (one per replica across all locations) | | Sentinel | `26380`, `26381`, ... (one per location) | `firewall.externalInboundAllowCIDR` must be set when public access is enabled. Without it, inbound external traffic will be blocked. **Connecting externally:** ```bash theme={null} # Redis replica 0 redis-cli -h redis.your-domain.com -p 6380 ping # Redis replica 1 redis-cli -h redis.your-domain.com -p 6381 ping # Sentinel replica 0 (location 0) redis-cli -h redis-sentinel.your-domain.com -p 26380 ping ``` ### Firewall A single firewall configuration applies to both the Redis and Sentinel workloads. * `firewall.internalAllowType` — Controls which workloads can connect internally: | Value | Description | | --------------- | --------------------------------------------------------------- | | `same-gvc` | Allow access from all workloads in the same GVC (recommended) | | `same-org` | Allow access from all workloads in the same organization | | `workload-list` | Allow access only from specific workloads listed in `workloads` | * `firewall.workloads` — List of specific workload links, used when `internalAllowType` is `workload-list`. * `firewall.externalInboundAllowCIDR` — Comma-separated list of CIDRs allowed to reach the workloads externally. **Required when public access is enabled.** Use `0.0.0.0/0` to allow all, or restrict to specific IP ranges. * `firewall.externalOutboundAllowCIDR` — Comma-separated list of CIDRs the workloads are allowed to connect to externally. ### Connecting to Redis All replicas across all locations belong to a single cluster and are accessible within the GVC. Replica 0 in the first configured location starts as the primary. **Option 1 — Load-balanced endpoint (any replica):** ```text theme={null} RELEASE_NAME-redis.GVC_NAME.cpln.local:6379 ``` **Option 2 — Direct to a specific replica:** ```text theme={null} replica-0.RELEASE_NAME-redis.LOCATION.GVC_NAME.cpln.local:6379 replica-1.RELEASE_NAME-redis.LOCATION.GVC_NAME.cpln.local:6379 ``` **Option 3 — Via Sentinel to always write to the current primary:** ```bash theme={null} MASTER_INFO=$(redis-cli -h RELEASE_NAME-sentinel.GVC_NAME.cpln.local -p 26379 SENTINEL get-master-addr-by-name mymaster) MASTER_HOST=$(echo $MASTER_INFO | cut -d' ' -f1) MASTER_PORT=$(echo $MASTER_INFO | cut -d' ' -f2) redis-cli -h $MASTER_HOST -p $MASTER_PORT SET my-key "Hello world" ``` For Sentinel-aware clients, connect through: ```text theme={null} RELEASE_NAME-sentinel.GVC_NAME.cpln.local:26379 ``` The Sentinel master name is `mymaster`. ### Ports | Workload | Port | Protocol | Description | | ----------------- | --------------------- | -------- | ----------------------------------------- | | Redis | `6379` | TCP | Redis data port (internal) | | Sentinel | `26379` | TCP | Sentinel discovery port (internal) | | Redis (public) | `6380`, `6381`, ... | TCP | One port per replica across all locations | | Sentinel (public) | `26380`, `26381`, ... | TCP | One port per location | ## Backup Backup is disabled by default. When enabled, a cron workload runs in the first configured location only and uploads a single compressed `.rdb.gz` snapshot to AWS S3 or GCS. The backup image is compatible with all Redis versions. * `backup.enabled` — Enable scheduled backups. * `backup.schedule` — Cron expression for backup frequency (default: daily at 2am UTC). * `backup.provider` — `aws` or `gcp`. * `backup.resources.cpu` / `backup.resources.memory` — Resources for the backup cron container. ### AWS S3 Before enabling backup with `provider: aws`, complete the following in your AWS account: 1. Create an S3 bucket. Set `backup.aws.bucket` to its name and `backup.aws.region` to its region. 2. If you do not have a Cloud Account set up, refer to the docs to [Create a Cloud Account](/guides/create-cloud-account). Set `backup.aws.cloudAccountName` to its name. 3. Create an IAM policy with the following JSON, replacing `YOUR_BUCKET_NAME`: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket", "s3:GetObjectVersion", "s3:DeleteObjectVersion" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET_NAME", "arn:aws:s3:::YOUR_BUCKET_NAME/*" ] } ] } ``` 4. Set `backup.aws.policyName` to the name of the policy created in step 3. 5. Set `backup.aws.prefix` to the folder path where backups will be stored. ### GCS Before enabling backup with `provider: gcp`, complete the following in your GCP account: 1. Create a GCS bucket. Set `backup.gcp.bucket` to its name. 2. If you do not have a Cloud Account set up, refer to the docs to [Create a Cloud Account](/guides/create-cloud-account). Set `backup.gcp.cloudAccountName` to its name. 3. Add the **Storage Admin** role to the GCP service account associated with the Cloud Account. 4. Set `backup.gcp.prefix` to the folder path where backups will be stored. ## Restoring a Backup The backup produces a single file (`redis-.rdb.gz`). Download and decompress it, then copy it to `/data/dump.rdb` on the replica you want to restore and restart that replica. For GCS, replace `aws s3 cp s3://...` with `gsutil cp gs://...`. ```sh theme={null} aws s3 cp s3://BUCKET_NAME/PREFIX/BACKUP_FILE.rdb.gz - \ | gunzip > /tmp/dump.rdb ``` ## External References Official Redis documentation Redis Sentinel setup and client configuration Source code for the Redis backup container image View the source files, default values, and chart definition # Redpanda Source: https://docs.controlplane.com/template-catalog/templates/redpanda Deploy a Kafka-compatible Redpanda streaming cluster on Control Plane. Covers SASL authentication, Schema Registry, Redpanda Console, external TLS access with per-replica SNI routing, and broker tuning. ## Overview Redpanda is a Kafka-compatible streaming platform written in C++. It implements the Kafka wire protocol natively, so any Kafka client, SDK, or tool works without modification. This template deploys a stateful Redpanda broker cluster with SASL authentication, Schema Registry, an optional HTTP REST proxy, and an optional web console. ### What Gets Created * **Stateful Redpanda Workload** — A multi-replica broker cluster using the Seastar async runtime. Each broker gets its own persistent volume. * **Standard Redpanda Console Workload** *(optional, enabled by default)* — Web UI for browsing topics, inspecting messages, managing consumer groups, and viewing Schema Registry schemas. * **Volume Set** — One persistent volume per broker replica for data storage. * **Identity & Policy** — An identity bound to the workloads with `reveal` access to credential secrets. * **Secrets** — A dictionary secret holding SASL user credentials injected at startup. This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites This template has no external prerequisites. To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} redpanda: name: cluster image: redpandadata/redpanda:v26.1.9 replicas: 3 multiZone: false env: [] cpu: 1500m memory: 4Gi minCpu: 500m minMemory: 2Gi smp: 1 # Seastar reactor threads — must match the floor of your cpu limit reserveMemory: 1G # memory reserved for the OS; Redpanda uses (memory - reserveMemory) volume: initialCapacity: 10 # In GiB performanceClass: general-purpose-ssd # or high-throughput-ssd (min 200 GiB) fileSystemType: xfs # xfs / ext4 # customEncryption: # enabled: true # region: aws-us-east-2 # keyId: arn:aws:kms:us-east-2:1234567890:key/your-key-id firewall: internal_inboundAllowType: "same-gvc" # Options: same-org / same-gvc # external_inboundAllowCIDR: 0.0.0.0/0 # inboundAllowWorkload: # - //gvc/my-gvc/workload/my-app listeners: kafka: internal: port: 9092 # external: # directReplicaRouting: # containerPort: 9094 # publicAddress: redpanda.example.com adminApi: port: 9644 schemaRegistry: port: 8081 pandaproxy: enabled: false port: 8082 auth: saslMechanism: SCRAM-SHA-256 # SCRAM-SHA-256 / SCRAM-SHA-512 users: - username: admin password: "your-admin-password" # - username: your-app-user # password: "your-app-password" superusers: [] acl: allowEveryoneIfNoAclFound: false secrets: cluster_id: "" # leave empty to auto-generate; set explicitly to preserve identity across reinstalls extra_configurations: {} # auto_create_topics_enabled: false # log_retention_ms: 604800000 # log_segment_size: 134217728 redpanda_console: enabled: true name: console image: redpandadata/console:v3.7.4 cpu: 200m memory: 256Mi minCpu: 50m minMemory: 64Mi replicas: 1 # domain: console.your-domain.com firewall: external_inboundAllowCIDR: "0.0.0.0/0" ``` ### Cluster Size and Resources * `redpanda.replicas` — Number of broker replicas. A minimum of 3 is recommended for production to ensure Raft quorum. * `redpanda.cpu` / `redpanda.memory` — Maximum CPU and memory per broker. * `redpanda.minCpu` / `redpanda.minMemory` — Minimum guaranteed CPU and memory per broker. * `redpanda.smp` — Number of Seastar reactor threads. Must match the floor of `cpu` (e.g., `cpu: 1500m` → `smp: 1`, `cpu: 3` → `smp: 3`). Without this, Seastar uses all node CPUs and incorrectly divides memory across them. * `redpanda.reserveMemory` — Memory set aside for the OS. Redpanda uses `(memory - reserveMemory)` for its own heap. Default `1G` works for most configurations. * `redpanda.multiZone` — Spread brokers across availability zones within the location. ### Storage Each broker replica gets its own persistent volume. For production workloads with high throughput, use `high-throughput-ssd` (minimum 200 GiB). * `redpanda.volume.initialCapacity` — Initial volume size in GiB. * `redpanda.volume.performanceClass` — `general-purpose-ssd` or `high-throughput-ssd`. * `redpanda.volume.fileSystemType` — `xfs` (default, recommended for Redpanda) or `ext4`. **Volume encryption** via AWS KMS is supported: ```yaml theme={null} redpanda: volume: customEncryption: enabled: true region: aws-us-east-2 keyId: arn:aws:kms:us-east-2:1234567890:key/your-key-id ``` After deploying with custom encryption enabled, navigate to each created volume in the Control Plane console, click `spec`, and follow the **AWS Custom Encryption Instructions** to complete the setup. ### Authentication SASL is always enabled. All users are defined under `redpanda.auth.users`. The first user in the list is automatically granted superuser privileges. Additional superusers can be added under `redpanda.auth.superusers`. * `redpanda.auth.saslMechanism` — `SCRAM-SHA-256` (default) or `SCRAM-SHA-512`. * `redpanda.auth.users` — List of `username` / `password` pairs created at startup. * `redpanda.auth.superusers` — Additional usernames to grant superuser privileges. ### ACLs * `redpanda.acl.allowEveryoneIfNoAclFound` — When `false` (default), clients without an explicit ACL are denied. Set to `true` to allow unauthenticated access when no ACL exists for a resource. ### Listeners | Listener | Port | Description | | --------------- | ------ | ----------------------------------------- | | Kafka | `9092` | Kafka-compatible wire protocol (internal) | | Admin API | `9644` | Redpanda Admin API for cluster management | | Schema Registry | `8081` | Confluent-compatible Schema Registry | | PandaProxy | `8082` | HTTP REST proxy *(disabled by default)* | Enable PandaProxy to produce and consume messages over HTTP without a Kafka client: ```yaml theme={null} redpanda: listeners: pandaproxy: enabled: true port: 8082 ``` ### Extra Broker Configuration Pass any Redpanda broker property directly via `extra_configurations`. These are injected into `redpanda.yaml` at startup: ```yaml theme={null} redpanda: extra_configurations: auto_create_topics_enabled: false log_retention_ms: 604800000 # 7 days log_segment_size: 134217728 # 128 MiB log_retention_bytes: -1 # unlimited ``` ### Firewall * `redpanda.firewall.internal_inboundAllowType` — Controls which workloads can reach the brokers: * `same-gvc` — All workloads in the same GVC (default). * `same-org` — All workloads in the org. * `redpanda.firewall.inboundAllowWorkload` — Allow specific workloads by path. ## Connecting Redpanda is accessible internally from any workload in the same GVC: | Listener | Hostname | Port | | ------------------------- | -------------------------------- | ------ | | Kafka | `{clusterName}.{gvc}.cpln.local` | `9092` | | Admin API | `{clusterName}.{gvc}.cpln.local` | `9644` | | Schema Registry | `{clusterName}.{gvc}.cpln.local` | `8081` | | PandaProxy *(if enabled)* | `{clusterName}.{gvc}.cpln.local` | `8082` | To connect to a specific broker replica directly: ``` {clusterName}-0.{clusterName}.{gvc}.cpln.local:9092 {clusterName}-1.{clusterName}.{gvc}.cpln.local:9092 ``` Connect using `rpk`: ```bash theme={null} rpk topic list \ -X brokers={clusterName}.{gvc}.cpln.local:9092 \ -X sasl.mechanism=SCRAM-SHA-256 \ -X user=admin \ -X pass=your-admin-password ``` For Kafka clients, use the following connection properties: ```properties theme={null} bootstrap.servers={clusterName}.{gvc}.cpln.local:9092 security.protocol=SASL_PLAINTEXT sasl.mechanism=SCRAM-SHA-256 sasl.jaas.config=org.apache.kafka.common.security.scram.ScramLoginModule required \ username="admin" \ password="your-admin-password"; ``` ## Redpanda Console The Redpanda Console is enabled by default and accessible via the Control Plane external endpoint for the `{release-name}-console` workload. It provides a web UI for browsing topics, inspecting messages, managing consumer groups, and viewing Schema Registry schemas. To expose the console on a custom domain, set `redpanda_console.domain`: ```yaml theme={null} redpanda_console: domain: console.your-domain.com ``` This creates a Control Plane domain resource that routes HTTPS traffic to the console workload. The same DNS prerequisites apply as for any Control Plane domain (ownership TXT record and CNAME to the GVC alias). To restrict console access to specific IPs, update `redpanda_console.firewall.external_inboundAllowCIDR`: ```yaml theme={null} redpanda_console: firewall: external_inboundAllowCIDR: "203.0.113.0/24" ``` ## External Access Redpanda brokers can be exposed over the internet via TLS using a public domain. Each broker advertises its own per-replica subdomain and Control Plane routes clients to the correct broker using SNI. ### Prerequisites 1. **A domain you control** with DNS managed by your registrar (e.g. Cloudflare). 2. **Dedicated Load Balancer** enabled on your GVC — required for external TCP routing. Enable under GVC settings in the Control Plane console. See [Configure Domain documentation](https://docs.controlplane.com/guides/configure-domain#dedicated-load-balancing). 3. **DNS records added before deploying.** Disable proxying (e.g. Cloudflare's orange cloud) — TCP traffic must pass through directly: | Type | Name | Value | | ----- | ---------------------------- | ------------------------------------- | | TXT | `_cpln.your-domain.com` | your Control Plane org name or org ID | | CNAME | `@` | `{gvcAlias}.cpln.app` | | CNAME | `_acme-challenge` | `_acme-challenge.cpln.app` | | CNAME | `{clusterName}-0-{location}` | `{gvcAlias}.cpln.app` | | CNAME | `{clusterName}-1-{location}` | `{gvcAlias}.cpln.app` | | CNAME | `{clusterName}-N-{location}` | `{gvcAlias}.cpln.app` | Add one CNAME per broker replica. The `_acme-challenge` record is required for Control Plane to issue the TLS certificate via DNS-01. Your GVC alias is visible under GVC settings in the Control Plane console. ### Configuration ```yaml theme={null} redpanda: listeners: kafka: external: directReplicaRouting: containerPort: 9094 publicAddress: your-domain.com ``` ### Connecting Externally Each broker advertises its own subdomain in the format `{clusterName}-{ordinal}-{location}.{domain}`. Use all broker addresses as the bootstrap list: ```bash theme={null} rpk topic list \ -X brokers=cluster-0-aws-us-east-1.your-domain.com:9094,cluster-1-aws-us-east-1.your-domain.com:9094,cluster-2-aws-us-east-1.your-domain.com:9094 \ -X tls.enabled=true \ -X sasl.mechanism=SCRAM-SHA-256 \ -X user=admin \ -X pass=your-admin-password ``` For Kafka clients: ```properties theme={null} security.protocol=SASL_SSL sasl.mechanism=SCRAM-SHA-256 bootstrap.servers=cluster-0-aws-us-east-1.your-domain.com:9094,cluster-1-aws-us-east-1.your-domain.com:9094,cluster-2-aws-us-east-1.your-domain.com:9094 sasl.jaas.config=org.apache.kafka.common.security.scram.ScramLoginModule required \ username="admin" \ password="your-admin-password"; ``` ## External References Official Redpanda documentation Redpanda Console UI guide rpk command reference for managing Redpanda clusters Confluent-compatible Schema Registry and HTTP Proxy API reference # SeaweedFS Source: https://docs.controlplane.com/template-catalog/templates/seaweedfs Deploy SeaweedFS on Control Plane using the Template Catalog. A single all-in-one node providing S3-compatible object storage with SigV4 authentication, persistent volume storage, startup bucket creation, and an admin UI — the in-org storage target for any template that speaks S3. ## Overview SeaweedFS is a distributed object store with an S3-compatible API. This template deploys a single all-in-one node — master, volume server, filer, S3 gateway, and admin UI in one process — with persistent storage, startup bucket creation, and SigV4-authenticated S3 on port `8333`. Its main use is as an in-org storage target: any workload or template that accepts an S3-compatible endpoint and a static access key pair can point at it without leaving your organization. Backing up [postgres-highly-available](/template-catalog/templates/postgres-highly-available) into SeaweedFS, and restoring from it, was verified end to end with no changes on the consumer side. See [Using SeaweedFS as an S3 backend](#using-seaweedfs-as-an-s3-backend). ### Architecture * **All-in-one node** — A single `stateful` workload running `weed mini`, which starts the master, volume server, filer, S3 gateway, and admin UI in one process. The S3 API is served on port `8333`; the admin UI on port `23646`. * **One disk for everything** — Object data, filer metadata (leveldb), and master metadata all live on one persistent volume set mounted at `/data`. * **Authenticated by default** — S3 credentials come from a dictionary secret you create before installing. SeaweedFS serves S3 with *no authentication at all* when credentials are absent, so the chart refuses to render without one. * **Private by default** — External access is off (`publicAccess.enabled: false`); the S3 API is reachable only from inside the GVC until you turn it on. The admin UI is never publicly routed. ### What Gets Created * **Stateful SeaweedFS Workload** — One replica serving the S3 API on port `8333`, plus the admin UI on port `23646` when enabled. * **Volume Set** — Persistent storage at `/data` for objects and all metadata, with optional autoscaling. A final snapshot is retained for 7 days when the volume set is deleted. * **Admin Secret** *(optional)* — A dictionary secret holding the admin UI username and password. Created only when `adminUI.enabled` is `true`. * **Identity & Policy** — An identity bound to the workload with `reveal` on exactly the secrets it mounts: your S3 credentials secret, plus the admin secret when the admin UI is enabled. This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites **Create the S3 credentials secret before installing.** The workload references it by name (`s3.credentialsSecretName`, default `my-seaweedfs-s3-credentials`), and the deployment waits on a missing secret indefinitely rather than starting up unauthenticated. Create it as a **dictionary** secret containing exactly these two keys: ```bash theme={null} cpln secret create-dictionary --name my-seaweedfs-s3-credentials \ --entry AWS_ACCESS_KEY_ID= \ --entry AWS_SECRET_ACCESS_KEY= ``` These are the credentials every S3 client uses. The secret is yours, not the release's — `helm uninstall` leaves it in place. Also change `adminUI.password` before installing. The shipped default is an obvious placeholder, not a working credential. Install the template using your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} # Official upstream image. Runs `weed mini` — the image's own default command — # which starts the master, volume server, filer, S3 gateway and admin UI in one # process. Pin a concrete released tag; never latest. image: chrislusf/seaweedfs:4.40 resources: minCpu: 250m maxCpu: 1000m minMemory: 512Mi maxMemory: 2Gi # raise for stores holding tens of millions of objects (in-memory volume index) # Storage: objects + filer metadata + master metadata, all at /data volumeset: capacity: 20 # initial capacity in GiB (minimum is 10) autoscaling: enabled: false # set to true to grow the volume automatically as it fills maxCapacity: 200 # maximum capacity in GiB when autoscaling is enabled minFreePercentage: 10 # minimum free percentage that triggers scaling scalingFactor: 1.2 # how much to grow the volume when scaling is triggered # S3 API (port 8333) s3: # PREREQUISITE dictionary secret — MUST EXIST BEFORE INSTALL, or the deployment # waits on it forever and looks broken. These are the S3 credentials every client # uses; SeaweedFS serves S3 with NO authentication if they are absent. credentialsSecretName: my-seaweedfs-s3-credentials # Buckets created at startup if they do not already exist, e.g. [backups, uploads]. # Most backup tools require the bucket to exist before they run. buckets: [] # Admin UI (port 23646, reachable inside the GVC only) adminUI: enabled: true # cluster status, bucket browser, user and maintenance management username: admin password: change-me-seaweedfs-admin # CHANGE THIS before install publicAccess: enabled: false # true = S3 API on the auto *.cpln.app HTTPS endpoint (path-style addressing) internalAccess: # internal firewall scope type: same-gvc # options: none, same-gvc, same-org, workload-list workloads: [] # only used when type is workload-list ``` ### Image and Resources * `image` — The official upstream image. `weed mini` is the image's own default command; the template pins every port explicitly because mini's port layout can change between releases. * `resources.minCpu` / `resources.maxCpu` / `resources.minMemory` / `resources.maxMemory` — CPU and memory for the container. The defaults run a typical store comfortably; raise `maxMemory` for stores holding tens of millions of objects, since the volume index is held in memory. ### Storage * `volumeset.capacity` — Initial volume size in GiB (minimum 10). This one volume holds object data, filer metadata, and master metadata. * `volumeset.autoscaling.enabled` — Automatically expand the volume as it fills. When enabled: * `maxCapacity` — Maximum volume size in GiB. Must be greater than or equal to `volumeset.capacity`. * `minFreePercentage` — Trigger a scale-up when free space drops below this percentage. * `scalingFactor` — Multiply the current capacity by this factor when scaling up. Data survives restarts, redeployments, and `helm upgrade` under the same release name. Uninstalling deletes the volume set — and every stored object with it — keeping a final snapshot for 7 days. SeaweedFS derives its volume file size from the disk capacity at startup, so growing the volume set takes effect on the next restart. This is harmless: SeaweedFS simply creates more volume files. ### S3 API * `s3.credentialsSecretName` — Name of the prerequisite dictionary secret holding `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`. Required; the chart fails to render if it is empty. * `s3.buckets` — Buckets created at startup if they do not already exist, for example `[backups, uploads]`. Names must be lowercase letters, digits, dots, and hyphens, 3–63 characters, starting with a letter or digit. Buckets are only ever created, never deleted — removing a name from this list leaves the bucket in place. You can also create buckets at runtime through the admin UI or with `aws s3 mb`. Rotating the credentials secret and redeploying genuinely rotates the keys: the S3 identity is rebuilt from the environment on every boot rather than stored on disk. The old access key is rejected with `InvalidAccessKeyId` once the new replica is serving; stored objects are untouched. ### Admin UI * `adminUI.enabled` — Cluster status, bucket browser, user, and maintenance management on port `23646`. Reachable from inside the GVC only. * `adminUI.username` / `adminUI.password` — Login credentials, stored in a dictionary secret the template creates. Required when the admin UI is enabled. Setting `adminUI.enabled: false` removes the port, the credentials, and the secret, and narrows the policy to the S3 credentials secret alone. The admin routes then return `404`. Upstream `weed mini` still starts the admin component in-process when the UI is disabled — only its routes are unregistered. Because the port is not declared on the workload and no credentials are injected, nothing is reachable from outside the container. ### Access * `publicAccess.enabled` — `false` by default. When set to `true`, the S3 API is served over HTTPS on the automatically assigned `*.cpln.app` canonical endpoint, using path-style addressing. Only port `8333` is exposed this way; the admin UI is never publicly routed. * `internalAccess.type` — Internal firewall scope of the workload: | Type | Description | | --------------- | -------------------------------------------------------------------------------------------------------------------- | | `none` | No internal access. | | `same-gvc` | Allow access from all workloads in the same GVC (default). | | `same-org` | Allow access from all workloads in the same organization, including other GVCs. | | `workload-list` | Allow access only from workloads listed in `internalAccess.workloads`, e.g. `//gvc/GVC_NAME/workload/WORKLOAD_NAME`. | Firewall changes take 60–90 seconds to propagate, and a client that cached a negative DNS answer can lag an allow by roughly another 30 seconds. ## Using SeaweedFS as an S3 Backend Any client that speaks S3 works, subject to three rules: * **Path-style addressing is required** (`http://host:8333/bucket/key`). Virtual-host style (`bucket.host`) is not served. * **Any region value works.** The region is read from the client's signature scope and never compared against a server-side value, so a consumer hardcoded to `us-east-1` is fine as-is. * **The bucket must already exist** for most backup tools. Create it with `s3.buckets`, through the admin UI, or with `aws s3 mb`. ### Verifying from inside the GVC ```bash theme={null} aws configure set default.s3.addressing_style path aws --endpoint-url http://RELEASE_NAME-seaweedfs:8333 s3 ls aws --endpoint-url http://RELEASE_NAME-seaweedfs:8333 s3 cp ./file s3://BUCKET/file ``` ### Catalog templates Endpoint and credentials for the common catalog consumers. `accessKey` / `secretKey` (or `accessSecret`) are the two values you put in the prerequisite secret. | Consumer template | Values to set | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [postgres-highly-available](/template-catalog/templates/postgres-highly-available), [timescaledb-highly-available](/template-catalog/templates/timescaledb-highly-available) | `backup.provider: minio`, `backup.minio.endpoint: http://RELEASE_NAME-seaweedfs:8333`, plus `backup.minio.bucket`, `backup.minio.accessKey`, `backup.minio.secretKey`, `backup.minio.prefix` | | [thanos](/template-catalog/templates/thanos), [mimir](/template-catalog/templates/mimir) | `storage.type: minio`, `storage.minio.endpoint: RELEASE_NAME-seaweedfs.GVC_NAME.cpln.local:8333` (host:port, no scheme), `storage.minio.insecure: true`, `storage.minio.region: us-east-1`, plus `bucket`, `accessKey`, `accessSecret` | | [prometheus](/template-catalog/templates/prometheus) | Same fields, nested one level deeper: `thanos.objectStorage.enabled: true`, `thanos.objectStorage.type: minio`, then `thanos.objectStorage.minio.*` | | [sftpgo](/template-catalog/templates/sftpgo) | `storage.type: minio`, `storage.minio.endpoint: http://RELEASE_NAME-seaweedfs:8333`, plus `bucket`, `region`, `accessKey`, `accessSecret` | | [docmost](/template-catalog/templates/docmost) | `storage.type: s3`, `storage.s3.endpoint: http://RELEASE_NAME-seaweedfs:8333`, `storage.s3.forcePathStyle: true`, `storage.s3.bucket`, `storage.s3.region: us-east-1`, and `storage.s3.auth.secretName` pointing at a **separate** dictionary secret — see the warning below | | [n8n](/template-catalog/templates/n8n), [metabase](/template-catalog/templates/metabase), [keycloak](/template-catalog/templates/keycloak), [unleash](/template-catalog/templates/unleash) | Backups of their bundled database: `postgresHA.backup.provider: minio` (or `postgres.backup.provider: minio` in single-instance mode) with the matching `…backup.minio.endpoint`, `bucket`, `accessKey`, `secretKey`, `prefix` | **Docmost needs a second secret with different key names.** Its static-key secret must contain `AWS_S3_ACCESS_KEY_ID` and `AWS_S3_SECRET_ACCESS_KEY` — the same two values as this template's secret, under different keys. Point `storage.s3.auth.secretName` at that secret, not at `s3.credentialsSecretName`. Templates whose object-storage support is limited to named providers — [ghost](/template-catalog/templates/ghost) and [clickhouse](/template-catalog/templates/clickhouse), which offer only `aws`/`gcp` and similar — cannot point at an arbitrary S3 endpoint, so they cannot use this template as their storage target. ## Connecting | Target | Address | Notes | | ----------------------------- | --------------------------------------------------------- | --------------------------------------------------------------------------------------------- | | S3 API, same GVC | `http://RELEASE_NAME-seaweedfs:8333` | Short name; resolves from any workload in the same GVC. | | S3 API, same GVC (FQDN) | `http://RELEASE_NAME-seaweedfs.GVC_NAME.cpln.local:8333` | Same host, fully qualified. | | S3 API, host:port form | `RELEASE_NAME-seaweedfs.GVC_NAME.cpln.local:8333` | For clients that take a bare host:port, such as Thanos and Mimir. Pair with `insecure: true`. | | S3 API, public *(if enabled)* | `https://.cpln.app` | Port 443, no port suffix. Requires `publicAccess.enabled: true`. | | Admin UI | `http://RELEASE_NAME-seaweedfs.GVC_NAME.cpln.local:23646` | Inside the GVC only. Log in with `adminUI.username` / `adminUI.password`. | | Health check | `GET /healthz` on port `8333` | Unauthenticated by design. | Same-GVC clients use plain `http://` over the mesh's mTLS; external clients use `https://`, with TLS terminated at the platform edge. The canonical hostname appears under `status.canonicalEndpoint` in `cpln workload get RELEASE_NAME-seaweedfs --gvc GVC_NAME -o yaml`. Requests without a valid signature are rejected: unsigned requests get `403`, and a wrong secret key gets `SignatureDoesNotMatch`. ## Important Notes * **Create the S3 credentials secret before installing.** A missing secret leaves the deployment waiting indefinitely, and omitting credentials entirely would make SeaweedFS serve S3 unauthenticated. * **Change `adminUI.password` before installing** — the shipped default is a placeholder, not a working credential. * **The template deploys a single replica by design.** `weed mini` runs one master, one filer, and one volume server in a single process, so raising the replica count would create separate, divergent object stores. Multi-node clustering is a planned follow-up. * **A redeploy or upgrade is a full S3 outage.** Measured at **337 failed requests over an 80.8 second gap** (at roughly 5 requests/second), with the store serving again about 128 seconds after the redeploy was triggered. Almost all of that is platform teardown and reschedule — SeaweedFS itself boots in about 1.5 seconds. Schedule upgrades accordingly, and expect the same window whenever the platform reschedules the replica. * **Clients must use path-style addressing**; virtual-host style is not served. * **Only the S3 API is publicly routable.** `publicAccess` exposes port `8333` alone — reach the admin UI from inside the GVC. * **Rotating the credentials secret and redeploying rotates the keys**, leaving stored data untouched. Update every consumer at the same time. * **Data survives restarts and upgrades; uninstall deletes the volume set** and every stored object, keeping a final snapshot for 7 days. * **Uninstall does not delete your S3 credentials secret** — it is your resource, created outside the release, and it stays until you remove it. ## External References Source, releases, and issue tracker The all-in-one mode this template runs Which S3 operations SeaweedFS implements How SeaweedFS resolves S3 identities and access keys Cluster status, bucket browser, and maintenance View the source files, default values, and chart definition # Secret Env Var Syncer Source: https://docs.controlplane.com/template-catalog/templates/secret-env-var-syncer ## Overview The Secret Env Var Syncer (SEVS) runs as a cron workload that syncs keys from Control Plane dictionary secrets into environment variables on GVCs or individual workload containers. It runs on a configurable schedule, applies the changes, then exits. ### How It Works SEVS runs as a cron workload on Control Plane. Your sync configuration is stored in a Control Plane secret and mounted into the workload as `config.yaml`. On each execution, SEVS reads the list of entries, fetches the specified dictionary secrets, and applies their keys as environment variables to the target GVCs or workload containers. The job then exits until the next scheduled run. Env var values are written as `cpln://secret/SECRET_NAME.KEY_NAME` references — not raw values. This means the target resource resolves the secret reference at runtime rather than storing the value inline. SEVS skips the `PUT` request when the target already matches the desired env vars, making each run idempotent. ### What Gets Created * **Cron SEVS Workload** — A cron workload that reads the mounted config, syncs dictionary secret keys as environment variable references to each configured target, then exits. * **Identity** — An identity bound to the workload, used to authenticate against the Control Plane API. * **Policies** — Three policies granting the identity `reveal` on all secrets, `edit` on all GVCs, and `edit` on all workloads. * **Secret** — An opaque secret containing the sync configuration (`config.yaml`), mounted into the workload at `/app/config.yaml`. This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites * One or more Control Plane [dictionary secrets](/reference/secret) containing the key-value pairs you want to sync as environment variables. ### Installation To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} image: ghcr.io/controlplane-com/cpln-build/secret-env-var-syncer:v1.3.1 resources: cpu: 100m memory: 256Mi schedule: "*/5 * * * *" # Default: every 5 minutes. Uses standard cron syntax (minute hour day month weekday). timeoutSeconds: 300 # Maximum time allowed for a single sync run. Increase if you have a large number of entries. sevsConfig: entries: - target: type: gvc name: my-gvc secret: my-dictionary-secret # - target: # type: workload # name: my-workload # gvc: my-gvc # container: app # secret: my-dictionary-secret ``` ### Top-Level Fields * `image` — The SEVS container image. Do not change unless upgrading. * `resources.cpu` / `resources.memory` — Resource limits for the workload container. * `schedule` — Cron expression controlling how often the sync runs (default: `*/5 * * * *`). * `timeoutSeconds` — Maximum time allowed for a single sync run (default: `300`). Increase if you have a large number of entries. * `sevsConfig` — The full sync configuration — a list of entries (see below). ### Entries Each entry in `sevsConfig.entries` syncs the keys of one Control Plane dictionary secret into the environment variables of one target. | Field | Description | | -------- | ------------------------------------------------------------ | | `target` | The resource to apply env vars to (see target types below) | | `secret` | The name of the Control Plane dictionary secret to read from | #### Target: GVC Applies all keys from the dictionary secret as environment variables on the entire GVC: ```yaml theme={null} - target: type: gvc name: my-gvc secret: my-dictionary-secret ``` #### Target: Workload Applies all keys from the dictionary secret as environment variables on a specific container within a workload: ```yaml theme={null} - target: type: workload name: my-workload gvc: my-gvc container: app secret: my-dictionary-secret ``` The `container` field is required for workload targets. The `gvc` field is required when the workload is in a different GVC than the one SEVS is deployed in. ### Synced Env Var Format Each dictionary key becomes an environment variable name on the target. The value is written as a Control Plane secret reference — not the raw secret value: ``` cpln://secret/SECRET_NAME.KEY_NAME ``` The target GVC or workload resolves this reference at runtime. ## Permissions SEVS requires the following permissions on its identity, all of which are created automatically by the template: | Resource Kind | Permission | Reason | | ------------- | ---------- | ------------------------------------------------------------------------------- | | `secret` | `reveal` | Read the dictionary secrets listed in each entry, and the mounted `config.yaml` | | `gvc` | `edit` | Write environment variables to GVC targets | | `workload` | `edit` | Write environment variables to workload container targets | ## Important Notes * **Dictionary secrets only** — Source secrets must be of type `dictionary`. Opaque secrets are not supported as sync sources. * **One-shot execution** — SEVS runs once per schedule tick and exits. It is not a long-running daemon. * **Concurrency** — The job uses `concurrencyPolicy: Forbid`. If a previous run is still active when the next schedule fires, the new run is skipped. * **Idempotent** — SEVS skips the update when the target already matches the desired env vars. * **Env var overwrite** — Existing environment variables on the target with the same key will be overwritten on each run. ## External References Source code for the Secret Env Var Syncer image View the source files, default values, and chart definition # SFTPGo Source: https://docs.controlplane.com/template-catalog/templates/sftpgo Deploy SFTPGo on Control Plane using the Template Catalog. An SFTP server backed by S3, GCS, or S3-compatible object storage, with a scale-to-zero mode that suspends the server when idle. ## Overview SFTPGo is an SFTP server backed by object storage. Clients speak standard SFTP; files land in your bucket, with per-user folder isolation and declarative user management. This template offers a choice between an always-on server and a **scale-to-zero mode** that suspends the server when idle behind a tiny always-on proxy. ### Architecture * **SFTPGo** — A single-replica stateful workload serving SFTP on port `2022`. An embedded bolt database and the SSH host keys persist on a volume set so host keys stay stable across restarts and wakes. * **Scale-to-zero proxy** *(scale\_to\_zero mode only)* — An always-on activator workload that accepts client connections while SFTPGo sleeps, wakes it via the platform API, splices traffic through, and suspends it again after an idle window. ### What Gets Created * **Stateful SFTPGo Workload** — Serves SFTP on port `2022`; holds the embedded database and SSH host keys. * **Standard Scale-to-Zero Proxy Workload** *(scale\_to\_zero mode only)* — Always-on TCP activator that fronts SFTPGo and manages suspend/wake. * **Volume Set** — 10 GiB persistent storage for the embedded database and SSH host keys. * **Secrets** — A dictionary secret with the admin bootstrap credentials and an opaque secret with the declared-users file. * **Identity & Policy** — An identity bound to the workloads with `reveal` access to the secrets, keyless cloud storage access for AWS and GCP backends, and a least-privilege policy letting the proxy's identity suspend and wake exactly the SFTPGo workload — nothing else. This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites SFTPGo requires an existing bucket in one of the supported backends and the access setup for it. Complete the steps for your chosen backend before installing. AWS S3 uses a Control Plane cloud identity — no credentials are stored; the workload's identity vends temporary credentials at runtime. Create an S3 bucket. Set `storage.aws.bucket` and `storage.aws.region` to match. If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) for your AWS account. Set `storage.aws.cloudAccountName` to its name. Create an AWS IAM policy with the JSON below (replace `YOUR_BUCKET`), then set `storage.aws.policyName` to the policy's name (bare name, not ARN): ```json theme={null} { "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Action": [ "s3:ListBucket", "s3:GetBucketLocation", "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:AbortMultipartUpload" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET", "arn:aws:s3:::YOUR_BUCKET/*" ] }] } ``` Google Cloud Storage uses a Control Plane cloud identity — no credentials are stored. Create a GCS bucket. Set `storage.gcp.bucket` to its name. If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) for your GCP project. Set `storage.gcp.cloudAccountName` to its name. No policy to author — the template grants the workload identity `roles/storage.objectAdmin` scoped to the bucket automatically. Ensure your cloud account setup permits its service account to receive that binding. Any S3-compatible server uses static access credentials, since these cannot federate with a cloud account. Create your bucket on the server. Set `storage.minio.bucket` to its name. Set `storage.minio.endpoint` to the S3 API address including port. For the `minio` marketplace template deployed in the same GVC, this is `http://WORKLOAD_NAME:9000`. Set `storage.minio.accessKey` and `storage.minio.accessSecret` to credentials with access to the bucket. For the MinIO template, these are its `admin.username` and `admin.password`. Once your backend is ready, install the template using your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Choosing a Mode The `mode` value selects how SFTPGo runs. `scale_to_zero` (the default) suspends the server when idle to save cost; `always_warm` keeps it running for instant connections. | | `scale_to_zero` (default) | `always_warm` | | ------------------------ | ------------------------------------------------------- | ------------------------------------------ | | Cost when idle | Proxy (\~100m/128Mi) + the dedicated load balancer | Full SFTPGo replica + load balancer | | First connect after idle | \~30s cold start (occasionally up to \~75s) | Instant | | Client requirements | Timeout ≥120s or retry (see below) | None — works unchanged | | Best for | Cost-sensitive, periodic transfers, cooperative clients | Strict SLAs, arbitrary third-party clients | ## Configuration The default `values.yaml` for this template: ```yaml theme={null} image: drakkan/sftpgo:v2.7.4-distroless-slim mode: scale_to_zero # options: scale_to_zero, always_warm scaleToZero: # only used when mode is scale_to_zero idleHold: 5m # suspend after this window with no active connections (e.g. 90s, 5m, 1h) proxy: image: ghcr.io/controlplane-com/scale-to-zero-proxy:0.1.0 resources: # all SFTP traffic flows through the proxy cpu: 500m memory: 256Mi minCpu: 100m minMemory: 128Mi resources: # SFTPGo container; CPU governs transfer throughput and wake speed cpu: 1000m memory: 512Mi minCpu: 250m minMemory: 256Mi admin: # SFTPGo administrator (REST API / optional web admin), created on first boot username: admin password: change-me-sftpgo-admin # change before installing storage: type: aws # options: aws, gcp, minio aws: # AWS S3 — keyless via Control Plane cloud identity bucket: my-sftp-bucket # bucket must already exist region: us-east-1 keyPrefix: "" # optional bucket-wide folder prefix, e.g. sftp/ cloudAccountName: my-s3-cloud-account # Control Plane AWS cloud account policyName: my-sftp-s3-policy # custom IAM policy granting bucket access (bare name) gcp: # Google Cloud Storage — keyless via Control Plane cloud identity bucket: my-sftp-bucket # bucket must already exist keyPrefix: "" # optional bucket-wide folder prefix, e.g. sftp/ cloudAccountName: my-gcs-cloud-account # Control Plane GCP cloud account minio: # Any S3-compatible server (MinIO, R2, Wasabi, …) — static keys only endpoint: http://my-minio-workload:9000 # e.g. an internal MinIO template deployment bucket: my-sftp-bucket # bucket must already exist region: us-east-1 keyPrefix: "" # optional bucket-wide folder prefix, e.g. sftp/ accessKey: my-minio-username accessSecret: my-minio-password users: # Declarative user list, re-applied on every start - username: sftpuser password: change-me-sftp-user # omit when publicKeys is set publicKeys: [] # SSH public keys, e.g. ["ssh-ed25519 AAAA... user@laptop"] # keyPrefix: custom/folder/ # optional per-user bucket folder override volumeset: capacity: 10 # GiB (minimum 10) — stores the embedded database and SSH host keys publicAccess: enabled: true # public SFTP endpoint (dedicated NLB) on the client-facing workload internalAccess: # internal firewall scope of the SFTPGo workload type: same-gvc # options: none, same-gvc, same-org, workload-list (scale_to_zero requires non-none) workloads: [] # used with workload-list; the scale-to-zero proxy is added automatically webAdmin: enabled: false # declare SFTPGo's web admin/REST API port 8080 ``` ### Mode * `mode` — `scale_to_zero` (default) or `always_warm`. See [Choosing a Mode](#choosing-a-mode). * `scaleToZero.idleHold` — Suspend SFTPGo after this window with no active connections (e.g. `90s`, `5m`, `1h`). Used only in `scale_to_zero` mode. * `scaleToZero.proxy.image` / `scaleToZero.proxy.resources` — Image and resources for the always-on activator proxy. All SFTP traffic flows through the proxy, so provision its CPU accordingly. ### SFTPGo * `image` — The SFTPGo container image. * `resources` — CPU and memory for the SFTPGo container. CPU governs transfer throughput and wake speed. * `admin.username` / `admin.password` — The SFTPGo administrator, created on first boot for the REST API and optional web admin. **Change the password before installing.** * `volumeset.capacity` — Volume size in GiB (minimum 10) for the embedded database and SSH host keys. * `webAdmin.enabled` — Declare SFTPGo's web admin / REST API on port `8080`. Reachable via the canonical endpoint in `always_warm` mode when `publicAccess` is enabled. ### Storage Backend Set `storage.type` to `aws`, `gcp`, or `minio`, and configure that block. AWS and GCP use a Control Plane cloud identity — no credentials are stored; the workload's identity vends temporary credentials at runtime. See [Prerequisites](#prerequisites) for the per-backend setup steps. | Field | Backends | Description | | ---------------------------- | -------------- | ------------------------------------------------- | | `bucket` | all | Target bucket. Must already exist. | | `region` | `aws`, `minio` | Bucket region. | | `keyPrefix` | all | Optional bucket-wide folder prefix, e.g. `sftp/`. | | `cloudAccountName` | `aws`, `gcp` | Control Plane cloud account for keyless access. | | `policyName` | `aws` | Bare name of the custom bucket-scoped IAM policy. | | `endpoint` | `minio` | S3 API address including port. | | `accessKey` / `accessSecret` | `minio` | Static credentials with access to the bucket. | ### Users Declared users are re-applied on every start; each is isolated to the bucket folder `{keyPrefix}{username}/` unless overridden per user. Provide a `password`, one or more `publicKeys`, or both. * `users[].username` — Login name. * `users[].password` — Password. Omit when `publicKeys` is set. * `users[].publicKeys` — SSH public keys, e.g. `["ssh-ed25519 AAAA... user@laptop"]`. * `users[].keyPrefix` — Optional per-user bucket folder override. ### Access * `publicAccess.enabled` — Expose a public SFTP endpoint on the client-facing workload via a dedicated direct load balancer, required for raw-TCP protocols like SFTP. Set to `false` for an internal-only endpoint (no load balancer; clients reach SFTPGo over the GVC network). * `internalAccess.type` — Internal firewall scope of the SFTPGo workload: | Type | Description | | --------------- | ---------------------------------------------------------------------------------------------------------------------- | | `none` | No internal access. Not allowed in `scale_to_zero` mode (the proxy must reach SFTPGo). | | `same-gvc` | Allow access from all workloads in the same GVC. | | `same-org` | Allow access from all workloads in the same organization. | | `workload-list` | Allow access only from workloads listed in `internalAccess.workloads`. The scale-to-zero proxy is added automatically. | ## Connecting | What | Value | | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------- | | Public SFTP endpoint | `tcp://...cpln.app:2022` — `status.endpoint` of `{release}-sftpgo-proxy` (scale\_to\_zero) or `{release}-sftpgo` (always\_warm) | | Connect | `sftp -P 2022 {username}@{endpoint-host}` | | In-GVC (internal) | `{release}-sftpgo-proxy:2022` (scale\_to\_zero) or `{release}-sftpgo:2022` (always\_warm) | | Web admin (if enabled) | Canonical `*.cpln.app` endpoint of `{release}-sftpgo` (always\_warm + publicAccess) | | Credentials | `users[]` entries; admin per `admin.*` | ## Cold Starts and Client Configuration In `scale_to_zero` mode, the first connection after an idle period wakes the server (measured \~30s, occasionally up to \~75s — the persistent volume attaches on each wake). The proxy holds the TCP connection so nothing is refused, but clients with short SSH banner timeouts (\~15s in several libraries) give up right at the finish line. Configure clients generously: * **paramiko** — `connect(..., banner_timeout=120, timeout=120)` * **WinSCP** — Session → Timeout ≥ 120s * **OpenSSH CLI** — Tolerant by default; no change needed * **Unattended jobs** — Retry with backoff. The first (even failed) attempt triggers the wake, and `idleHold` keeps the server warm so the retry lands instantly. * **Right after install** — The load balancer needs a few minutes to warm up; the very first cold connect may time out once, then succeed on retry. For third-party clients you cannot configure, use `always_warm`. ## Important Notes * **Change the default admin and user passwords before installing.** The admin bootstrap only applies on first boot; changing `admin.*` later requires the REST API. * **Declared users are authoritative** — edits made to them via the admin API/UI are overwritten on the next restart or wake. Users *created* via the API are untouched. * **Upgrading while suspended wakes the server**; it re-suspends after the next connection comes and goes. * **First install: the public endpoint's DNS takes a few minutes** to propagate after the load balancer is created. * **Switching modes moves the client-facing endpoint** (proxy ↔ SFTPGo) — plan a client cutover if you change modes on a live install. * **The dedicated load balancer is the dominant idle cost** in `scale_to_zero` mode, not the proxy's compute. ## External References Official SFTPGo documentation SFTPGo S3-compatible storage backend guide SFTPGo environment variables reference Source for the always-on TCP activator proxy View the source files, default values, and chart definition # Supabase Source: https://docs.controlplane.com/template-catalog/templates/supabase Deploy a self-hosted Supabase instance on Control Plane. Covers PostgreSQL with Supabase extensions, Kong API gateway, GoTrue authentication, PostgREST, Realtime, file storage, Studio dashboard, PgBouncer connection pooling, and logical or WAL-G backups. ## Overview Supabase is an open-source backend-as-a-service built on PostgreSQL. This template deploys the full self-hosted Supabase stack on Control Plane — all services run in your own GVC with no dependency on Supabase Cloud. ### Architecture * **Postgres** — Supabase-patched PostgreSQL 15 with pgvector, pg\_graphql, pg\_net, pgjwt, and other required extensions pre-installed. * **Kong** — API gateway and single public entry point. Routes all traffic to PostgREST, Auth, Realtime, and Storage. * **PostgREST** — Auto-generated REST and GraphQL API derived from your Postgres schema. Stateless, scales horizontally. * **Auth (GoTrue)** — Full-featured auth service supporting email/password, magic links, OAuth providers, and JWT sessions. Stateless, scales horizontally. * **Realtime** — WebSocket server that streams database change events to subscribed clients. Stateless, scales horizontally. * **Storage** — Object storage API backed by S3, GCS, or a local volume. * **Studio** — Web dashboard for managing your database, auth users, storage, and API settings. * **pg\_meta** — Postgres metadata API. Runs as a sidecar inside the Studio workload. * **PgBouncer** *(optional)* — Connection pooler that multiplexes application connections into a smaller pool of real database connections. * **Backup** *(optional)* — Logical (`pg_dump` cron) or WAL-G (continuous WAL archiving with PITR support). ### What Gets Created * **Stateful Postgres Workload** — Supabase-patched PostgreSQL with a persistent volume set. * **Standard Kong Workload** — API gateway, autoscales on RPS. * **Standard PostgREST Workload** — REST/GraphQL API, autoscales on RPS. * **Standard Auth Workload** — GoTrue auth service, autoscales on RPS. * **Standard Realtime Workload** — WebSocket change feed, autoscales on RPS. * **Standard Storage Workload** — Object storage API, autoscales on RPS *(optional, enabled by default)*. * **Standard Studio Workload** — Web dashboard with pg\_meta sidecar *(optional, enabled by default)*. * **Standard PgBouncer Workload** — Connection pooler *(optional, disabled by default)*. * **Cron Backup Workload** *(optional)* — Logical or WAL-G backup on a schedule. * **Volume Sets** — One for Postgres data; one for Storage when using the `local` backend. * **Identity & Policy** — Identities bound to each workload with `reveal` access to credential secrets, and cloud storage access when backup or S3/GCS storage is enabled. * **Secrets** — Dictionary secrets for Postgres credentials, JWT keys, and service API keys; opaque secrets for Kong routing config, SMTP settings, and OAuth provider credentials. This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites This template has no external prerequisites unless S3/GCS storage or backup is enabled. To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} postgres: image: supabase/postgres:15.8.1.060 password: change-me-postgres database: postgres resources: minCpu: 500m minMemory: 512Mi maxCpu: 2 maxMemory: 2Gi volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) autoscaling: enabled: false maxCapacity: 100 minFreePercentage: 10 scalingFactor: 1.2 internalAccess: type: same-gvc # options: none, same-gvc, same-org, workload-list workloads: #- //gvc/GVC_NAME/workload/WORKLOAD_NAME kong: image: kong:2.8.1 resources: minCpu: 100m minMemory: 256Mi maxCpu: 1000m maxMemory: 1Gi minReplicas: 1 maxReplicas: 3 publicAccess: enabled: false siteUrl: "" # e.g. https://api.my-app.com — required when publicAccess is enabled postgrest: image: postgrest/postgrest:v12.2.3 resources: minCpu: 100m minMemory: 128Mi maxCpu: 500m maxMemory: 512Mi minReplicas: 1 maxReplicas: 3 auth: image: supabase/gotrue:v2.170.0 resources: minCpu: 100m minMemory: 128Mi maxCpu: 500m maxMemory: 512Mi minReplicas: 1 maxReplicas: 3 disableSignup: false smtp: enabled: false host: smtp.example.com port: 587 user: smtp-user password: smtp-password senderName: Supabase senderEmail: noreply@example.com # providers: # github: # clientId: "" # clientSecret: "" # google: # clientId: "" # clientSecret: "" realtime: enabled: true image: supabase/realtime:v2.34.47 resources: minCpu: 100m minMemory: 128Mi maxCpu: 500m maxMemory: 512Mi minReplicas: 1 maxReplicas: 3 storage: enabled: true image: supabase/storage-api:v1.14.6 resources: minCpu: 100m minMemory: 128Mi maxCpu: 500m maxMemory: 512Mi minReplicas: 1 maxReplicas: 3 backend: s3 # options: local, s3, gcs volumeset: # only used when backend is local capacity: 10 autoscaling: enabled: false maxCapacity: 100 minFreePercentage: 10 scalingFactor: 1.2 s3: # only used when backend is s3 bucket: my-storage-bucket region: us-east-1 cloudAccountName: my-s3-cloudaccount policyName: my-storage-policy gcs: # only used when backend is gcs (S3-compatible HMAC auth, no cloud account needed) bucket: my-storage-bucket accessKeyId: my-gcs-hmac-access-key-id secretAccessKey: my-gcs-hmac-secret-access-key studio: enabled: true image: supabase/studio:2025.06.02-sha-8f2993d username: supabase password: change-me-studio resources: minCpu: 100m minMemory: 256Mi maxCpu: 500m maxMemory: 512Mi allowedCidrs: [] # - 203.0.113.0/24 # - 0.0.0.0/0 internalAccess: type: same-gvc meta: image: supabase/postgres-meta:v0.86.0 resources: minCpu: 100m minMemory: 64Mi maxCpu: 200m maxMemory: 256Mi jwt: secret: your-super-secret-jwt-token-with-at-least-32-characters-long secretKeyBase: your-super-secret-key-base-used-by-realtime-must-be-at-least-64-characters-long!! anonKey: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... serviceRoleKey: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... pgbouncer: enabled: false image: edoburu/pgbouncer:v1.25.1-p0 poolMode: transaction # options: session, transaction, statement defaultPoolSize: 25 maxClientConn: 1000 replicas: 1 resources: minCpu: 100m minMemory: 64Mi maxCpu: 200m maxMemory: 128Mi backup: enabled: false mode: logical # options: logical, walg provider: aws # options: aws, gcp resources: minCpu: 100m minMemory: 128Mi maxCpu: 200m maxMemory: 256Mi logical: image: ghcr.io/controlplane-com/backup-images/postgres-backup:17.1.0 schedule: "0 2 * * *" # daily at 2am UTC walg: intervalSeconds: 21600 # base backup every 6 hours aws: bucket: my-supabase-backup-bucket region: us-east-1 cloudAccountName: my-backup-cloudaccount policyName: my-backup-policy prefix: supabase/backups gcp: bucket: my-supabase-backup-bucket cloudAccountName: my-backup-cloudaccount prefix: supabase/backups ``` ### JWT Keys Supabase uses JWT to authenticate requests between services and from clients. * `jwt.secret` — The signing secret for all JWTs. Must be at least 32 characters. * `jwt.secretKeyBase` — Used by Realtime (Phoenix) for cookie signing. Must be at least 64 characters. Generate with `openssl rand -base64 64`. * `jwt.anonKey` — Public key for unauthenticated (anonymous) client access. * `jwt.serviceRoleKey` — Privileged key that bypasses row-level security. For trusted server-side code only. The default values are Supabase's official published development keys and work together out of the box. **Change all of them before any production deployment.** `anonKey` and `serviceRoleKey` must be valid HMAC-SHA256 JWTs signed with `jwt.secret`. Using mismatched keys causes `bad_jwt` errors across all services. Use the [Supabase key generator](https://supabase.com/docs/guides/self-hosting/docker#generate-api-keys) to produce a matching set. ### Kong (API Gateway) Kong is the single entry point for all Supabase API traffic. PostgREST, Auth, Realtime, and Storage are only reachable through Kong. * `kong.publicAccess.enabled` — Expose Kong on a public external endpoint. * `kong.publicAccess.siteUrl` — The full URL clients will reach Kong at (e.g. `https://api.my-app.com`). Required when `publicAccess` is enabled. GoTrue uses this for OAuth redirect callbacks and magic link emails. When `publicAccess` is disabled, internal clients use the Kong hostname within the GVC and OAuth/magic links will not work. ### Postgres You must use the `supabase/postgres` image. The standard `postgres` image is missing required extensions (pgvector, pg\_graphql, pg\_net, pgjwt, etc.) that GoTrue, PostgREST, Realtime, and Storage depend on. * `postgres.password` — Database superuser password. **Change before deploying to production.** * `postgres.resources` — CPU and memory limits/requests for the Postgres workload. * `postgres.volumeset.capacity` — Initial volume size in GiB (minimum 10). Autoscaling is configurable. * `postgres.internalAccess.type` — Controls which workloads can reach Postgres directly: `same-gvc`, `same-org`, `workload-list`, or `none`. All Supabase services connect internally. Use `workload-list` to also grant access to your own application workloads. ### Auth (GoTrue) * `auth.disableSignup` — Set to `true` to prevent new user registration. **SMTP** — required for magic link login, email confirmation, and password reset: * `auth.smtp.enabled` — Enable SMTP. When disabled, all signups are auto-confirmed and email flows are unavailable. * `auth.smtp.host` / `auth.smtp.port` — SMTP server address and port. * `auth.smtp.user` / `auth.smtp.password` — SMTP credentials. * `auth.smtp.senderName` / `auth.smtp.senderEmail` — Display name and from address for outgoing emails. Any SMTP provider works: Gmail, SendGrid, Mailgun, Postmark, Mailtrap, etc. **OAuth Providers** — enable by uncommenting `auth.providers` and adding credentials: ```yaml theme={null} auth: providers: github: clientId: "" clientSecret: "" google: clientId: "" clientSecret: "" ``` Supported providers: Apple, Azure, Bitbucket, Discord, Facebook, Figma, GitHub, GitLab, Google, Kakao, Keycloak, LinkedIn, Notion, Slack, Spotify, Twitch, Twitter/X, WorkOS, Zoom. When setting up OAuth in your provider's developer console: * **Authorized JavaScript Origin**: `{kong.publicAccess.siteUrl}` * **Authorized Redirect URI**: `{kong.publicAccess.siteUrl}/auth/v1/callback` OAuth requires `kong.publicAccess.enabled: true` and a valid `siteUrl`. OAuth providers will not redirect to internal hostnames. ### Storage Three backends are supported: | Backend | Description | | ------- | -------------------------------------------------------------------------------- | | `s3` | Stateless, horizontally scalable. Recommended for production. | | `gcs` | GCS accessed via the S3-compatible API using HMAC keys. No Cloud Account needed. | | `local` | Stateful, single-replica, volume-backed. For development only. | For `s3`: set `storage.s3.bucket`, `storage.s3.region`, `storage.s3.cloudAccountName`, and `storage.s3.policyName`. The IAM policy must grant `s3:GetObject`, `s3:PutObject`, and `s3:DeleteObject` on the bucket. For `gcs`: create HMAC keys in the GCP console under **Cloud Storage → Settings → Interoperability** and set `storage.gcs.accessKeyId` and `storage.gcs.secretAccessKey`. If using S3/GCS for backup as well, storage and backup require **separate** buckets, cloud accounts, and IAM policies. ### Studio (Web Dashboard) Studio is protected by username and password login and has no external access by default. * `studio.username` / `studio.password` — Login credentials. **Change before deploying to production.** * `studio.allowedCidrs` — List of CIDRs allowed to reach Studio externally. Empty by default (no external access). Use `cpln workload connect` for local tunnel access, or set to `["0.0.0.0/0"]` to open it publicly (login is still required). ### PgBouncer PgBouncer multiplexes application connections into a smaller pool of real database connections, reducing Postgres connection overhead under high concurrency. Disabled by default. | Pool Mode | Description | | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `transaction` | Connection held only for the duration of a transaction. Best for most web and API workloads. Not compatible with `SET` variables, temporary tables, or advisory locks. | | `session` | Connection held for the entire client session. Compatible with all Postgres features. | | `statement` | Connection returned after every statement. Transactions not supported. | * `pgbouncer.defaultPoolSize` — Number of real Postgres connections per pool (default: `25`). * `pgbouncer.maxClientConn` — Maximum number of client connections PgBouncer accepts (default: `1000`). When enabled, connect through PgBouncer rather than directly to Postgres. ## Connecting All API traffic flows through Kong. Connect your application using the Kong endpoint: | Endpoint | Address | | ------------------------ | ----------------------------------------------------------------- | | API — internal | `{release-name}-kong.{gvc}.cpln.local:8000` | | API — public | `{kong.publicAccess.siteUrl}` *(when publicAccess enabled)* | | Postgres — direct | `{release-name}-postgres.{gvc}.cpln.local:5432` | | Postgres — via PgBouncer | `{release-name}-pgbouncer.{gvc}.cpln.local:5432` *(when enabled)* | Key API paths routed through Kong: | Service | Path | | -------------------- | --------------- | | PostgREST (REST API) | `/rest/v1/` | | Auth (GoTrue) | `/auth/v1/` | | Storage | `/storage/v1/` | | Realtime | `/realtime/v1/` | Pass `apikey: {anonKey}` as a header on all requests. Use `serviceRoleKey` for privileged server-side calls. The Supabase client library handles auth headers automatically: ```js theme={null} import { createClient } from '@supabase/supabase-js' const supabase = createClient( 'https://api.my-app.com', // kong.publicAccess.siteUrl 'YOUR_ANON_KEY' ) ``` ## Backing Up Two backup modes are available: | Mode | Mechanism | Best For | | --------- | --------------------------------------- | --------------------------------------------------------------- | | `logical` | `pg_dump` cron | Portable SQL dumps, smaller databases, cross-version migrations | | `walg` | Continuous WAL archiving + base backups | Production — supports point-in-time recovery (PITR) | Set `backup.enabled: true`, choose a `mode` and `provider`, then fill in the corresponding provider block. Switching `backup.mode` between `logical` and `walg` changes Postgres `archive_mode` and `wal_level`, which requires a Postgres restart. Plan the switch accordingly. ### AWS S3 Create an S3 bucket. Set `backup.aws.bucket` and `backup.aws.region` to match. If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account). Set `backup.aws.cloudAccountName` to its name. Create an IAM policy with the following JSON (replace `YOUR_BUCKET_NAME`) and set `backup.aws.policyName` to its name: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket", "s3:GetObjectVersion", "s3:DeleteObjectVersion" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET_NAME", "arn:aws:s3:::YOUR_BUCKET_NAME/*" ] } ] } ``` ### GCS Create a GCS bucket. Set `backup.gcp.bucket` to its name. If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account). Set `backup.gcp.cloudAccountName` to its name. You must add the `Storage Admin` role to the GCP service account created for the Cloud Account. ## Restoring a Backup ### Logical Restore Run from a machine with network access to Postgres (e.g. via `cpln workload connect`): **AWS S3:** ```bash theme={null} export PGPASSWORD="YOUR_POSTGRES_PASSWORD" aws s3 cp "s3://BUCKET_NAME/PREFIX/BACKUP_FILE.sql.gz" - \ | gunzip \ | psql \ --host={release-name}-postgres.{gvc}.cpln.local \ --port=5432 \ --username=postgres \ --dbname=postgres unset PGPASSWORD ``` **GCS:** ```bash theme={null} export PGPASSWORD="YOUR_POSTGRES_PASSWORD" gsutil cp "gs://BUCKET_NAME/PREFIX/BACKUP_FILE.sql.gz" - \ | gunzip \ | psql \ --host={release-name}-postgres.{gvc}.cpln.local \ --port=5432 \ --username=postgres \ --dbname=postgres unset PGPASSWORD ``` ### WAL-G Restore WAL-G restores require an empty data directory. ```bash theme={null} cpln workload connect {release-name}-postgres --gvc {gvc} --container wal-g-backup -- wal-g backup-list ``` Stop the workload via the Control Plane console or CLI. Create a fresh empty volume set for the restore target — do not reuse the existing one. Run a one-off workload with the new volume set mounted at `/var/lib/postgresql/data` and execute: ```bash theme={null} wal-g backup-fetch /var/lib/postgresql/data/pg_data BACKUP_NAME ``` Re-point the Postgres workload to the restored volume set and restart. After restore, update `backup.aws.prefix` (or `backup.gcp.prefix`) to a new path before re-enabling backups to avoid WAL stream conflicts with the original cluster's archived segments. ## Important Notes * **Use the Supabase Postgres image** — `supabase/postgres` is required. The standard `postgres` image is missing extensions that GoTrue, PostgREST, Realtime, and Storage depend on. * **JWT keys must match** — `anonKey` and `serviceRoleKey` must be HMAC-SHA256 JWTs signed with `jwt.secret`. Mismatched keys produce `bad_jwt` errors. Use the [official Supabase key generator](https://supabase.com/docs/guides/self-hosting/docker#generate-api-keys) to produce a matching set. * **Change default credentials before production** — `jwt.secret`, `postgres.password`, and `studio.password` are placeholders. Replace all of them before any production deployment. * **OAuth requires a public siteUrl** — Set `kong.publicAccess.enabled: true` and `kong.publicAccess.siteUrl` before configuring any OAuth provider. * **Storage and backup use separate buckets** — Do not share a bucket between `storage.s3` and `backup.aws`. Each requires its own bucket, cloud account, and IAM policy. * **Studio has no external access by default** — Use `cpln workload connect` for local access, or set `studio.allowedCidrs` to open it externally. ## External References Official self-hosting documentation and architecture overview JavaScript, Python, Swift, Kotlin, and other client SDK references GoTrue auth service configuration and API reference Auto-generated REST API from your Postgres schema WAL-G continuous archiving and PITR restore documentation Generate matching JWT secret, anonKey, and serviceRoleKey # Tailscale Source: https://docs.controlplane.com/template-catalog/templates/tailscale Deploy Tailscale on Control Plane using the Template Catalog. Covers configuration, scaling, and mesh VPN gateway setup for routing Tailscale clients to GVC workloads. ## Overview Tailscale is a mesh VPN built on WireGuard. This template deploys a Tailscale gateway workload on Control Plane that joins your Tailscale network and advertises internal Control Plane routes, allowing any connected Tailscale client to reach your GVC workloads via their `cpln.local` internal endpoints. The gateway runs in a single configured location. Other locations are suspended so only one Tailscale node is active at a time. ### What Gets Created * **Standard Workload** — The Tailscale gateway (`RELEASE_NAME-tailscale`), active in the configured location only. Advertises Control Plane's internal network CIDRs and the location's internal DNS server to Tailscale. * **Secret** — A dictionary secret storing the Tailscale auth key, injected into the container at startup. * **Identity & Policy** — An identity bound to the workload with `reveal` access to the auth key secret. * **Serverless Workload** *(optional)* — An httpbin test workload (`RELEASE_NAME-httpbin`) accessible only from the Tailscale workload, useful for verifying connectivity. This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites Complete the following in your Tailscale account before installing: ### 1. Create an Auth Key In the Tailscale [Admin UI → Settings → Keys](https://login.tailscale.com/admin/settings/keys), create a new auth key with **Reusable** and **Ephemeral** enabled. Save the key value — you will set it as `AuthKey` in your values. ### 2. Update the Tailscale ACL Add `autoApprovers` to your [Tailscale ACL](https://login.tailscale.com/admin/acls/file) so that advertised routes are approved automatically: ```json theme={null} { "autoApprovers": { "routes": { "192.168.0.0/16": ["autogroup:member"], "240.240.0.0/16": ["autogroup:member"], "10.0.0.0/16": ["autogroup:member"], "172.20.0.10/32": ["autogroup:member"], "10.1.0.10/32": ["autogroup:member"], "10.194.112.10/32": ["autogroup:member"] } } } ``` If your location uses a DNS IP not listed above, add its `/32` entry here as well. ### 3. Configure Tailscale DNS In the Tailscale [Admin UI → DNS tab](https://login.tailscale.com/admin/dns), add a custom nameserver scoped to the `cpln.local` domain. Use the DNS IP for your selected location from the `locationDNS` map in values (e.g. `172.20.0.10` for most AWS locations). If you need to reach per-replica stateful workload endpoints, add an additional custom nameserver scoped to `.svc.cluster.local` using the same DNS IP. ## Installation To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} # The single location where the Tailscale gateway will be active location: aws-us-east-1 image: repository: tailscale/tailscale tag: stable resources: cpu: 500m memory: 128Mi extraEnv: - name: TS_HOSTNAME value: cpln-test-new # - name: TS_EXTRA_ARGS # value: --advertise-exit-node # Internal DNS server IPs per location — used to advertise a DNS route to Tailscale clients locationDNS: aws-us-west-2: 172.20.0.10 aws-us-east-1: 172.20.0.10 aws-us-east-2: 172.20.0.10 aws-sa-east-1: 172.20.0.10 aws-eu-west-2: 172.20.0.10 aws-eu-central-1: 172.20.0.10 aws-il-central-1: 172.20.0.10 gcp-us-west1: 10.67.224.10 gcp-us-east1: 10.194.112.10 gcp-us-central1: 10.228.48.10 gcp-me-west1: 10.110.48.10 azure-centralus: 10.1.0.10 azure-eastus2: 10.1.0.10 deployHttpbinExample: true AuthKey: replaceWithYourTailscaleAuthKey ``` ### Auth Key * `AuthKey` — Your Tailscale auth key. **Replace this before deploying.** The key must be created with the **Reusable** and **Ephemeral** options enabled. ### Location * `location` — The single Control Plane location where the Tailscale gateway will run. All other locations in the GVC are suspended. Must match one of the locations configured in your GVC. The Tailscale workload will appear as `Partially Suspended` in the console — this is expected behavior. ### Advertised Routes The gateway automatically advertises the following routes to Tailscale: | Route | Purpose | | ---------------------------- | --------------------------------------------- | | `192.168.0.0/16` | Control Plane internal network | | `240.240.0.0/16` | Control Plane internal network | | `/32` | Internal DNS server for the selected location | The `/32` DNS host route allows Tailscale clients to resolve `cpln.local` endpoints after configuring a custom nameserver in Tailscale DNS. ### Location DNS * `locationDNS` — A map of Control Plane location names to their internal DNS server IPs. The IP for the selected `location` is advertised as a host route (`/32`) to enable `cpln.local` name resolution on Tailscale clients. If your location is not listed, add it to the map with the correct DNS IP for that location. ### Resources and Image * `resources.cpu` / `resources.memory` — CPU and memory for the Tailscale gateway container. * `image.repository` / `image.tag` — Tailscale container image. Defaults to `tailscale/tailscale:stable`. ### Extra Environment Variables * `extraEnv` — Additional environment variables passed to the Tailscale container. Common options: * `TS_HOSTNAME` — The hostname the gateway registers under in your Tailscale network. * `TS_EXTRA_ARGS` — Additional Tailscale daemon flags, e.g. `--advertise-exit-node` to use the gateway as an exit node. ### Example Workload * `deployHttpbinExample` — When `true`, deploys an httpbin test workload that only accepts internal connections from the Tailscale gateway. Use it to verify connectivity after setup. ## Accessing GVC Workloads Once the gateway is running and connected to Tailscale: 1. Ensure your local machine is connected to the same Tailscale network. 2. Any GVC workload that grants internal access to the Tailscale workload can be reached via its `cpln.local` address: ```text theme={null} WORKLOAD_NAME.GVC_NAME.cpln.local:PORT ``` To grant access, add the Tailscale workload to a target workload's internal firewall: ```yaml theme={null} firewallConfig: internal: inboundAllowType: workload-list inboundAllowWorkload: - //gvc/GVC_NAME/workload/RELEASE_NAME-tailscale ``` ## External References Official Tailscale documentation Manage your Tailscale network, keys, ACLs, and DNS View the source files, default values, and chart definition # Temporal Source: https://docs.controlplane.com/template-catalog/templates/temporal Deploy Temporal on Control Plane using the Template Catalog. Durable-execution platform for workflows that survive crashes and restarts, backed by a highly available PostgreSQL cluster. Covers worker connections, database modes, the permanent history-shard count, and the internal-only Web UI. ## Overview Temporal is an open-source (MIT) durable-execution platform — applications write workflows as ordinary code, and the Temporal server guarantees they run to completion, surviving process crashes, restarts, and long waits. This template deploys a single-process Temporal server exposing the gRPC frontend to your workloads, plus the Temporal Web UI, both internal-only, backed by a highly available PostgreSQL cluster by default. Database and schema setup run automatically at boot, including schema migrations on version upgrades. ### Architecture * **Temporal server** — A single-replica standard workload running all four Temporal services (frontend, history, matching, worker) in one process; gRPC frontend on port `7233`, internal-only. Stateless by design: all workflow state lives in PostgreSQL. * **Temporal Web UI** *(optional, default on)* — A standard workload serving the UI on port `8080`, internal-only (the UI has no built-in authentication). * **PostgreSQL (HA, default)** — The [postgres-highly-available](/template-catalog/templates/postgres-highly-available) template as a subchart: 3× Patroni PostgreSQL, 3× etcd, and a HAProxy leader-routing endpoint Temporal connects through. * **PostgreSQL (dev/lightweight, optional)** — The single-instance [postgres](/template-catalog/templates/postgres) template instead, for lighter non-HA deployments. ### What Gets Created * **Standard Temporal Server Workload** — A single replica running all four Temporal services in one process, serving gRPC on port `7233`. No volume set — all state lives in the database. * **Standard Temporal UI Workload** *(optional)* — The Temporal Web UI on port `8080`. * **Database Workloads** — HA mode: a stateful Patroni PostgreSQL workload, a stateful etcd workload, and a standard HAProxy leader-routing workload. Single mode: one stateful PostgreSQL workload. * **Volume Sets** — The database subchart's persistent volumes (10 GiB per replica by default). Temporal itself has none. * **Secrets** — The database credentials from the subchart. * **Identity & Policy** — A least-privilege policy granting the server identity `reveal` on exactly the database credentials secret. The UI workload has no identity — it mounts no secrets. * **Cron Backup Workload** *(optional)* — When database backups are enabled. This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites None for a default install. For optional database backups, you need a bucket and access setup for one of the supported providers — see [Backing Up](#backing-up). Install the template using your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Choosing a Database Mode Exactly one of the two database modes must be enabled — the chart enforces this at render and fails the install with a clear message otherwise. | | `postgresHA` (default) | `postgres` | | ----------------- | --------------------------------------------------------------- | -------------------------------------- | | What runs | 3× Patroni PostgreSQL, 3× etcd, HAProxy leader endpoint | One single-replica PostgreSQL workload | | Database failover | Automatic (Patroni leader election) | None | | Footprint | 8 replicas across 3 workloads (3× Patroni, 3× etcd, 2× HAProxy) | 1 workload | | Best for | Production | Development and lightweight installs | A fresh HA-mode install converges in roughly 4–6 minutes — the server waits for the database, then runs full schema setup before it starts listening. Single mode is ready in under 3 minutes. ## Configuration The default `values.yaml` for this template: ```yaml theme={null} image: temporalio/auto-setup:1.29.7 # Temporal server + schema tools; boot runs schema setup, then the server resources: cpu: 1000m memory: 2Gi minCpu: 500m minMemory: 1Gi historyShards: 512 # PERMANENT after first install — cannot ever be changed for this cluster namespaceRetention: 72h # how long closed workflow histories are kept in the default namespace ui: # internal-only: the Temporal UI has no built-in authentication enabled: true image: temporalio/ui:2.52.1 resources: cpu: 500m memory: 512Mi minCpu: 125m minMemory: 128Mi internalAccess: # internal firewall scope for the gRPC frontend (:7233) and the UI (:8080) type: same-gvc # options: none, same-gvc, same-org, workload-list workloads: [] # used with workload-list, e.g. //gvc/GVC_NAME/workload/WORKLOAD_NAME postgresHA: # default: highly available PostgreSQL enabled: true postgres: username: temporal password: change-me-temporal-db-password # change before installing database: temporal replicas: 3 volumeset: capacity: 10 # initial capacity in GiB per replica (minimum is 10) backup: enabled: false # optional — see Backing Up mode: logical # logical or wal-g resources: cpu: 100m memory: 128Mi logical: image: ghcr.io/controlplane-com/backup-images/postgres-backup:17.1.0 schedule: "0 2 * * *" walg: intervalSeconds: 21600 provider: aws # options: aws, gcp, minio aws: bucket: temporal-pg-backup-bucket region: us-east-1 cloudAccountName: my-s3-cloud-account policyName: temporal-pg-backup-policy prefix: postgres/backups gcp: bucket: temporal-pg-backup-bucket cloudAccountName: my-gcs-cloud-account prefix: postgres/backups minio: endpoint: http://my-minio-workload:9000 bucket: temporal-pg-backup-bucket accessKey: my-minio-username secretKey: my-minio-password prefix: postgres/backups postgres: # dev/lightweight: single-instance PostgreSQL (disable postgresHA first) enabled: false config: username: temporal password: change-me-temporal-db-password # change before installing database: temporal volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) backup: enabled: false # optional — see Backing Up image: ghcr.io/controlplane-com/backup-images/postgres-backup:18.1.0 schedule: "0 2 * * *" resources: cpu: 100m memory: 128Mi provider: aws # options: aws, gcp, minio aws: bucket: temporal-pg-backup-bucket region: us-east-1 cloudAccountName: my-s3-cloud-account policyName: temporal-pg-backup-policy prefix: postgres/backups gcp: bucket: temporal-pg-backup-bucket cloudAccountName: my-gcs-cloud-account prefix: postgres/backups minio: endpoint: http://my-minio-workload:9000 bucket: temporal-pg-backup-bucket accessKey: my-minio-username secretKey: my-minio-password prefix: postgres/backups ``` ### Temporal Server * `image` — The upstream `auto-setup` image: at boot it creates the `temporal` and `temporal_visibility` databases, applies or upgrades the schema, registers the `default` namespace, then starts the server — all idempotently, so restarts and upgrades are safe. Image bumps apply new schema migrations automatically at boot. * `resources` — CPU and memory for the server container. * `historyShards` — The cluster's history shard count. **Permanent: fixed at the cluster's first boot and can never be changed** — the server refuses a different value later. Size it before installing; 512 suits most deployments. * `namespaceRetention` — How long closed workflow histories stay queryable in the `default` namespace (e.g. `72h`, `96h`). ### Web UI * `ui.enabled` — Set `false` to remove the UI workload. * `ui.image` / `ui.resources` — The Temporal UI container image and its CPU/memory. The Temporal Web UI has **no built-in authentication** and is internal-only by design — this template has no knob to expose it publicly. To offer browser access from outside the internal scope, put your own authenticating proxy in front of it. ### Access Both workloads are internal-only — there is no public-access option in this template. The gRPC frontend serves workers and clients running as workloads in your org. `internalAccess.type` sets the internal firewall scope for both the gRPC frontend (`:7233`) and the UI (`:8080`): | Type | Description | | --------------- | ---------------------------------------------------------------------- | | `none` | No internal access. | | `same-gvc` | Allow access from all workloads in the same GVC (default). | | `same-org` | Allow access from all workloads in the same organization. | | `workload-list` | Allow access only from workloads listed in `internalAccess.workloads`. | ### Database Enable exactly one of `postgresHA` (production, default) or `postgres` (dev/lightweight) — see [Choosing a Database Mode](#choosing-a-database-mode). In both modes, **change the database password before installing** (`postgresHA.postgres.password` / `postgres.config.password`). Temporal is wired to the active database automatically — the HAProxy leader endpoint in HA mode, or the single instance directly in dev mode. **Template version `1.0.0` did not compact the etcd cluster inside the bundled highly available database**, so etcd's backend grows with time alone and goes read-only once it reaches its 2 GiB quota — after roughly 110 days — taking PostgreSQL failover with it. Only installs running the HA database are affected (`postgresHA.enabled`, the default here); see [etcd History Compaction](/template-catalog/templates/postgres-highly-available#etcd-history-compaction) for the mechanism and the symptoms. Upgrade to `1.0.1` or later to turn compaction on: that stops further growth but cannot shrink a backend that has already grown, and a cluster that has already raised a `NOSPACE` alarm needs operator recovery rather than an upgrade. ## Connecting | What | Value | | ---------------------------------- | ---------------------------------------------------------------------------------------------------------- | | gRPC frontend (workers/clients) | `{release}-temporal.{gvc}.cpln.local:7233` | | Namespace | `default` | | Web UI (internal) | `http://{release}-temporal-ui.{gvc}.cpln.local:8080` | | PostgreSQL (internal, HA mode) | `{release}-postgres-ha-proxy.{gvc}.cpln.local:5432`, credentials in the `{release}-postgres-config` secret | | PostgreSQL (internal, single mode) | `{release}-postgres.{gvc}.cpln.local:5432`, credentials in the `{release}-pg-config` secret | Always use the full `.cpln.local` FQDN in worker and client connection config — short workload names do not resolve. ### Running Workers Workers run as their own workloads (in the internal access scope) and connect to the gRPC frontend. A minimal Python worker: ```python theme={null} import asyncio from temporalio.client import Client from temporalio.worker import Worker async def main(): client = await Client.connect( "my-release-temporal.my-gvc.cpln.local:7233", # full FQDN required namespace="default", ) worker = Worker( client, task_queue="my-task-queue", workflows=[MyWorkflow], activities=[my_activity], ) await worker.run() if __name__ == "__main__": # required — see note below asyncio.run(main()) ``` Python workers must guard their entrypoint with `if __name__ == "__main__":` — the Temporal Python SDK's workflow sandbox re-imports the worker module, and an unguarded module-level `main()` crashes the worker. ## Backing Up Database backups are optional and disabled by default. They cover the Temporal databases — all workflow histories, task queues, timers, and visibility data. Enable them with `postgresHA.backup.enabled` or `postgres.backup.enabled` (matching your database mode), and complete the storage setup for your provider **before** installing. The values below are shown under `backup.*` — set them within the enabled database block. Create an S3 bucket. Set `backup.aws.bucket` and `backup.aws.region` to match. If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) for your AWS account. Set `backup.aws.cloudAccountName` to its name. Create an AWS IAM policy with the JSON below (replace `YOUR_BUCKET`), then set `backup.aws.policyName` to the policy's name: ```json theme={null} { "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Action": [ "s3:ListBucket", "s3:GetBucketLocation", "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:AbortMultipartUpload" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET", "arn:aws:s3:::YOUR_BUCKET/*" ] }] } ``` Create a GCS bucket. Set `backup.gcp.bucket` to its name. If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) for your GCP project. Set `backup.gcp.cloudAccountName` to its name — the backup identity is granted access to the bucket keylessly (no stored credentials). Create your bucket on the server. Set `backup.minio.bucket` to its name. Set `backup.minio.endpoint` to the S3 API address including port. For the `minio` marketplace template deployed in the same GVC, this is `http://WORKLOAD_NAME:9000`. Set `backup.minio.accessKey` and `backup.minio.secretKey` to credentials with access to the bucket. In HA mode, `backup.mode` selects `logical` (scheduled `pg_dump` via a cron workload) or `wal-g` (continuous WAL archiving). The single-instance mode takes scheduled logical dumps. ## Important Notes * **Change the database password** (`postgresHA.postgres.password` / `postgres.config.password`) before installing. * **`historyShards` is permanent** — the shard count is fixed at the cluster's first boot and can never be changed; the server refuses a different value later. Size it before installing (512 suits most deployments). * **Never expose the Web UI publicly** — it has no built-in authentication. To offer browser access from outside the internal scope, put your own authenticating proxy in front of it. * **Temporal connects and runs schema setup as the database superuser** provisioned by the PostgreSQL subchart — it needs `CREATE DATABASE` and DDL rights at every version upgrade. * **Upgrades restart the single server replica and apply schema migrations automatically at boot** — expect a brief frontend outage per Helm upgrade; in-flight workflows resume where they left off once the server is back. * **Keep the server at one replica** — manually scaling the workload will not form a working cluster (required inter-service ports are not declared and the database connection limit would be exceeded). * **Uninstall deletes the database volume sets** — all workflow histories and state. Enable backups if the data matters. ## External References Official Temporal documentation Running and operating a self-hosted Temporal service Writing workflows, activities, and workers in every supported language Namespaces and workflow-history retention The Temporal Web UI reference View the source files, default values, and chart definition # Thanos Source: https://docs.controlplane.com/template-catalog/templates/thanos Deploy Thanos on Control Plane using the Template Catalog. A global PromQL query layer that fans out over your Prometheus servers with HA deduplication, plus an optional Store Gateway and Compactor for object-storage-backed long-term retention. ## Overview Thanos turns many independent Prometheus servers into one queryable metrics system. This template deploys Thanos Query — a stateless global PromQL layer that fans each query out over the gRPC Store API endpoints you list (typically the Thanos sidecar of each [Prometheus template](/template-catalog/templates/prometheus) install, across GVCs and regions) and deduplicates HA pairs — plus an optional object-storage tier (Store Gateway + Compactor) that serves and manages long-term metric history from your bucket. This is a **self-hosted query layer for your own metrics from your own Prometheus servers**. It is separate from — and not a replacement for — Control Plane's built-in observability, which continues to collect and dashboard your workloads' metrics natively. ### Architecture * **Thanos Query** — A stateless standard workload. UI and PromQL API on port `10902`, gRPC Store API on `10901`. Single replica by default — set `replicas: 2` or more for an HA query tier; queries keep succeeding through rolling restarts and replica loss. * **Store Gateway** *(optional, default off)* — A stateful workload that serves historical blocks from your object bucket to Query, with a local index/meta cache volume (safe to lose — it rebuilds on start). Query picks it up automatically when enabled. * **Compactor** *(optional, default off)* — A stateful **singleton** that compacts, downsamples, and applies retention to the blocks in your bucket. * **Object storage** *(optional)* — The bucket your Prometheus Thanos sidecars upload blocks to; the Store Gateway and Compactor share one rendered `objstore.yml` configuration for it. ### What Gets Created * **Standard Thanos Query Workload** — Serves the UI and PromQL API on port `10902` and its own Store API on `10901`. * **Stateful Store Gateway Workload** *(optional)* — Serves historical bucket blocks to Query. * **Stateful Compactor Workload** *(optional)* — Compacts, downsamples, and applies retention to bucket blocks. * **Volume Sets** *(optional)* — A 10 GiB cache volume for the Store Gateway and a 20 GiB workspace volume for the Compactor. * **Secret** *(optional)* — An opaque secret holding the rendered `objstore.yml` bucket configuration, mounted as a file; created only when the storage tier is enabled. * **Identity & Policy** — A shared identity bound to all workloads; when the storage tier is enabled, a policy grants it `reveal` on the objstore secret, and cloud access is scoped to exactly your bucket (AWS and GCP backends). This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites None for a default install — Query boots with an empty store list and you point it at your store endpoints via `stores`. The optional storage tier (`storeGateway` / `compactor`) requires an existing bucket in one of the supported backends — the **same bucket** your Prometheus template's Thanos sidecar uploads blocks to. Complete the steps for your chosen backend below. AWS S3 uses a Control Plane cloud identity — no credentials are stored; the workload's identity vends temporary credentials at runtime. Create an S3 bucket. Set `storage.aws.bucket` and `storage.aws.region` to match. If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) for your AWS account. Set `storage.aws.cloudAccountName` to its name. Create an AWS IAM policy with the JSON below (replace `YOUR_BUCKET`), then set `storage.aws.policyName` to the policy's name (bare name, not ARN): ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["s3:ListBucket", "s3:GetObject", "s3:PutObject", "s3:DeleteObject"], "Resource": [ "arn:aws:s3:::YOUR_BUCKET", "arn:aws:s3:::YOUR_BUCKET/*" ] } ] } ``` Google Cloud Storage uses a Control Plane cloud identity — no credentials are stored. Create a GCS bucket. Set `storage.gcp.bucket` to its name. If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) for your GCP project. Set `storage.gcp.cloudAccountName` to its name. No policy to author — the template grants the workload identity `roles/storage.objectAdmin` scoped to the bucket automatically. Any S3-compatible server uses static access credentials, since these cannot federate with a cloud account. Create your bucket on the server and credentials that can read and write it. Set `storage.minio.bucket`, `storage.minio.accessKey`, and `storage.minio.accessSecret` to match. Set `storage.minio.endpoint` to the S3 API address as `host:port` with **no scheme** (e.g. `my-minio:9000`), and `storage.minio.insecure: true` when the endpoint is plain HTTP. Install the template using your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} image: quay.io/thanos/thanos:v0.42.2 resources: # Thanos Query cpu: 500m memory: 1Gi minCpu: 250m minMemory: 512Mi replicas: 1 # Query is stateless — set 2+ for an HA query tier stores: [] # gRPC Store API endpoints Query fans out over, host:port with NO scheme # stores: # - my-prometheus-prometheus.metrics-east.cpln.local:10901 # - my-prometheus-b-prometheus.metrics-west.cpln.local:10901 queryReplicaLabels: # external label(s) marking HA duplicates to deduplicate - replica publicAccess: enabled: false # true = Query UI/API at a *.cpln.app URL — it has NO built-in auth internalAccess: type: same-gvc # options: none, same-gvc, same-org, workload-list workloads: [] # used with workload-list, e.g. //gvc/GVC/workload/NAME storeGateway: enabled: false # serves historical bucket blocks to Query resources: cpu: 500m memory: 1Gi minCpu: 250m minMemory: 512Mi volumeset: capacity: 10 # GiB — local index/meta cache; safe to lose (rebuilds on start) compactor: enabled: false # MUST be the ONLY compactor on the bucket, across all installs resources: cpu: 500m memory: 1Gi # size up for large buckets — compaction is memory-hungry minCpu: 250m minMemory: 512Mi volumeset: capacity: 20 # GiB — compaction workspace; size ~2x two weeks of raw blocks retention: # per-resolution bucket retention; "0d" keeps forever raw: "0d" fiveMinutes: "0d" oneHour: "0d" storage: # used only when storeGateway or compactor is enabled type: aws # options: aws, gcp, minio aws: # AWS S3 — keyless via Control Plane cloud identity bucket: my-thanos-bucket # bucket must already exist region: us-east-1 cloudAccountName: my-s3-cloud-account # Control Plane AWS cloud account policyName: my-thanos-s3-policy # custom bucket-scoped IAM policy (bare name) gcp: # Google Cloud Storage — keyless via Control Plane cloud identity bucket: my-thanos-bucket # bucket must already exist cloudAccountName: my-gcs-cloud-account # Control Plane GCP cloud account minio: # Any S3-compatible server (MinIO, R2, Wasabi, …) — static keys + endpoint endpoint: my-minio:9000 # host:port, NO scheme insecure: true # true when the endpoint is plain HTTP bucket: my-thanos-bucket # bucket must already exist region: us-east-1 accessKey: my-minio-username accessSecret: my-minio-password ``` ### Query * `image` — The Thanos container image, shared by all components. * `resources` — CPU and memory for the Query container (the Store Gateway and Compactor have their own `resources` blocks under their keys). * `replicas` — Query is stateless, so replicas need no coordination. Set `2` or more for an HA query tier that serves queries without interruption through rolling restarts and replica loss. ### Store Endpoints Each `stores` entry is a gRPC Store API endpoint — typically the Thanos sidecar of a [Prometheus template](/template-catalog/templates/prometheus) install — as `host:port` with **no scheme**: | Source | Endpoint form | | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Same GVC | `WORKLOAD.GVC.cpln.local:10901` | | Cross-GVC / cross-region | `WORKLOAD.GVC.cpln.local:10901` (service-level DNS — the Prometheus template is single-replica, so this reaches the one replica directly and is the most reliable form) | Two things to know for cross-GVC endpoints, both **on the Prometheus side**: 1. The target workload's internal firewall must allow inbound from this Query workload — set its `internalAccess.type` to `same-org`, or `workload-list` including `//gvc/GVC/workload/RELEASE-thanos`. A store showing as "down" in the Query UI is almost always this firewall. 2. Cross-location internal traffic (Query in one region querying sidecars in another) incurs egress charges — co-locate the query tier with its stores where practical. `queryReplicaLabels` names the external label(s) that mark HA duplicates: series identical except for this label are collapsed into one at query time. The default `replica` matches the Prometheus template's [HA pair recipe](/template-catalog/templates/prometheus#high-availability) — the label name must exactly match what your HA pair sets. Changing `stores` takes effect via `helm upgrade` — endpoints are workload arguments, so an upgrade safely redeploys Query with the new list, and Query keeps answering queries even while some listed endpoints are down. ### Access * `publicAccess.enabled` — Exposes the Query UI and PromQL API at a `*.cpln.app` canonical endpoint. **Query has no built-in authentication**, so anyone with the URL can run queries against everything it federates — keep this off (the default) or front Query with your own authenticating proxy. * `internalAccess.type` — Controls which workloads can reach Query internally: | Type | Description | | --------------- | --------------------------------------------------------------- | | `none` | No internal access allowed | | `same-gvc` | Allow access from all workloads in the same GVC | | `same-org` | Allow access from all workloads in the same organization | | `workload-list` | Allow access only from specific workloads listed in `workloads` | `workloads` entries take the form `//gvc/GVC_NAME/workload/WORKLOAD_NAME`. ### Long-Term Storage Tier Off by default, the storage tier serves metric history from the object bucket your Prometheus sidecars upload blocks to — configure the same bucket under `storage`: * `storeGateway.enabled` — Runs the Store Gateway, which serves historical bucket blocks to Query. Query picks up its endpoint automatically — no `stores` entry needed. Its volume is only a local index/meta cache and is safe to lose. * `compactor.enabled` — Runs the Compactor, which compacts and downsamples bucket blocks and applies `compactor.retention` per resolution (`raw`, `fiveMinutes`, `oneHour`; `"0d"` keeps forever). Run **exactly one Compactor per bucket, across all installs and regions** — a second Compactor on the same bucket corrupts the block layout and requires manual repair. Multi-region users enable `compactor` in one install only. Long-term reads need both halves on the same bucket: a Prometheus sidecar uploading blocks to it, and `storeGateway.enabled: true` here to serve them. ### Object Storage Set `storage.type` to `aws`, `gcp`, or `minio`, and configure that block. AWS and GCP use a Control Plane cloud identity — no credentials are stored. See [Prerequisites](#prerequisites) for the per-backend setup steps. | Field | Backends | Description | | ---------------------------- | -------------- | --------------------------------------------------- | | `bucket` | all | Target bucket. Must already exist. | | `region` | `aws`, `minio` | Bucket region. | | `cloudAccountName` | `aws`, `gcp` | Control Plane cloud account granting bucket access. | | `policyName` | `aws` | Bare name of the custom bucket-scoped IAM policy. | | `endpoint` | `minio` | S3 API address as `host:port` — no scheme. | | `insecure` | `minio` | `true` when the endpoint is plain HTTP. | | `accessKey` / `accessSecret` | `minio` | Static credentials with access to the bucket. | ## Connecting | What | Value | | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | | Query UI / PromQL API (in-GVC) | `http://RELEASE_NAME-thanos.GVC_NAME.cpln.local:10902` | | Grafana Prometheus datasource | The same URL — Query speaks the Prometheus HTTP API | | Query UI / API (public, when `publicAccess.enabled`) | The `*.cpln.app` canonical endpoint (`cpln workload get RELEASE_NAME-thanos -o yaml` → `status.canonicalEndpoint`) | | Query's own Store API (for a higher Thanos tier) | `RELEASE_NAME-thanos.GVC_NAME.cpln.local:10901` (gRPC) | Point your Grafana at Query instead of individual Prometheus servers to get one deduplicated global view. The Query UI's **Stores** page shows every configured endpoint with its health and labels — the first place to look when a source is missing from results. ## Important Notes * **Query has no built-in authentication** — with `publicAccess.enabled: true`, anyone with the URL can run queries. Keep it off, or front Query with your own authenticating proxy. * **Run exactly one Compactor per bucket, across all installs and regions** — a second one corrupts the block layout and requires manual repair. * **Cross-GVC stores need a firewall change on the Prometheus side** — the sidecar workload must allow inbound `same-org` or list this Query workload (see [Store Endpoints](#store-endpoints)). * **No deduplication happening?** `queryReplicaLabels` must exactly match the external label name your HA Prometheus pair sets (`replica` by default). * **Long-term reads need both halves on the same bucket** — a sidecar uploading blocks to it, and `storeGateway.enabled: true` here to serve them. * **Disabling the storage tier does not remove existing cloud grants from the identity** — the platform deep-merges updates, so a previously-applied AWS/GCP binding stays until you remove it (edit the identity, or uninstall and reinstall the release). ## External References Official Thanos documentation Global query layer, fan-out, and deduplication reference Serving historical bucket blocks to the query layer Compaction, downsampling, and retention reference Source code and releases View the source files, default values, and chart definition # TiDB Source: https://docs.controlplane.com/template-catalog/templates/tidb Deploy TiDB on Control Plane using the Template Catalog. Covers configuration, volumes, scaling, and distributed MySQL-compatible clustering with TiKV storage and PD coordination. ## Overview TiDB is a distributed, MySQL-compatible database designed for horizontal scalability and high availability. It separates compute from storage across three components: a SQL processing layer (TiDB Server), a distributed key-value store (TiKV), and a placement driver (PD) that manages cluster metadata and scheduling. This template deploys a production-ready TiDB cluster across multiple Control Plane locations using PingCAP's official images. ### What Gets Created * **GVC** — A new GVC spanning the configured locations. * **Stateful PD Workload** — (`RELEASE_NAME-pd`): placement driver cluster distributed across locations according to `pdReplicas`. Uses `replicaDirect` addressing so each PD node is individually reachable. * **Stateful TiKV Workload** — (`RELEASE_NAME-tikv`): distributed storage nodes. Replica count per location is controlled by `gvc.locations[].replicas`. * **Standard TiDB Server Workload** — (`RELEASE_NAME-server`): MySQL-compatible SQL layer on port `4000`. Per-location replica count follows `gvc.locations[].replicas`. * **DB Init Workload** *(optional)* — (`RELEASE_NAME-tidb-db-init`): a one-time initialization job that sets the root password and creates the application database and user. Disable after first deployment. * **Backup Cron Workload** *(optional)* — A scheduled backup job that uses TiDB's `br` tool to write a full cluster snapshot to AWS S3 or GCS. * **Volume Set** — PD storage (`RELEASE_NAME-tidb-pd-vs`): 10 GiB fixed, ext4, general-purpose-ssd, with 7-day snapshot retention. * **Volume Set** — TiKV storage (`RELEASE_NAME-tidb-tikv-vs`): configurable capacity with optional autoscaling, ext4, general-purpose-ssd, with 7-day snapshot retention. * **Secrets** — Opaque secrets containing startup scripts for PD, TiKV, and TiDB Server, plus an optional dictionary secret with database credentials. * **Identity & Policy** — A shared identity bound to all workloads with `reveal` access to all secrets, and cloud storage access when backup is enabled. ## Installation This template has no external prerequisites unless backup is enabled. To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} devMode: false # WARNING: For development/testing only. Bypasses the 3-location HA requirement. Do NOT enable in production. gvc: name: tidb-gvc locations: # Replica count applies to TiKV and TiDB Server workloads; PD uses pdReplicas - name: aws-us-east-2 replicas: 1 - name: aws-us-west-2 replicas: 1 - name: aws-us-east-1 replicas: 1 pdReplicas: 3 # options: 3, 5, 7 images: server: pingcap/tidb:v8.5.3 tikv: pingcap/tikv:v8.5.3 pd: pingcap/pd:v8.5.3 resources: pd: cpu: 2 memory: 4Gi server: cpu: 2 memory: 2Gi tikv: cpu: 2 memory: 4Gi autoCreateDatabase: # Enable to automatically create the database on initialization enabled: true deployInitWorkload: true # Set to false after the DB has been initialized to remove the init workload and save resources database: # Set database values rootPassword: myrootpw user: myuser password: mypw db: mydb volumeset: tikv: capacity: 10 # initial capacity in GiB (minimum is 10) autoscaling: enabled: false # Set to true to enable autoscaling maxCapacity: 100 # Maximum capacity in GiB minFreePercentage: 10 # Minimum free percentage before scaling triggers scalingFactor: 1.2 # Multiplier applied to current capacity when scaling pd: capacity: 10 # initial capacity in GiB (minimum is 10) exposeServer: true # Set to true to expose TiDB server publicly external_access: # Set if client is outside the GVC or in another location server_outboundAllowCIDR: [] tikv_outboundAllowCIDR: [] # Note: when backup.enabled is true, the template automatically allows outbound access (0.0.0.0/0) regardless of this value. pd_outboundAllowCIDR: [] internal_access: server: type: same-gvc # options: same-gvc, same-org, workload-list workloads: # Note: can only be used if type is same-gvc or workload-list #- //gvc/GVC_NAME/workload/WORKLOAD_NAME tikv: type: same-gvc # options: same-gvc, same-org, workload-list workloads: # Note: can only be used if type is same-gvc or workload-list #- //gvc/GVC_NAME/workload/WORKLOAD_NAME pd: type: same-gvc # options: same-gvc, same-org, workload-list workloads: # Note: can only be used if type is same-gvc or workload-list #- //gvc/GVC_NAME/workload/WORKLOAD_NAME backup: enabled: false image: controlplanecorporation/tidb-backup:v8.5.3 schedule: "0 2 * * *" # daily at 2am UTC activeDeadlineSeconds: 14400 # 4 hours max per backup job location: aws-us-east-1 # Run backup in the location closest to your storage bucket/region resources: cpu: 1 memory: 1Gi provider: aws # Options: aws or gcp aws: bucket: my-backup-bucket region: us-east-1 cloudAccountName: my-backup-cloudaccount policyName: my-backup-policy prefix: tidb/backups gcp: bucket: my-backup-bucket cloudAccountName: my-backup-cloudaccount prefix: tidb/backups ``` ### Locations * `gvc.name` — Name of the GVC to create. Must be unique within your organization if deploying multiple instances. * `gvc.locations` — List of Control Plane locations. **At least 3 locations are required** unless `devMode` is enabled. * `locations[].replicas` — Number of TiKV and TiDB Server replicas per location. Set to `0` to suspend a component in that location without removing it from the configuration. * `gvc.pdReplicas` — Total number of PD replicas across all locations. Must be `3`, `5`, or `7`. When set to `3`, exactly 3 locations are required. Replicas are distributed evenly across locations. ### Development Mode Set `devMode: true` to bypass the 3-location requirement and deploy with 1 or 2 locations for development and testing purposes. `devMode` provides no fault tolerance. If the location(s) become unavailable, the cluster will halt. Never enable this in production. Even in dev mode, PD still requires 3 replicas (`pdReplicas: 3`) and TiKV still needs at least 3 total instances across all locations. Configure `replicas` per location accordingly: **1 location — all 3 TiKV instances in a single location:** ```yaml theme={null} devMode: true gvc: locations: - name: aws-us-east-2 replicas: 3 pdReplicas: 3 ``` **2 locations — at least 3 total TiKV instances across both:** ```yaml theme={null} devMode: true gvc: locations: - name: aws-us-east-2 replicas: 2 - name: aws-us-east-1 replicas: 1 pdReplicas: 3 ``` ### Database Initialization * `autoCreateDatabase.enabled` — Creates a dictionary secret with database credentials used by the TiDB Server and init workload. * `autoCreateDatabase.deployInitWorkload` — Deploys a one-time init job that sets the root password and creates the application database and user. The job checks whether the database already exists before running — if it does, it exits immediately. After the cluster is initialized, set `autoCreateDatabase.deployInitWorkload` to `false` and upgrade the template to remove the init workload and free up resources. ### Credentials * `autoCreateDatabase.database.rootPassword` — MySQL root password. **Change before deploying to production.** * `autoCreateDatabase.database.user` — Application database username. * `autoCreateDatabase.database.password` — Application database password. * `autoCreateDatabase.database.db` — Name of the application database to create. These values are only applied on first initialization. If the database already exists, the init workload exits without making changes. To modify credentials on an existing cluster, use MySQL's native commands (e.g. `ALTER USER`, `ALTER DATABASE`). ### Resources * `resources.pd.cpu` / `resources.pd.memory` — CPU and memory per PD replica. * `resources.server.cpu` / `resources.server.memory` — CPU and memory per TiDB Server replica. * `resources.tikv.cpu` / `resources.tikv.memory` — CPU and memory per TiKV replica. ### Storage **TiKV storage** (configurable): * `volumeset.tikv.capacity` — Initial volume size in GiB (minimum 10). * `volumeset.tikv.autoscaling.enabled` — Automatically expand volumes as they fill. When enabled: * `maxCapacity` — Maximum volume size in GiB. * `minFreePercentage` — Trigger a scale-up when free space drops below this percentage. * `scalingFactor` — Multiply current capacity by this factor when scaling up. **PD storage** (fixed): * `volumeset.pd.capacity` — Initial volume size in GiB for PD metadata (minimum 10). Both volume sets retain snapshots for 7 days and create a final snapshot on deletion. ### Access **Internal access** — configured per component (`server`, `tikv`, `pd`): | Type | Description | | --------------- | ------------------------------------------------------------- | | `same-gvc` | Allow access from all workloads in the same GVC (recommended) | | `same-org` | Allow access from all workloads in the same organization | | `workload-list` | Allow access only from specific workloads | **External access:** * `exposeServer` — Set to `true` to allow external connections to the TiDB MySQL port (`4000`) from any IP (`0.0.0.0/0`). * `external_access.server_outboundAllowCIDR` / `tikv_outboundAllowCIDR` / `pd_outboundAllowCIDR` — Outbound CIDR allowlists for each component, for reaching external services. When `backup.enabled` is `true`, TiKV outbound access is automatically set to `0.0.0.0/0` so nodes can upload directly to cloud storage, regardless of `tikv_outboundAllowCIDR`. ### Connecting to TiDB TiDB Server is MySQL-compatible. Connect using any MySQL client from within the same GVC: ```text theme={null} RELEASE_NAME-server.GVC_NAME.cpln.local:4000 ``` Use the `user` / `password` credentials from `autoCreateDatabase.database`, or connect as `root` with `rootPassword`. ### Ports | Workload | Port | Protocol | Description | | ----------- | ------- | -------- | ------------------------- | | TiDB Server | `4000` | TCP | MySQL-compatible SQL port | | TiDB Server | `10080` | HTTP | TiDB status and metrics | | PD | `2379` | TCP | PD client port | | PD | `2380` | TCP | PD peer (Raft) port | | TiKV | `20160` | TCP | TiKV data port | | TiKV | `20180` | TCP | TiKV status port | ## Backup Backup is disabled by default. When enabled, a cron workload uses TiDB's `br` tool to take a full cluster snapshot on the configured schedule and upload it to AWS S3 or GCS. * `backup.enabled` — Enable scheduled backups. * `backup.schedule` — Cron expression for backup frequency (default: daily at 2am UTC). * `backup.provider` — `aws` or `gcp`. * `backup.location` — The Control Plane location where the backup job runs. Set to the location **closest to your storage bucket** to minimize cross-region transfer latency and costs. * `backup.activeDeadlineSeconds` — Maximum time allowed per backup job in seconds (default: `14400` / 4 hours). * `backup.resources.cpu` / `backup.resources.memory` — Resources for the backup cron container. When `backup.enabled` is `true`, the template automatically grants TiKV outbound access to `0.0.0.0/0` so nodes can upload data directly to cloud storage. This overrides `external_access.tikv_outboundAllowCIDR`. ### AWS S3 Before enabling backup with `provider: aws`, complete the following in your AWS account: 1. Create an S3 bucket. Set `backup.aws.bucket` to its name and `backup.aws.region` to its region. 2. If you do not have a Cloud Account set up, refer to the docs to [Create a Cloud Account](/guides/create-cloud-account). Set `backup.aws.cloudAccountName` to its name. 3. Create an IAM policy with the following JSON, replacing `YOUR_BUCKET_NAME`: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket", "s3:GetObjectVersion", "s3:DeleteObjectVersion" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET_NAME", "arn:aws:s3:::YOUR_BUCKET_NAME/*" ] } ] } ``` 4. Set `backup.aws.policyName` to the name of the policy created in step 3. 5. Set `backup.aws.prefix` to the folder path where backups will be stored. ### GCS Before enabling backup with `provider: gcp`, complete the following in your GCP account: 1. Create a GCS bucket. Set `backup.gcp.bucket` to its name. 2. If you do not have a Cloud Account set up, refer to the docs to [Create a Cloud Account](/guides/create-cloud-account). Set `backup.gcp.cloudAccountName` to its name. 3. Add the **Storage Admin** role to the GCP service account associated with the Cloud Account. 4. Set `backup.gcp.prefix` to the folder path where backups will be stored. ## Restoring a Backup Backups are stored at `BUCKET/PREFIX/tidb-TIMESTAMP/`. To restore, run `br restore full` from a machine with network access to the PD endpoint. **AWS S3:** ```sh theme={null} br restore full \ --pd="RELEASE_NAME-pd.GVC_NAME.cpln.local:2379" \ --storage="s3://BUCKET_NAME/PREFIX/tidb-TIMESTAMP" \ --s3.region="BUCKET_REGION" ``` **GCS:** ```sh theme={null} br restore full \ --pd="RELEASE_NAME-pd.GVC_NAME.cpln.local:2379" \ --storage="gcs://BUCKET_NAME/PREFIX/tidb-TIMESTAMP" ``` The `br` binary version must match your TiDB cluster version. Download it from the [TiDB Community Toolkit](https://docs.pingcap.com/tidb/stable/download-ecosystem-tools). ## External References Official TiDB documentation Download `br` and other TiDB ecosystem tools Source code for the TiDB backup container image View the source files, default values, and chart definition # TimescaleDB Source: https://docs.controlplane.com/template-catalog/templates/timescaledb Deploy TimescaleDB — the PostgreSQL 18 time-series database — on Control Plane. Covers hypertables, compression, continuous aggregates, retention, PgBouncer pooling, and scheduled S3, GCS, or MinIO backups. ## Overview TimescaleDB is a time-series database built as a PostgreSQL extension. This template deploys a single-instance PostgreSQL 18 server with the TimescaleDB Community extension preloaded and auto-created, giving you hypertables, columnar compression, continuous aggregates, and retention policies alongside regular relational tables — all through any PostgreSQL client or ORM. Optional PgBouncer connection pooling and scheduled backups to AWS S3, GCS, or a self-hosted MinIO instance are included. TimescaleDB is licensed under the Timescale License (TSL). It is free to self-host, including all Community features (compression, continuous aggregates, retention); the license only forbids reselling TimescaleDB itself as a managed database service. TimescaleDB on Control Plane operates as a single-instance deployment, pinned to one replica. Do not scale up the replica count — PostgreSQL is a single-writer database, and additional replicas would run as isolated instances rather than a cluster. ### What Gets Created * **Stateful TimescaleDB Workload** — A single-replica PostgreSQL 18 + TimescaleDB 2.28.3 container on port `5432`. The extension is preloaded, created automatically in your database, and auto-tuned to the container's resources at first boot. * **Volume Set** — Persistent storage for the database data directory, with optional autoscaling and 7-day snapshots. * **Secret** — A dictionary secret storing the database username and password, injected into the container at startup. * **Identity & Policy** — An identity bound to the workload with `reveal` access to the credentials secret, and scoped cloud storage access when backup is enabled. * **PgBouncer Workload** *(optional)* — A PgBouncer connection pooler deployed as a separate workload in front of TimescaleDB. * **Backup Cron Workload** *(optional)* — A scheduled `pg_dumpall` backup job that writes compressed SQL dumps to AWS S3, GCS, or MinIO. This template does not create a GVC. You must deploy it into an existing GVC. ## Installation This template has no external prerequisites unless backup is enabled. To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} image: timescale/timescaledb:2.28.3-pg18 # PostgreSQL 18 + TimescaleDB Community edition resources: minCpu: 200m minMemory: 512Mi maxCpu: 500m maxMemory: 1024Mi # timescaledb-tune sizes shared_buffers/workers from this at first boot config: username: username password: password database: test # TimescaleDB extension is created automatically in this database volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) autoscaling: enabled: false # Set to true to enable autoscaling maxCapacity: 100 # Maximum capacity in GiB when autoscaling is enabled minFreePercentage: 10 # Minimum free percentage to trigger scaling when autoscaling is enabled scalingFactor: 1.2 # Scaling factor to determine how much to scale up when autoscaling is triggered internalAccess: # Sets the internal firewall scope - if set to none, replicas will not be able to reach each other type: same-gvc # options: none, same-gvc, same-org, workload-list workloads: # Note: can only be used if type is same-gvc or workload-list #- //gvc/GVC_NAME/workload/WORKLOAD_NAME publicAccess: enabled: false # exposes 5432 via a TCP load balancer; connections are unencrypted — prefer internal access pgbouncer: enabled: false image: edoburu/pgbouncer:v1.25.1-p0 poolMode: transaction # options: session, transaction, statement defaultPoolSize: 25 # number of real Postgres connections PgBouncer maintains maxClientConn: 1000 # maximum number of client connections PgBouncer accepts replicas: 1 resources: cpu: 200m memory: 128Mi backup: enabled: false image: ghcr.io/controlplane-com/backup-images/postgres-backup:18.1.0 # PG18 client, matches server major schedule: "0 2 * * *" # daily at 2am UTC resources: cpu: 100m memory: 128Mi provider: aws # Options: aws, gcp, or minio aws: bucket: my-backup-bucket region: us-east-1 cloudAccountName: my-backup-cloudaccount policyName: my-backup-policy prefix: timescaledb/backups # folder name where your backups will be stored gcp: bucket: my-backup-bucket cloudAccountName: my-backup-cloudaccount prefix: timescaledb/backups # folder name where your backups will be stored minio: # Backup to a self-hosted MinIO workload (or any S3-compatible endpoint) endpoint: http://my-minio-workload:9000 # e.g. http://WORKLOAD_NAME:9000 for an internal MinIO template deployment bucket: my-backup-bucket accessKey: my-minio-username # matches the MinIO template's admin.username secretKey: my-minio-password # matches the MinIO template's admin.password prefix: timescaledb/backups # folder name where your backups will be stored ``` ### Image and Resources * `image` — The TimescaleDB image tag. Keep it in the default (Community) series; `-oss` tags remove compression, continuous aggregates, and retention. * `resources.minCpu` / `resources.minMemory` — Minimum CPU and memory guaranteed to the workload. * `resources.maxCpu` / `resources.maxMemory` — Maximum CPU and memory the workload can use. `timescaledb-tune` sizes `shared_buffers` and worker settings from `maxMemory` at first boot (for example, `shared_buffers` becomes \~25% of the memory limit). Auto-tuning is captured only at first boot, when the data volume is empty. Raising `resources.maxMemory` on an existing deployment does not retune PostgreSQL — adjust settings manually with `ALTER SYSTEM`, or uninstall (which deletes the volume set) and reinstall. ### Credentials * `config.username` — Database username. **Change before deploying to production.** * `config.password` — Database password. **Change before deploying to production.** * `config.database` — Name of the database created on startup. The TimescaleDB extension is created automatically inside it. These values are only applied on first startup when the data directory is empty. Updating them after the initial deployment has no effect on the running database. To change credentials or the database name on an existing instance, use PostgreSQL's native commands (e.g. `ALTER USER`, `ALTER DATABASE`). ### Storage * `volumeset.capacity` — Initial volume size in GiB (minimum 10). * `volumeset.autoscaling.enabled` — Automatically expand the volume as it fills. When enabled: * `maxCapacity` — Maximum volume size in GiB. * `minFreePercentage` — Trigger a scale-up when free space drops below this percentage. * `scalingFactor` — Multiply the current capacity by this factor when scaling up. ### Internal Access * `internalAccess.type` — Controls which workloads can connect to TimescaleDB on port `5432`: | Type | Description | | --------------- | --------------------------------------------------------------- | | `none` | No internal access allowed | | `same-gvc` | Allow access from all workloads in the same GVC | | `same-org` | Allow access from all workloads in the same organization | | `workload-list` | Allow access only from specific workloads listed in `workloads` | * `internalAccess.workloads` — When `type` is `workload-list`, the list of workload links (e.g. `//gvc/GVC_NAME/workload/WORKLOAD_NAME`) allowed to connect. ### Public Access * `publicAccess.enabled` — When `true`, exposes port `5432` through a TCP load balancer and assigns a public `*.cpln.app` canonical endpoint. Public access is unencrypted — the image ships no TLS certificates, so connections over the public endpoint are plaintext. Keep `publicAccess.enabled: false` and use internal access unless you accept plaintext connections. ### PgBouncer Connection Pooling PgBouncer is an optional connection pooler that sits in front of TimescaleDB and multiplexes application connections into a smaller pool of real database connections. This reduces connection overhead and protects the database from exhaustion under high concurrency. When enabled, PgBouncer is deployed as a separate workload and becomes the primary connection endpoint for your applications: ```text theme={null} RELEASE_NAME-pgbouncer.GVC_NAME.cpln.local:5432 ``` * `pgbouncer.enabled` — Enable or disable PgBouncer. * `pgbouncer.poolMode` — Controls how connections are reused: | Mode | Description | | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `transaction` | Connection held only for the duration of a transaction, then returned to the pool. Best for most web and API workloads. Not compatible with session-level features (`SET` variables, temporary tables, advisory locks). | | `session` | Connection held for the entire client session. Compatible with all PostgreSQL features but provides less reuse. | | `statement` | Connection returned after every statement. Transactions are not supported. Rarely used. | * `pgbouncer.defaultPoolSize` — Number of real database connections PgBouncer maintains per pool (default: `25`). * `pgbouncer.maxClientConn` — Maximum number of client connections PgBouncer accepts (default: `1000`). * `pgbouncer.replicas` — Number of PgBouncer instances. PgBouncer is stateless and can be scaled horizontally for high-throughput workloads. * `pgbouncer.resources.cpu` / `pgbouncer.resources.memory` — Resources allocated to each PgBouncer replica. PgBouncer shares the same credentials and identity as the TimescaleDB workload — no additional secrets or IAM configuration is required. The `userlist.txt` and `pgbouncer.ini` are generated automatically from your `config.username`, `config.password`, and `config.database` values at startup. ### Backup Backup is disabled by default. When enabled, a cron workload runs `pg_dumpall` on the configured schedule and uploads a compressed SQL dump to AWS S3, GCS, or a MinIO-compatible endpoint. * `backup.enabled` — Enable scheduled backups. * `backup.image` — The backup container image. Match its tag to the server major version — `18.1.0` for the default PostgreSQL 18 image, `17.1.0` for a PostgreSQL 17 image. * `backup.schedule` — Cron expression for backup frequency (default: daily at 2am UTC). * `backup.provider` — `aws`, `gcp`, or `minio`. * `backup.resources.cpu` / `backup.resources.memory` — Resources for the backup cron container. Complete the [backup prerequisites](#backup-prerequisites) for your provider before enabling backup. ## Connecting | What | Value | | ------------------------------ | ---------------------------------------------------------------------------------------------------- | | Internal (same GVC) | `RELEASE_NAME-timescaledb.GVC_NAME.cpln.local:5432` | | Via PgBouncer *(when enabled)* | `RELEASE_NAME-pgbouncer.GVC_NAME.cpln.local:5432` — use this as your application endpoint | | Public *(when enabled)* | The `status.canonicalEndpoint` of the `RELEASE_NAME-timescaledb` workload, port `5432` (unencrypted) | | Credentials | `config.username` / `config.password` | ## Using TimescaleDB Any PostgreSQL client or ORM works unchanged. Turn a regular table into a hypertable (automatically partitioned by time) and query it with time buckets: ```sql theme={null} CREATE TABLE metrics (time timestamptz NOT NULL, device text, value double precision); SELECT create_hypertable('metrics', by_range('time')); INSERT INTO metrics VALUES (now(), 'sensor-1', 23.5); SELECT time_bucket('1 hour', time) AS bucket, device, avg(value) FROM metrics GROUP BY bucket, device ORDER BY bucket; ``` From here you can add columnar compression, continuous aggregates, and retention policies — all Community features included in the default image. ## Backup Prerequisites ### AWS S3 Before enabling backup with `provider: aws`, complete the following in your AWS account: Create an S3 bucket. Set `backup.aws.bucket` to its name and `backup.aws.region` to its region. If you do not have one, [create a Cloud Account](/guides/create-cloud-account) for your AWS account. Set `backup.aws.cloudAccountName` to its name. Create an IAM policy with the JSON below, replacing `YOUR_BUCKET_NAME`, then set `backup.aws.policyName` to the policy's name and `backup.aws.prefix` to the folder path for backups. ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:ListBucket", "s3:GetBucketLocation", "s3:GetObject", "s3:GetObjectVersion", "s3:PutObject", "s3:DeleteObject", "s3:DeleteObjectVersion", "s3:AbortMultipartUpload" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET_NAME", "arn:aws:s3:::YOUR_BUCKET_NAME/*" ] } ] } ``` ### GCS Before enabling backup with `provider: gcp`, complete the following in your GCP account: Create a GCS bucket. Set `backup.gcp.bucket` to its name. If you do not have one, [create a Cloud Account](/guides/create-cloud-account) for your GCP project. Set `backup.gcp.cloudAccountName` to its name — access is keyless, with no stored credentials. Grant the **Storage Admin** role (`roles/storage.objectAdmin` scoped to the bucket also works) to the GCP service account created for the Cloud Account. Set `backup.gcp.prefix` to the folder path for backups. ### MinIO Before enabling backup with `provider: minio`, ensure your MinIO instance (or any S3-compatible endpoint) is accessible: Create a bucket on the server. Set `backup.minio.bucket` to its name. Set `backup.minio.endpoint` to the S3 API address including the port. For the `minio` marketplace template deployed in the same GVC, use `http://WORKLOAD_NAME:9000`. Set `backup.minio.accessKey` and `backup.minio.secretKey` to credentials with access to the bucket. For the MinIO template, these match the `admin.username` and `admin.password` values. Set `backup.minio.prefix` to the folder path for backups. MinIO backup requires no Control Plane Cloud Account — credentials are passed directly to the backup job. ## Restoring a Backup Restoring a TimescaleDB dump is **not** the vanilla PostgreSQL procedure. The target server must run the **same TimescaleDB extension version** as the dump, and the replay must be wrapped in `timescaledb_pre_restore()` and `timescaledb_post_restore()`. Run the following from a client with access to the backup bucket and to a fresh database: Restore replays a full-cluster dump. Run it against a fresh instance, not a database that already holds data you want to keep. **AWS S3:** ```sh theme={null} export PGPASSWORD="PASSWORD" psql -h RELEASE_NAME-timescaledb.GVC_NAME.cpln.local -U USERNAME -d DATABASE \ -c "SELECT timescaledb_pre_restore();" aws s3 cp "s3://BUCKET_NAME/PREFIX/BACKUP_FILE.sql.gz" - \ | gunzip \ | psql -h RELEASE_NAME-timescaledb.GVC_NAME.cpln.local -p 5432 -U USERNAME -d postgres psql -h RELEASE_NAME-timescaledb.GVC_NAME.cpln.local -U USERNAME -d DATABASE \ -c "SELECT timescaledb_post_restore();" unset PGPASSWORD ``` For **GCS**, replace the download with `gsutil cp "gs://BUCKET_NAME/PREFIX/BACKUP_FILE.sql.gz" -`. For **MinIO**, add `--endpoint-url "http://MINIO_ENDPOINT:9000"` to the `aws s3 cp` command and export the MinIO access and secret keys as `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`. ## Important Notes * **Change `config.password` before installing.** Credentials are written into the data directory at first boot; changing the value later does not change the database password. * **Do not scale this workload.** It is single-writer PostgreSQL, pinned to one replica. Additional replicas would run as isolated instances, not a cluster. * **Auto-tuning is captured at first boot only.** `timescaledb-tune` sizes memory settings from the container limit when the volume is empty; raising `resources.maxMemory` later does not retune. * **Public access is unencrypted.** The image ships no TLS certificates — keep `publicAccess.enabled: false` unless you accept plaintext connections. * **Keep the image tag in the default (Community) series.** `-oss` tags remove compression, continuous aggregates, and retention policies. If you pin a different `pgXX` server image, match `backup.image` to the same major (`17.1.0` for PostgreSQL 17). * **Uninstall deletes the volume set.** A final snapshot is kept for 7 days; enable backups if the data matters long-term. ## External References Official TimescaleDB documentation Hypertables, compression, continuous aggregates, and retention PgBouncer configuration reference View the source files, default values, and chart definition # TimescaleDB Highly Available Source: https://docs.controlplane.com/template-catalog/templates/timescaledb-highly-available Deploy a highly available TimescaleDB cluster on Control Plane. A Patroni-managed PostgreSQL 18 + TimescaleDB time-series cluster with etcd consensus, HAProxy leader routing, automatic failover, and optional PgBouncer pooling and scheduled backups. ## Overview TimescaleDB Highly Available deploys the TimescaleDB time-series database (a PostgreSQL 18 extension) as a Patroni-managed cluster with automatic failover. Three replicas — one leader plus two hot standbys — form the cluster over an etcd consensus store, and an HAProxy leader endpoint routes all writes to the current primary. When the leader fails, Patroni promotes a standby and HAProxy re-selects the new primary automatically, giving applications a single stable endpoint. Optional PgBouncer connection pooling and scheduled logical backups to AWS S3, GCS, or MinIO are included. TimescaleDB is licensed under the Timescale License (TSL) plus the PostgreSQL License. It is free to self-host at any scale, including all Community features (compression, continuous aggregates, retention); the license only forbids reselling TimescaleDB itself as a managed database service. For high availability, keep at least 3 TimescaleDB replicas and 3 etcd replicas. etcd requires an odd number of replicas (3, 5, 7) for quorum. `replicas: 1` renders a single-member cluster with no failover. ### What Gets Created * **Stateful TimescaleDB Workload** — A Patroni-managed PostgreSQL 18 + TimescaleDB 2.28.3 cluster on port `5432` (`replicas: 3` by default: 1 leader + 2 hot standbys). Each replica has per-replica DNS and its own volume; the extension is preloaded and created automatically in your database. Leadership is handed off before any replica is shut down, keeping writes available during deployments and restarts. * **etcd Workload** — A dedicated etcd cluster providing the distributed consensus store (DCS) for Patroni leader election, from the `etcd` subchart dependency. * **HAProxy Leader-Routing Workload** *(enabled by default)* — Routes write traffic to the current primary via Patroni's REST health check, providing a stable connection endpoint across failover. * **PgBouncer Workload** *(optional)* — A connection pooler deployed in front of HAProxy, multiplexing application connections into a smaller pool of real database connections. * **Backup Cron Workload** *(optional)* — A scheduled `pg_dumpall` logical backup that dumps the leader through HAProxy and writes a compressed SQL dump to AWS S3, GCS, or MinIO. * **Volume Sets** — Per-replica persistent storage for the TimescaleDB and etcd data directories, with optional autoscaling. * **Secrets** — A dictionary secret with database credentials, plus opaque secrets for the Patroni and HAProxy start scripts. * **Identity & Policy** — An identity bound to the workload with least-privilege `reveal` access to exactly those secrets, and scoped cloud storage access when backup is enabled. This template does not create a GVC. You must deploy it into an existing GVC. ## Installation This template has no external prerequisites unless backup is enabled. To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} replicas: 3 # 1 leader + 2 hot standbys; Patroni forms the cluster and handles failover resources: minCpu: 500m minMemory: 1Gi # time-series ingest is memory-sensitive maxCpu: 1 maxMemory: 2Gi image: timescale/timescaledb-ha:pg18.4-ts2.28.3 # PostgreSQL 18 + TimescaleDB 2.28.3 Community, Patroni bundled postgres: username: username password: password # change before installing database: test # TimescaleDB extension is created automatically in this database multiZone: false # spread replicas across zones in the location volumeset: capacity: 10 # initial capacity in GiB per replica (minimum 10) autoscaling: enabled: false # set to true to grow each volume automatically maxCapacity: 100 # maximum capacity in GiB when autoscaling is enabled minFreePercentage: 10 # free-space threshold that triggers scaling scalingFactor: 1.2 # how much to scale up by internal_access: type: same-gvc # options: same-gvc, same-org, workload-list workloads: # only when type is same-gvc or workload-list #- //gvc/GVC_NAME/workload/WORKLOAD_NAME etcd: replicas: 3 # use an odd number (3, 5, 7) for quorum resources: cpu: 500m memory: 512Mi multiZone: false tuning: # Passed through to the bundled etcd. Without compaction its backend grows # with time alone — Patroni renews its lease every ~10s and every renewal is # a revision — until it hits the quota and etcd goes READ-ONLY. autoCompactionMode: periodic # periodic (retention is a duration) or revision (a revision count) autoCompactionRetention: 1h # periodic needs an explicit unit (1h, 30m, 24h); revision takes a count quotaBackendBytes: 0 # backend size limit in bytes; 0 = etcd's own default of 2 GiB volumeset: capacity: 10 internal_access: type: same-gvc pgbouncer: enabled: false image: edoburu/pgbouncer:v1.25.1-p0 poolMode: transaction # options: session, transaction, statement defaultPoolSize: 25 # real Postgres connections PgBouncer maintains per pod maxClientConn: 1000 # maximum client connections PgBouncer accepts per pod maxDbConnections: 100 # hard cap on total Postgres connections across all pods minReplicas: 2 maxReplicas: 4 resources: cpu: 200m memory: 128Mi proxy: # HAProxy leader endpoint; auto-enabled when pgbouncer is enabled, required for logical backups enabled: true image: haproxy:2.9 resources: cpu: 100m memory: 128Mi minReplicas: 2 maxReplicas: 2 backup: enabled: false image: ghcr.io/controlplane-com/backup-images/postgres-backup:18.1.0 # PG18 client, matches server major schedule: "0 2 * * *" # daily at 2am UTC resources: cpu: 100m memory: 128Mi provider: aws # options: aws, gcp, minio aws: bucket: my-backup-bucket region: us-east-1 cloudAccountName: my-backup-cloudaccount policyName: my-backup-policy prefix: timescaledb/backups # folder where backups are stored gcp: bucket: my-backup-bucket cloudAccountName: my-backup-cloudaccount prefix: timescaledb/backups minio: # self-hosted MinIO or any S3-compatible endpoint endpoint: http://my-minio-workload:9000 # e.g. http://WORKLOAD_NAME:9000 for an internal MinIO deployment bucket: my-backup-bucket accessKey: my-minio-username # matches the MinIO template's admin.username secretKey: my-minio-password # matches the MinIO template's admin.password prefix: timescaledb/backups ``` ### Credentials * `postgres.username` — Database superuser username. **Change before deploying to production.** * `postgres.password` — Database superuser password. **Change before deploying to production.** * `postgres.database` — Name of the database created on first startup. The TimescaleDB extension is created automatically inside it. These values are only applied on first startup when the data directory is empty. Updating them after the initial deployment has no effect on the running database. To change credentials or the database name on an existing cluster, use PostgreSQL's native commands (e.g. `ALTER USER`, `ALTER DATABASE`). ### Cluster and Resources * `replicas` — Number of TimescaleDB replicas (default `3`: 1 leader + 2 hot standbys). Use at least 3 for high availability. * `image` — The TimescaleDB HA image tag (PostgreSQL 18 + TimescaleDB 2.28.3 Community, with Patroni bundled). * `resources.minCpu` / `resources.minMemory` — Minimum CPU and memory guaranteed per replica. * `resources.maxCpu` / `resources.maxMemory` — Maximum CPU and memory per replica. * `multiZone` — Spread replicas across availability zones within the location. ### Storage * `volumeset.capacity` — Initial volume size in GiB (minimum 10). Each replica gets its own volume. * `volumeset.autoscaling.enabled` — Automatically expand the volume as it fills. When enabled: * `maxCapacity` — Maximum volume size in GiB. * `minFreePercentage` — Trigger a scale-up when free space drops below this percentage. * `scalingFactor` — Multiply the current capacity by this factor when scaling up. ### etcd Cluster * `etcd.replicas` — Number of etcd replicas. Must be an **odd number** (3, 5, 7) for quorum. * `etcd.resources.cpu` / `etcd.resources.memory` — CPU and memory per etcd replica. * `etcd.multiZone` — Spread etcd replicas across availability zones. * `etcd.volumeset.capacity` — Initial volume size for etcd data in GiB. * `etcd.internal_access.type` — Controls which workloads can reach the etcd cluster. etcd is a hard dependency. If the etcd members are unhealthy, Patroni loses its consensus store and the cluster goes read-only. If writes start failing, check the `RELEASE_NAME-etcd` workload first. #### etcd History Compaction `etcd.tuning.autoCompactionMode`, `etcd.tuning.autoCompactionRetention` and `etcd.tuning.quotaBackendBytes` control how much revision history the bundled etcd cluster keeps and how large its backend may grow. The defaults — `periodic`, `1h` and `0` (etcd's own 2 GiB limit) — are the right settings for a Patroni consensus store and should be left alone. The [etcd template](/template-catalog/templates/etcd#compaction-and-backend-growth) documents the mechanism, the accepted value formats, and the read-only commands for inspecting a cluster. **Template version `1.0.0` did not compact the bundled etcd cluster.** Patroni renews its leader lease about every 10 seconds and every renewal creates a revision, so etcd grows with time alone — roughly 19 MB per day on an idle cluster — and reaches its 2 GiB backend quota in about 110 days. etcd then goes read-only across the whole cluster, and the TimescaleDB replicas restart-loop with `exitCode: 0` and `reason: Completed`, which reads as healthy and is easily mistaken for a database fault. Upgrade to `1.0.1` or later to turn compaction on. Upgrading prevents further growth but cannot shrink a backend that has already grown, and a cluster that has already raised a `NOSPACE` alarm needs operator recovery rather than an upgrade — see [If the Backend Quota Is Already Full](/template-catalog/templates/etcd#if-the-backend-quota-is-already-full). ### HAProxy Leader Endpoint In a Patroni cluster, only the leader replica accepts writes — the standbys are read-only. HAProxy automatically routes writes to the current leader by health-checking each replica's Patroni REST endpoint, giving applications a stable connection endpoint even during failover. It is enabled by default. * `proxy.enabled` — Deploy the HAProxy leader-routing workload (default: `true`). * `proxy.resources.cpu` / `proxy.resources.memory` — CPU and memory per HAProxy replica. * `proxy.minReplicas` / `proxy.maxReplicas` — Replica count for the proxy workload. `proxy.enabled` must stay `true` for logical backups — the backup job dumps the leader through the proxy endpoint. ### Internal Access The cluster is internal-only; there is no public endpoint. Reach it from other workloads through the proxy (or PgBouncer). * `internal_access.type` — Controls which workloads can connect to the cluster on port `5432`: | Type | Description | | --------------- | --------------------------------------------------------------- | | `same-gvc` | Allow access from all workloads in the same GVC | | `same-org` | Allow access from all workloads in the same organization | | `workload-list` | Allow access only from specific workloads listed in `workloads` | * `internal_access.workloads` — When `type` is `workload-list`, the list of workload links (e.g. `//gvc/GVC_NAME/workload/WORKLOAD_NAME`) allowed to connect. ### PgBouncer Connection Pooling PgBouncer is an optional connection pooler that sits in front of HAProxy, multiplexing application connections into a smaller pool of real database connections. HAProxy handles leader routing and failover transparently — PgBouncer is unaware of which replica is the primary. Enabling PgBouncer **automatically enables HAProxy**, as it is required for leader-aware routing. When enabled, PgBouncer becomes the primary connection endpoint: ```text theme={null} RELEASE_NAME-pgbouncer.GVC_NAME.cpln.local:5432 ``` * `pgbouncer.poolMode` — Controls how connections are reused: | Mode | Description | | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `transaction` | Connection held only for the duration of a transaction, then returned to the pool. Best for most web and API workloads. Not compatible with session-level features (`SET` variables, temporary tables, advisory locks). | | `session` | Connection held for the entire client session. Compatible with all PostgreSQL features but provides less reuse. | | `statement` | Connection returned after every statement. Transactions are not supported. Rarely used. | * `pgbouncer.defaultPoolSize` — Number of real database connections PgBouncer maintains per pod (default: `25`). * `pgbouncer.maxClientConn` — Maximum number of client connections PgBouncer accepts per pod (default: `1000`). * `pgbouncer.maxDbConnections` — Hard cap on total PostgreSQL connections across **all** PgBouncer pods (default: `100`). Prevents connection blowout when PgBouncer scales out. * `pgbouncer.minReplicas` / `pgbouncer.maxReplicas` — PgBouncer autoscales between these bounds. * `pgbouncer.resources.cpu` / `pgbouncer.resources.memory` — Resources allocated to each PgBouncer pod. ### Backup Backup is disabled by default. When enabled, a cron workload runs `pg_dumpall` against the leader (through HAProxy) on the configured schedule and uploads a compressed SQL dump to AWS S3, GCS, or a MinIO-compatible endpoint. * `backup.enabled` — Enable scheduled logical backups. * `backup.image` — The backup container image. Match its tag to the server major version — `18.1.0` for the default PostgreSQL 18 image. * `backup.schedule` — Cron expression for backup frequency (default: daily at 2am UTC). * `backup.provider` — `aws`, `gcp`, or `minio`. * `backup.resources.cpu` / `backup.resources.memory` — Resources for the backup cron container. Complete the [backup prerequisites](#backup-prerequisites) for your provider before enabling backup. ## Connecting Always connect through the proxy (or PgBouncer) — never a raw replica address, since the leader moves on failover. | What | Value | | ------------------------------ | ----------------------------------------------------------------------------------------- | | Via HAProxy (default) | `RELEASE_NAME-timescaledb-ha-proxy.GVC_NAME.cpln.local:5432` | | Via PgBouncer *(when enabled)* | `RELEASE_NAME-pgbouncer.GVC_NAME.cpln.local:5432` — use this as your application endpoint | | Credentials | `postgres.username` / `postgres.password` | | Database | `postgres.database` (TimescaleDB extension created here) | ## Using TimescaleDB Any PostgreSQL client or ORM works unchanged. Turn a regular table into a hypertable (automatically partitioned by time) and query it with time buckets: ```sql theme={null} CREATE TABLE metrics (time timestamptz NOT NULL, device text, value double precision); SELECT create_hypertable('metrics', by_range('time')); INSERT INTO metrics VALUES (now(), 'sensor-1', 23.5); SELECT time_bucket('1 hour', time) AS bucket, device, avg(value) FROM metrics GROUP BY bucket, device ORDER BY bucket; ``` From here you can add columnar compression, continuous aggregates, and retention policies — all Community features included in the default image. ### Availability During Failover and Upgrades The cluster keeps writes available across leader changes, but applications should implement connection retry with exponential backoff — a few seconds of write interruption is expected at each leader restart: * **Automatic failover** — When the leader fails, Patroni promotes a standby (a manual/orchestrated promotion completes in roughly 5 seconds in testing, with no committed rows lost) and HAProxy re-selects the new primary automatically, with no client change. * **Rolling restarts and upgrades** — Each replica is restarted in turn; leadership is handed off before the leader shuts down. Expect a brief write gap (a few seconds) at each leader switchover during the roll. ## Backup Prerequisites ### AWS S3 Before enabling backup with `provider: aws`, complete the following in your AWS account: Create an S3 bucket. Set `backup.aws.bucket` to its name and `backup.aws.region` to its region. If you do not have one, [create a Cloud Account](/guides/create-cloud-account) for your AWS account. Set `backup.aws.cloudAccountName` to its name. Create an IAM policy with the JSON below, replacing `YOUR_BUCKET_NAME`, then set `backup.aws.policyName` to the policy's name and `backup.aws.prefix` to the folder path for backups. ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:ListBucket", "s3:GetBucketLocation", "s3:GetObject", "s3:GetObjectVersion", "s3:PutObject", "s3:DeleteObject", "s3:DeleteObjectVersion", "s3:AbortMultipartUpload" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET_NAME", "arn:aws:s3:::YOUR_BUCKET_NAME/*" ] } ] } ``` ### GCS Before enabling backup with `provider: gcp`, complete the following in your GCP account: Create a GCS bucket. Set `backup.gcp.bucket` to its name. If you do not have one, [create a Cloud Account](/guides/create-cloud-account) for your GCP project. Set `backup.gcp.cloudAccountName` to its name — access is keyless, with no stored credentials. Grant the **Storage Admin** role (`roles/storage.objectAdmin` scoped to the bucket also works) to the GCP service account created for the Cloud Account. Set `backup.gcp.prefix` to the folder path for backups. ### MinIO Before enabling backup with `provider: minio`, ensure your MinIO instance (or any S3-compatible endpoint) is accessible: Create a bucket on the server. Set `backup.minio.bucket` to its name. Set `backup.minio.endpoint` to the S3 API address including the port. For the `minio` marketplace template deployed in the same GVC, use `http://WORKLOAD_NAME:9000`. Set `backup.minio.accessKey` and `backup.minio.secretKey` to credentials with access to the bucket. For the MinIO template, these match the `admin.username` and `admin.password` values. Set `backup.minio.prefix` to the folder path for backups. MinIO backup requires no Control Plane Cloud Account — credentials are passed directly to the backup job. ## Restoring a Backup Restoring a TimescaleDB dump is **not** the vanilla PostgreSQL procedure. The target server must run the **same TimescaleDB extension version** as the dump, and the replay must be wrapped in `timescaledb_pre_restore()` and `timescaledb_post_restore()`. Connect through the proxy so the restore targets the current leader. Restore replays a full-cluster dump. Run it against a fresh cluster, not a database that already holds data you want to keep. **AWS S3:** ```sh theme={null} export PGPASSWORD="PASSWORD" psql -h RELEASE_NAME-timescaledb-ha-proxy.GVC_NAME.cpln.local -U USERNAME -d DATABASE \ -c "SELECT timescaledb_pre_restore();" aws s3 cp "s3://BUCKET_NAME/PREFIX/BACKUP_FILE.sql.gz" - \ | gunzip \ | psql -h RELEASE_NAME-timescaledb-ha-proxy.GVC_NAME.cpln.local -p 5432 -U USERNAME -d postgres psql -h RELEASE_NAME-timescaledb-ha-proxy.GVC_NAME.cpln.local -U USERNAME -d DATABASE \ -c "SELECT timescaledb_post_restore();" unset PGPASSWORD ``` For **GCS**, replace the download with `gsutil cp "gs://BUCKET_NAME/PREFIX/BACKUP_FILE.sql.gz" -`. For **MinIO**, add `--endpoint-url "http://MINIO_ENDPOINT:9000"` to the `aws s3 cp` command and export the MinIO access and secret keys as `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`. ## Important Notes * **Change `postgres.password` before installing.** Credentials are baked into the data directory at first boot; changing the value later does not change the database password. To reset, uninstall (which deletes the volumes) and reinstall. * **Always connect via the proxy, never a replica.** The leader moves on failover; a pinned replica address will break. PgBouncer and backups already route through the proxy. * **`replicas: 1` has no HA.** It renders a single-member Patroni cluster with no failover. Use at least 3, and an odd `etcd.replicas` (3, 5, 7) for quorum. * **etcd is a hard dependency.** If the etcd members are unhealthy, Patroni loses its consensus store and the cluster goes read-only — check `RELEASE_NAME-etcd` first if writes fail. * **`proxy.enabled` must stay `true` for backups.** The logical backup dumps the leader through the proxy endpoint. * **Backups are logical-only.** A scheduled `pg_dumpall`; continuous WAL archiving / point-in-time restore is a planned follow-up. * **Expect a brief write interruption per leader restart.** During failover or a rolling upgrade, applications should retry with exponential backoff. ## External References Official TimescaleDB documentation Hypertables, compression, continuous aggregates, and retention Patroni automatic failover documentation Official etcd documentation Using timescaledb\_pre\_restore and timescaledb\_post\_restore View the source files, default values, and chart definition # ToolJet Source: https://docs.controlplane.com/template-catalog/templates/tooljet Deploy ToolJet on Control Plane using the Template Catalog. Open-source low-code platform for internal tools — visual app builder, 50+ datasource connectors, and the built-in ToolJet Database, backed by PostgreSQL with optional Redis for multi-replica scaling. ## Overview ToolJet is an open-source, low-code platform for building internal tools: a visual app builder with 50+ datasource connectors and the built-in ToolJet Database. This template deploys the ToolJet server — UI and API on port `3000` — backed by a PostgreSQL instance that holds both the application database and the ToolJet Database, with optional Redis for running more than one replica. ### Architecture * **ToolJet server** — A standard HTTP workload serving the UI and REST API on port `3000`. It runs its database migrations automatically on boot and scales horizontally through `tooljet.replicas`. PostgREST (which serves the ToolJet Database API) and a single-instance fallback Redis are bundled inside the official image, so no extra workloads are deployed for them. * **PostgreSQL** — The [postgres](/template-catalog/templates/postgres) template as a subchart. A single instance holds two databases, both created automatically on first boot: `tooljet` (apps, users, and encrypted datasource credentials) and `tooljet_db` (the ToolJet Database, served through the in-image PostgREST). ToolJet's boot scripts also create a `sample_db` with sample data. * **Redis + Sentinel** *(optional)* — The [redis](/template-catalog/templates/redis) template as a subchart, pinned to one data node and one sentinel. It carries the shared job queue and multiplayer coordination between replicas, and is required whenever `tooljet.replicas` is 2 or more. ### What Gets Created * **Standard ToolJet Workload** — The ToolJet server on port `3000` (`{release}-tooljet`), with `tooljet.replicas` replicas and configurable CPU and memory. * **Stateful PostgreSQL Workload** — One PostgreSQL instance from the subchart, with its own volume set (10 GiB by default). * **Redis and Sentinel Workloads** *(optional)* — One Redis data node and one sentinel, created when `redis.enabled` is `true`. * **Secret** — A template-created dictionary secret (`{release}-tooljet-db`) holding the database username and password used to assemble ToolJet's `PG_*`, `TOOLJET_DB_*`, and `PGRST_DB_URI` settings. Your prerequisite key secret is *not* created by the template — see [Prerequisites](#prerequisites). * **Identity & Policy** — An identity bound to the ToolJet workload, and a policy granting it `reveal` on exactly the secrets it reads: the database secret, your prerequisite key secret, and — when configured — the SMTP and Redis password secrets. This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites ToolJet reads its root-of-trust keys from a [dictionary secret](/guides/create-secret/dictionary) that you create **before** installing. The keys are never passed through values. Create a dictionary secret containing the three keys ToolJet requires, generated the same way upstream's setup scripts generate them: ```bash theme={null} cpln secret create-dictionary --name my-tooljet-secrets \ --entry SECRET_KEY_BASE=$(openssl rand -hex 64) \ --entry LOCKBOX_MASTER_KEY=$(openssl rand -hex 32) \ --entry PGRST_JWT_SECRET=$(openssl rand -hex 32) ``` | Key | Purpose | | -------------------- | ---------------------------------------------------------------------------- | | `SECRET_KEY_BASE` | Signs sessions and cookies. | | `LOCKBOX_MASTER_KEY` | Encrypts every stored datasource credential. | | `PGRST_JWT_SECRET` | Signs the JWTs the in-image PostgREST accepts for ToolJet Database requests. | Set `secrets.name` to the secret's name (default `my-tooljet-secrets`). Secrets are org-level, so no GVC flag is involved. Store a copy of all three values somewhere safe, outside Control Plane. Create the secret **before** installing. A missing secret pauses the deployment waiting on it — the install looks broken until the secret exists. `SECRET_KEY_BASE` and `LOCKBOX_MASTER_KEY` are write-once: rotating `LOCKBOX_MASTER_KEY` makes every datasource credential ToolJet has stored undecryptable. For authenticated SMTP, you also need a second dictionary secret holding `SMTP_USERNAME` and `SMTP_PASSWORD` — see [Email](#email). Everything else works with the defaults. Once your key secret exists, install the template using your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## First Run ToolJet ships no default account. The first person to open the endpoint completes the Super Admin onboarding and creates the initial workspace, so open the instance and claim it as soon as the workload reports ready. The container waits for PostgreSQL, creates both databases, and runs all of its schema migrations before it starts serving. Expect roughly two minutes once the image is cached on the location, and noticeably longer on the very first pull — the ToolJet image is multi-gigabyte. Browse to the canonical `*.cpln.app` endpoint of the `{release}-tooljet` workload and complete the Super Admin form. Invitations are sent by email, so configure [SMTP](#email) at install time if you plan to invite users. ## Configuration The default `values.yaml` for this template: ```yaml theme={null} tooljet: image: tooljet/tooljet:v3.20.204-lts replicas: 1 # >=2 requires redis.enabled=true (shared job queue + multiplayer coordination) host: "" # public base URL ToolJet advertises, with https://; empty = derive from the canonical endpoint resources: minCpu: 500m maxCpu: 2000m minMemory: 1Gi maxMemory: 4Gi # first-boot migrations OOM below this (the image bakes a 4096MB Node heap) secrets: name: my-tooljet-secrets # dictionary secret with the three keys — MUST exist before install smtp: enabled: false # only takes effect at INITIAL install (see Email) host: smtp.example.com port: 587 fromEmail: no-reply@example.com auth: secretName: "" # optional dictionary secret with SMTP_USERNAME + SMTP_PASSWORD; empty = unauthenticated relay publicAccess: enabled: true # HTTPS UI + API on the auto *.cpln.app endpoint; false = internal-only internalAccess: type: same-gvc # options: none, same-gvc, same-org, workload-list workloads: [] # used only with workload-list, e.g. //gvc/GVC_NAME/workload/WORKLOAD_NAME postgres: # app DB + ToolJet Database image: postgres:16 config: username: tooljet password: change-me-tooljet-pg # change before installing database: tooljet # tooljet_db is auto-created alongside it resources: minCpu: 250m maxCpu: 1000m minMemory: 512Mi maxMemory: 1Gi volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) redis: # optional — required for tooljet.replicas >= 2 enabled: false redis: image: redis:7.4 replicas: 1 # keep at 1 — ToolJet is not Sentinel-aware auth: password: enabled: false # true = require AUTH, wired into ToolJet as REDIS_PASSWORD value: change-me-tooljet-redis resources: cpu: 200m memory: 256Mi minCpu: 80m minMemory: 128Mi sentinel: image: redis:7.4 replicas: 1 # monitor only resources: cpu: 200m memory: 256Mi minCpu: 80m minMemory: 128Mi ``` ### ToolJet Server * `image` — The official ToolJet LTS image. * `replicas` — Number of ToolJet replicas. Anything above `1` requires `redis.enabled: true`; the chart fails the render with a clear message otherwise. * `host` — The public base URL ToolJet advertises in links and redirects, including the `https://` scheme. Leave it empty to derive it from the workload's canonical endpoint; set it when you serve ToolJet on a custom domain. * `resources` — CPU and memory bounds for the ToolJet container. Do not lower `tooljet.resources.maxMemory` below the default `4Gi`. The image bakes a 4096 MB Node heap, and the first boot's schema migrations are killed by the out-of-memory reaper at smaller limits, leaving the workload in a crash loop that never finishes migrating. ### Databases One PostgreSQL instance from the [postgres](/template-catalog/templates/postgres) subchart backs the whole deployment, and ToolJet's boot scripts create the databases it needs: | Database | Contents | | ------------ | -------------------------------------------------------------------------------------------- | | `tooljet` | Apps, users, workspaces, settings, and encrypted datasource credentials. | | `tooljet_db` | The ToolJet Database — user tables created in the UI, served through the in-image PostgREST. | | `sample_db` | Sample data created by ToolJet's boot scripts. | **Change `postgres.config.password` before installing** — it seeds the database on first boot and cannot be changed afterwards by editing values. `postgres.volumeset.capacity` sets the initial data volume size in GiB (minimum 10), and `postgres.resources` bounds the database container. The ToolJet Database is a core part of ToolJet 3.x and cannot be turned off. ### Scaling `tooljet.replicas` sets how many ToolJet replicas run. With more than one replica, all replicas must share one Redis for the job queue and for multiplayer coordination in the app editor, so `redis.enabled` must be `true` — the single-instance Redis bundled in the image cannot be shared. ```yaml theme={null} tooljet: replicas: 2 redis: enabled: true ``` * `redis.enabled` — Deploys the [redis](/template-catalog/templates/redis) subchart and points ToolJet at it. When disabled, a single-replica ToolJet uses the Redis inside its own container. * `redis.redis.auth.password.enabled` / `.value` — Require `AUTH` on the Redis data node and wire the password into ToolJet. When enabled, the password is stored in a template-created secret that the ToolJet identity is granted `reveal` on. * `redis.redis.resources` / `redis.sentinel.resources` — CPU and memory bounds for the Redis and sentinel containers. Keep `redis.redis.replicas` and `redis.sentinel.replicas` at `1`. ToolJet's Redis client is not Sentinel-aware and connects straight to the service DNS name, so additional data nodes would spread writes onto read-only replicas. The chart rejects any other value at render time. Sessions stay valid across replicas, and long-lived websocket connections — the ones the multiplayer app editor uses — are not cut short by the workload request timeout. ### Access * `publicAccess.enabled` — Serve the UI and API on the auto-assigned `*.cpln.app` HTTPS endpoint (default). Set to `false` for an internal-only instance: external requests are then refused at the edge, and in-GVC callers still reach it per `internalAccess`. * `internalAccess.type` — Controls which workloads can reach ToolJet over the internal network: | Type | Description | | --------------- | ---------------------------------------------------------------------- | | `none` | No internal access. | | `same-gvc` | Allow access from all workloads in the same GVC (default). | | `same-org` | Allow access from all workloads in the same organization. | | `workload-list` | Allow access only from workloads listed in `internalAccess.workloads`. | Firewall changes take a minute or two to propagate after a `helm upgrade` reports success. ### Email SMTP powers user invitations and password resets. It is off by default, and a default install works without it — the first visitor still becomes Super Admin. ```yaml theme={null} smtp: enabled: true host: smtp.example.com port: 587 fromEmail: no-reply@example.com auth: secretName: my-tooljet-smtp # dictionary secret with SMTP_USERNAME + SMTP_PASSWORD ``` For an authenticated relay, create the credentials secret before installing and reference it by name: ```bash theme={null} cpln secret create-dictionary --name my-tooljet-smtp \ --entry SMTP_USERNAME=apikey --entry SMTP_PASSWORD=YOUR_PASSWORD ``` Leave `smtp.auth.secretName` empty for a relay that does not require authentication. When it is set, the ToolJet identity is granted `reveal` on exactly that secret. Configure SMTP at the **initial install**. ToolJet seeds its mail settings from the environment during a first-boot migration, so enabling `smtp.*` through a later Helm upgrade has no effect — the values are applied to the workload but ToolJet keeps mail disabled. Decide before you install. ## Connecting | What | Value | | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | | Public UI / API | `https://.cpln.app` — `status.canonicalEndpoint` of `{release}-tooljet` (`cpln workload get {release}-tooljet -o yaml`) | | First-run onboarding | `https://.cpln.app/setup` | | Health check | `https://.cpln.app/api/health` — unauthenticated | | Internal (same GVC) | `http://{release}-tooljet.{gvc}.cpln.local:3000` | | Login | The Super Admin account created during first-run onboarding | | PostgreSQL (internal) | `{release}-postgres.{gvc}.cpln.local:5432`, credentials from `postgres.config` | | Redis (internal, optional) | `{release}-redis.{gvc}.cpln.local:6379` | ## Important Notes * **Create the key secret before installing** — `secrets.name` must point at an existing dictionary secret with `SECRET_KEY_BASE`, `LOCKBOX_MASTER_KEY`, and `PGRST_JWT_SECRET`. A missing secret pauses the deployment until the secret is created. * **`SECRET_KEY_BASE` and `LOCKBOX_MASTER_KEY` are write-once** — rotating `LOCKBOX_MASTER_KEY` makes every stored datasource credential undecryptable. Back the keys up outside Control Plane. * **Do not lower `tooljet.resources.maxMemory` below `4Gi`** — the first boot's migrations run out of memory below it and the workload never becomes ready. * **Claim the Super Admin account right after install** — the first visitor to complete onboarding owns the instance. * **Configure SMTP at initial install** — enabling it via a later upgrade silently does nothing. * **`tooljet.replicas` of 2 or more requires `redis.enabled: true`**, and both the Redis data node and sentinel must stay at one replica. * **Change `postgres.config.password` before installing** — it seeds the database on first boot and is not updated by later value edits. * **All ToolJet state lives in PostgreSQL** — apps, users, and ToolJet Database tables survive restarts and upgrades. Uninstalling deletes the database volume set and everything in it. * **ToolJet is licensed under the AGPL** — the deployed image is the LTS build and runs the free tier without a license key. ## External References Official ToolJet documentation Every setting the ToolJet server reads from its environment The built-in PostgREST-backed database and its API First-run onboarding and instance-wide administration Source code and release notes View the source files, default values, and chart definition # Trino Source: https://docs.controlplane.com/template-catalog/templates/trino Deploy Trino on Control Plane using the Template Catalog. A distributed SQL query engine that joins PostgreSQL, MySQL, ClickHouse, MongoDB and more in one query, with a coordinator plus a scalable worker tier. Covers catalogs, credential secrets, authentication, and access. ## Overview Trino is a distributed SQL query engine that queries data where it lives. This template deploys a Trino cluster — one coordinator plus a scalable worker tier — that connects to PostgreSQL, MySQL, ClickHouse, MongoDB and 40+ other sources through **catalogs**, and joins across them in a single query without copying any data. Databases you already run on Control Plane are reachable over internal DNS, so a query can span several of them at once. Trino stores nothing itself: it plans and executes queries against your existing systems, so there are no volumes and a restart costs only the queries in flight. ### Architecture * **Coordinator** — Parses and plans queries, serves the Web UI and the JDBC/REST endpoint on port `8080`, and tracks the workers. Always exactly one replica. * **Workers** — Stateless execution tier on port `8080`, `workers.replicas` replicas. Setting `workers.replicas: 0` collapses the cluster to a single node where the coordinator executes queries itself. * **Catalogs** — One properties file per data source, mounted on every node. The image already ships the `tpch`, `tpcds`, `memory` and `jmx` catalogs, so a default install is queryable immediately. ### What Gets Created * **Standard Coordinator Workload** — A single replica serving clients, the Web UI and worker discovery on port `8080`. * **Standard Worker Workload** — `workers.replicas` interchangeable execution replicas on port `8080`. Not created when `workers.replicas: 0`. * **Config Secrets** — The rendered `config.properties` (one per tier), `jvm.config` and `node.properties`, mounted into `/etc/trino`. * **Catalog Secrets** *(optional)* — One per `catalogs[]` entry, mounted as `/etc/trino/catalog/.properties` on every node. * **Password-authenticator Secret** *(optional)* — Created on the coordinator when `auth.enabled`. * **Identity & Policy** — One identity shared by both workloads, with a policy granting `reveal` on exactly the config, catalog and authentication secrets in use — including the credential secrets you created yourself. * **No Volume Sets** — Trino owns no data. This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites * **None for a default install.** The built-in `tpch`, `tpcds`, `memory` and `jmx` catalogs make the cluster queryable the moment it is ready. * **To query your own data sources:** one Control Plane secret per credential, created **before** installing — see [Connecting Data Sources](#connecting-data-sources). Credentials are never passed through values. * **To enable authentication** (required for public access): two [opaque secrets](/guides/create-secret/opaque) created before installing — see [Authentication](#authentication). ## Installation Install the template using your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} image: trinodb/trino:483 # ─── Coordinator ────────────────────────────────────────────────────────────── # One coordinator per cluster (Trino's design): plans queries, serves the Web UI, # the JDBC/REST endpoint, and worker discovery. coordinator: resources: minCpu: 500m maxCpu: 1000m minMemory: 2Gi maxMemory: 4Gi # JVM heap is a percentage OF THIS — see jvm.maxRAMPercentage # ─── Workers ────────────────────────────────────────────────────────────────── # Stateless query-execution tier. Raise replicas for more query capacity and to # keep capacity through a rolling restart. workers: replicas: 1 # 0 = single-node mode (the coordinator executes queries itself) resources: minCpu: 500m maxCpu: 2000m minMemory: 2Gi maxMemory: 4Gi # JVM heap is a percentage OF THIS — see jvm.maxRAMPercentage # ─── JVM ────────────────────────────────────────────────────────────────────── # Heap = this percentage of each tier's maxMemory. Trino derives per-node query # memory (30% of heap) and headroom (30% of heap) from it, so maxMemory is the # only number you normally change. Allowed range 40–80. jvm: maxRAMPercentage: 70 # ─── Catalogs (data sources) ────────────────────────────────────────────────── # One entry per data source. `properties` is a Trino connector properties file — # copy it from the connector's documentation page. Never put a password here: # create a Control Plane secret first and reference it as ${ENV:NAME} plus a # `secrets` entry below. The image already ships the tpch, tpcds, memory and jmx # catalogs, so a default install is queryable with no catalogs configured. catalogs: [] # worked examples: see "Connecting Data Sources" below # ─── Authentication ─────────────────────────────────────────────────────────── # Off by default: Trino is then unauthenticated and reachable only inside the # GVC. REQUIRED before publicAccess can be enabled. Both secrets must exist # BEFORE install. Note: the SERVER refuses password auth over plain HTTP, so # auth requires publicAccess — the chart enforces it. auth: enabled: false passwordFileSecretName: "" # opaque secret, payload = bcrypt password file (e.g. my-trino-passwords) sharedSecretName: "" # opaque secret, payload = random string shared by all nodes (e.g. my-trino-shared-secret) # ─── Access ─────────────────────────────────────────────────────────────────── publicAccess: enabled: false # true = Web UI + JDBC on the automatic *.cpln.app HTTPS endpoint; requires auth.enabled internalAccess: # who may reach the coordinator from inside Control Plane type: same-gvc # options: same-gvc, same-org, workload-list ('none' breaks Trino — the coordinator calls itself over this path) workloads: [] # used with workload-list, e.g. //gvc/GVC_NAME/workload/WORKLOAD_NAME ``` ### Coordinator and Workers * `image` — The Trino container image. The chart is shipped and tested on Trino 483. * `coordinator.resources` — CPU and memory for the single coordinator replica. * `workers.replicas` — Number of worker replicas. Raise it for more query throughput and to keep capacity available through a rolling restart; `0` collapses the cluster to a single node where the coordinator both plans and executes. * `workers.resources` — CPU and memory per worker replica. `maxMemory` must be a whole number of GiB and at least `2Gi`, `minMemory` may not exceed `maxMemory`, and `maxCpu:minCpu` may not exceed 4:1 (a Control Plane limit) — the chart refuses to render otherwise, with a message naming the value to fix. ### JVM and Memory `jvm.maxRAMPercentage` (default `70`, allowed range 40–80) sets the JVM heap as a percentage of each tier's `maxMemory`. Trino derives its per-node query memory and headroom from that heap, so `maxMemory` is normally the only number you change. Capacity AI is disabled on both workloads: the JVM sizes its heap from the container limit at startup, so shrinking the container afterwards would be an out-of-memory kill with no diagnostic. ### Catalogs Each `catalogs[]` entry renders one secret and mounts one file at `/etc/trino/catalog/.properties` on every node: | Field | Description | | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | `name` | Catalog name, queried as `..`. Lowercase letters, digits and underscores; `system` is reserved. | | `properties` | The connector properties file, copied from the connector's documentation page. Must contain a `connector.name=` line. | | `secrets[].env` | `UPPER_SNAKE_CASE` environment variable name, referenced from `properties` as `${ENV:NAME}`. Must be unique across all catalogs. | | `secrets[].secretName` | Name of the Control Plane secret holding the credential. It must exist **before** install. | | `secrets[].secretKey` | Key within a [dictionary secret](/guides/create-secret/dictionary). Omit it for an [opaque secret](/guides/create-secret/opaque) (its payload is used). | Object-storage catalogs (Hive, Iceberg, Delta Lake) need an external metastore, which this template does not bundle. For Iceberg, deploy the [Polaris](/template-catalog/templates/polaris) template as the REST catalog and point a `catalogs[]` entry at it — that pairing is tested end to end, including writing Iceberg tables to [SeaweedFS](/template-catalog/templates/seaweedfs). ### Authentication `auth.enabled` turns on Trino's file-based password authentication on the coordinator and the shared secret that nodes use to authenticate to each other. It requires `publicAccess.enabled`, and both secret names are required when it is on. Create both [opaque secrets](/guides/create-secret/opaque) before installing: ```bash theme={null} # password file — bcrypt, cost >= 8, one user per line htpasswd -B -C 10 -n alice | cpln secret create-opaque \ --name my-trino-passwords --encoding plain -f - # internal shared secret — a random string used by every node printf '%s' "$(openssl rand -hex 32)" | cpln secret create-opaque \ --name my-trino-shared-secret --encoding plain -f - ``` `auth.enabled` requires `publicAccess.enabled`, and the chart enforces both directions. Trino's **server** refuses password authentication over plain HTTP, so an internal-only cluster with authentication on cannot be queried by anyone — in-GVC clients get `401 Password not allowed for insecure authentication`. Either enable public access (TLS terminates at the Control Plane edge) or leave authentication off and let the internal firewall be the boundary. Public access without authentication is rejected too: an unauthenticated Trino on the internet is a read primitive over every connected data source. ### Access * `publicAccess.enabled` — Serve the Web UI and the JDBC/REST endpoint on the automatic `*.cpln.app` HTTPS endpoint. Requires `auth.enabled`. When off, external requests are blocked at the edge even though the workload still has a canonical endpoint. * `internalAccess.type` — Which workloads inside Control Plane may reach the coordinator: | Type | Description | | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `same-gvc` | Allow access from all workloads in the same GVC (default). | | `same-org` | Allow access from all workloads in the same organization. | | `workload-list` | Allow access only from the workloads listed in `internalAccess.workloads`. The chart automatically adds the coordinator itself and its worker workload, so you only list your clients. | `none` is rejected by the chart. The coordinator addresses itself by service DNS, so its own task and status calls travel through this internal firewall — with `none` every query fails with `403 RBAC: access denied`, even at `workers.replicas: 0`. The worker tier keeps its own least-privilege firewall regardless of this setting: only the coordinator and sibling workers may reach it. ## Connecting Data Sources Point a catalog at any database Trino can reach. Templates deployed in the same GVC are reachable at `{workload-name}.{gvc}.cpln.local`. Credentials always come from a secret you create first and are referenced with Trino's `${ENV:NAME}` substitution, so no password is written into values or into any rendered file — the mounted catalog file contains only the placeholder. Use an [opaque secret](/guides/create-secret/opaque) for a single credential, or a [dictionary secret](/guides/create-secret/dictionary) when one secret holds several values: ```bash theme={null} # one credential — referenced without secretKey printf '%s' 'the-password' | cpln secret create-opaque \ --name my-postgres-credentials --encoding plain -f - # several values — referenced with secretKey cpln secret create-dictionary --name my-mysql-credentials --entry password=the-password ``` Reference the secret by name and inject it as an environment variable: ```yaml theme={null} catalogs: - name: pg # queried as pg..
properties: | connector.name=postgresql connection-url=jdbc:postgresql://my-postgres.my-gvc.cpln.local:5432/postgres connection-user=postgres connection-password=${ENV:PG_PASSWORD} secrets: - env: PG_PASSWORD secretName: my-postgres-credentials # must exist BEFORE install secretKey: password # omit for an opaque secret (uses its payload) ``` The catalog appears in `SHOW CATALOGS` once the cluster restarts with the new values. ### Catalog Examples Worked entries for the sibling database templates: ```yaml theme={null} catalogs: # postgres / postgres-highly-available / timescaledb / cockroach / postgis - name: pg properties: | connector.name=postgresql connection-url=jdbc:postgresql://my-postgres.my-gvc.cpln.local:5432/postgres connection-user=postgres connection-password=${ENV:PG_PASSWORD} secrets: - env: PG_PASSWORD secretName: my-postgres-credentials # mysql / mariadb / tidb — no database name in the URL - name: mysql properties: | connector.name=mysql connection-url=jdbc:mysql://my-mysql.my-gvc.cpln.local:3306 connection-user=root connection-password=${ENV:MYSQL_PASSWORD} secrets: - env: MYSQL_PASSWORD secretName: my-mysql-credentials secretKey: password # clickhouse — HTTP interface on 8123 - name: clickhouse properties: | connector.name=clickhouse connection-url=jdbc:clickhouse://my-clickhouse.my-gvc.cpln.local:8123/ connection-user=default connection-password=${ENV:CLICKHOUSE_PASSWORD} secrets: - env: CLICKHOUSE_PASSWORD secretName: my-clickhouse-credentials # mongodb — credentials live inside the URL, so the whole URL is the secret - name: mongo properties: | connector.name=mongodb mongodb.connection-url=${ENV:MONGO_URL} secrets: - env: MONGO_URL secretName: my-mongo-url ``` The matching templates are [postgres](/template-catalog/templates/postgres), [postgres-highly-available](/template-catalog/templates/postgres-highly-available), [mysql](/template-catalog/templates/mysql), [mariadb](/template-catalog/templates/mariadb), [clickhouse](/template-catalog/templates/clickhouse) and [mongodb](/template-catalog/templates/mongodb). ### Querying Across Catalogs With several catalogs configured, one statement spans all of them — including the built-in `tpch` data — and Trino performs the join itself: ```sql theme={null} SELECT c.name AS customer, c.tier, n.name AS nation, count(*) AS orders, sum(o.amount) AS total FROM pg.public.orders o JOIN mysql.demo.customers c ON o.customer_id = c.customer_id JOIN tpch.tiny.nation n ON o.nation_key = n.nationkey GROUP BY c.name, c.tier, n.name ORDER BY total DESC; ``` Trino reads from each source at query time; nothing is copied and nothing is written back. ## Connecting | What | Value | | ------------------------- | ------------------------------------------------------------------------------------------------------------------------ | | Web UI / JDBC (public) | `https://.cpln.app` — `status.canonicalEndpoint` of `{release}-trino`. Available when `publicAccess.enabled`. | | JDBC / clients (internal) | `jdbc:trino://{release}-trino.{gvc}.cpln.local:8080//` | | REST API | `POST /v1/statement` on the same host | | CLI inside the cluster | `cpln workload exec {release}-trino --container trino -- trino --execute "SELECT 1"` | | Credentials | A user from your password file when `auth.enabled`; none when it is off | | Built-in catalogs | `tpch`, `tpcds`, `memory`, `jmx`, `system` | With authentication on, the Web UI login form is served at `/ui/legacy/login.html` on the public endpoint. ## Availability Trino has no fault-tolerant execution in this template: a query running on a node that goes away fails and must be retried. Measured on a 3-worker cluster: | Event | Impact | | ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | | Worker-tier rolling restart (`replicas: 3`) | 3 of 80 queries failed, all inside the \~3 minute rollout window; every query after convergence succeeded. | | Worker replica lost | 0 of 90 queries failed. The replacement replica joined after \~40 s and the dead node was evicted from `system.runtime.nodes` after \~115 s. | | Coordinator restart | \~6 seconds of `503` responses and one in-flight query lost (`Query is gone (server restarted?)`). | The coordinator is the single point of failure — open-source Trino has no coordinator failover, and this template does not claim otherwise. Workers are interchangeable, so `workers.replicas` is the knob for both capacity and restart tolerance. At the default `workers.replicas: 1` a rolling restart empties the execution tier entirely, so every query in flight fails for the duration. ## Important Notes * **Every catalog credential secret must exist before installing** — a missing secret leaves the deployment waiting and the cluster never becomes ready. * **Rotating a secret's payload does not take effect until the workloads are redeployed.** Control Plane resolves secret-backed environment variables at deployment time, so run a Helm upgrade after changing a credential. * **`auth.enabled` requires `publicAccess.enabled`, and public access requires authentication** — the chart rejects either one on its own at render time. * **`internalAccess.type: none` is rejected** — the coordinator must be able to reach itself over the internal path. * **Trino is read-oriented here** — it queries the databases you connect; the template never installs or modifies them, and uninstalling removes only the cluster. Your own credential secrets are left in place. * **Keep the GVC single-location** — coordinator-to-worker traffic is per-query and latency-sensitive. ## External References Official Trino documentation Every available connector and its properties Trino SQL statement and syntax reference Connect BI tools and applications over JDBC How the bcrypt password file is used The `${ENV:NAME}` substitution used by catalog credentials View the source files, default values, and chart definition # Twenty Source: https://docs.controlplane.com/template-catalog/templates/twenty Deploy Twenty on Control Plane using the Template Catalog. Open-source CRM with customizable objects, pipelines, and views, a background job worker, a bundled Redis queue, and a single-instance or highly available PostgreSQL backing store. ## Overview Twenty is an open-source CRM — a self-hosted alternative to Salesforce, HubSpot, and Pipedrive — with customizable objects, pipelines, views, and workflows behind a REST and GraphQL API. This template deploys the Twenty server on port `3000`, a background worker that runs its job queues and cron jobs, a bundled Redis, and a PostgreSQL backing store that is a single instance by default or a highly available Patroni cluster when you opt in. ### Architecture * **Twenty server** — A stateful workload running the official image, serving the React front end, the REST API, and the GraphQL API on port `3000`. It scales horizontally through `twenty.replicas`. * **Background worker** — A second workload from the same image running `yarn worker:prod`. It processes the BullMQ queues (entity events, webhooks, workflow triggers, file maintenance) and runs the registered cron jobs. It is always exactly one replica. * **Redis** — A bundled single-node Redis with AOF persistence and a `noeviction` memory policy, deployed by this chart. Twenty does not boot without it: the queues and the cache both live here, and an evicted key would silently drop queued jobs. * **PostgreSQL (single instance, default)** — The [postgres](/template-catalog/templates/postgres) template as a subchart, running PostgreSQL 18. Holds every record, workspace, and workspace-level schema. * **PostgreSQL (highly available, optional)** — The [postgres-highly-available](/template-catalog/templates/postgres-highly-available) template instead, running PostgreSQL 17: 3× Patroni PostgreSQL with automatic failover, 3× etcd, and an HAProxy leader endpoint that Twenty connects through. * **Attachment storage** — Either a shared persistent volume mounted by both the server and the worker (default), or an S3 bucket you own. ### What Gets Created * **Stateful Twenty Workload** — The Twenty server on port `3000` (`{release}-twenty`), with `twenty.replicas` replicas and configurable CPU and memory. * **Standard Worker Workload** — The background worker (`{release}-twenty-worker`), pinned to one replica, with no inbound access at all. * **Stateful Redis Workload** — The bundled single-node Redis (`{release}-twenty-redis`) with password authentication. * **Database Workloads** — Single mode: one stateful PostgreSQL workload. HA mode: a stateful Patroni PostgreSQL workload, a stateful etcd workload, and a standard HAProxy leader-routing workload. * **Volume Sets** — A shared (read-write-many) attachment volume set mounted by both the server and the worker at `/app/packages/twenty-server/.local-storage` (only when `storage.type` is `local`), one for the Redis AOF file at `/data`, and the database subchart's data volumes. * **Secrets** — A dictionary secret (`{release}-twenty-creds`) holding the bundled Redis password used to assemble `REDIS_URL`, and an opaque secret (`{release}-twenty-worker-start`) holding the worker's start script. Your prerequisite app-key secret is *not* created by the template — see [Prerequisites](#prerequisites). * **Identity & Policy** — One identity shared by the server, worker, and Redis workloads, and a policy granting it `reveal` on exactly the secrets they read: the credentials secret, the worker start script, the active database's credentials secret, your app-key secret, and — when configured — the rotation fallback and S3 static-key secrets. In keyless S3 mode the identity also carries the cloud account link and your bucket-scoped IAM policy. * **Cron Backup Workload** *(optional)* — Created inside the backing PostgreSQL store when database backups are enabled. This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites Twenty encrypts stored OAuth tokens, TOTP secrets, and app variables, and signs its auth tokens, with a single app key that you supply through an [opaque secret](/guides/create-secret/opaque) created **before** installing. The value is never passed through Helm values. The payload is a single random string: ```bash theme={null} printf '%s' "$(openssl rand -base64 32)" | \ cpln secret create-opaque --name my-twenty-app-secret --encoding plain -f - ``` Use `printf` rather than `echo` so the payload carries no trailing newline. Set `secrets.name` to the secret's name (default `my-twenty-app-secret`). Secrets are org-level, so no GVC flag is involved. The template supplies the value to Twenty as both `APP_SECRET` and `ENCRYPTION_KEY`. Store a copy somewhere safe, outside Control Plane. Create the secret **before** installing. Without it the deployment sits waiting on a secret that does not exist and never becomes ready, which looks like a broken install. Treat the key as write-once: changing `secrets.name` without also setting `secrets.fallbackName` to the previous key makes stored OAuth tokens, TOTP secrets, and app variables undecryptable and logs everyone out. See [Rotating the App Key](#rotating-the-app-key). Everything else works with the defaults. Two optional features need their own setup first: * **S3 attachment storage** — an existing bucket plus either a Control Plane cloud account (AWS) or a static-key secret (S3-compatible servers). See [Attachment Storage](#attachment-storage). Required if you plan to run more than one replica. * **Database backups** — a bucket and provider access set up beforehand. See [Backing Up](#backing-up). Once your app key secret exists, install the template using your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Choosing a Database Mode Exactly one of the two backing stores must be enabled — the chart enforces this at render. Twenty is wired to the active database automatically, including for its boot migrations, and both modes create the `uuid-ossp` and `unaccent` extensions on first boot. | | `postgres` (default) | `postgresHA` | | ---------------------- | -------------------------------------- | ------------------------------------------------------- | | What runs | One single-replica PostgreSQL workload | 3× Patroni PostgreSQL, 3× etcd, HAProxy leader endpoint | | PostgreSQL major | 18 | 17 | | Database failover | None | Automatic (Patroni leader election) | | First install to ready | \~4 minutes | \~9 minutes | | Best for | Development and lightweight installs | Production | The two paths run **different PostgreSQL majors**, so pick the mode before you have data: moving an existing database between them is a `pg_dump` and restore, not a values change. To use HA mode, set `postgres.enabled: false` and `postgresHA.enabled: true`. ## First Run Twenty ships no default account, and the first person to sign up creates the workspace and becomes its full administrator. PostgreSQL and Redis come up first, then Twenty creates its schemas, installs the `uuid-ossp` and `unaccent` extensions, and runs its migrations before it starts serving. Expect roughly 4 minutes on the single-instance database and roughly 9 minutes in HA mode, where the server retries against the HAProxy endpoint until Patroni has elected a leader. Every fresh install logs `error: relation "core.appToken" does not exist` and similar lines for about a minute, along with `[DatabaseConfigDriver] [INIT] Failed to load config variables from database, falling back to environment variables`. These are containers reading the schema while it is still being created; they stop on their own once migrations finish. Browse to the canonical `*.cpln.app` endpoint of the `{release}-twenty` workload and create the first account. It becomes the workspace administrator with full admin-panel access. SMTP, AI provider keys, rate limits, and OAuth/SSO are not template values. Set them in **Settings → Admin Panel → Configuration Variables**; changes take effect within about 15 seconds. Do not abort a slow first install. The Twenty server does not listen on port `3000` until its boot migrations complete, and in HA mode the container deliberately restarts while it waits for the Patroni leader. Give it the full window above before investigating. ## Configuration The default `values.yaml` for this template: ```yaml theme={null} twenty: image: twentycrm/twenty:v2.26.1 replicas: 1 # >1 REQUIRES storage.type: s3 and moves boot migrations to the worker serverUrl: "" # public base URL; empty = the platform canonical endpoint dbPoolMaxConnections: 10 # PostgreSQL connections per pool, per process resources: minCpu: 250m maxCpu: 1000m minMemory: 512Mi maxMemory: 2Gi # headroom for first-boot migrations and upgrade backfills worker: # same image, `yarn worker:prod`; always exactly one replica resources: minCpu: 250m maxCpu: 1000m minMemory: 512Mi maxMemory: 2Gi secrets: name: my-twenty-app-secret # opaque secret holding the app key — MUST exist before install fallbackName: "" # opaque secret holding the PREVIOUS key, only during a rotation storage: type: local # local | s3 (s3 required for twenty.replicas > 1) local: volumeset: capacity: 10 # GiB (minimum 10); mounted at /app/packages/twenty-server/.local-storage s3: bucket: my-twenty-bucket region: us-east-1 endpoint: "" # set for S3-compatible servers (e.g. http://my-minio:9000) cloudAccountName: my-s3-cloud-account # keyless AWS auth; used only when auth.secretName is empty policyName: my-twenty-s3-policy # your pre-created bucket-scoped IAM policy auth: secretName: "" # dictionary secret with STORAGE_S3_ACCESS_KEY_ID + STORAGE_S3_SECRET_ACCESS_KEY; S3-compatible servers only publicAccess: enabled: true # HTTPS UI + API on the auto *.cpln.app endpoint; false = internal-only internalAccess: type: same-gvc # options: none, same-gvc, same-org, workload-list workloads: [] # used only with workload-list, e.g. //gvc/GVC_NAME/workload/WORKLOAD_NAME redis: # bundled single node — job queues + cache (required) image: redis:8.10.0 auth: password: change-me-twenty-redis # change before installing; letters, digits, - and _ only resources: minCpu: 100m maxCpu: 400m minMemory: 256Mi maxMemory: 512Mi volumeset: capacity: 10 # GiB (minimum 10); AOF at /data postgres: # single instance (default) — PostgreSQL 18 enabled: true image: postgres:18 config: username: twenty password: change-me-twenty-db # change before installing; letters, digits, - and _ only database: twenty resources: minCpu: 250m maxCpu: 1000m minMemory: 512Mi maxMemory: 1Gi volumeset: capacity: 10 # GiB (minimum 10) backup: enabled: false # see Backing Up; provider: aws | gcp | minio postgresHA: # highly available (opt-in) — PostgreSQL 17 enabled: false postgres: username: twenty password: change-me-twenty-db # change before installing; letters, digits, - and _ only database: twenty replicas: 3 resources: minCpu: 500m maxCpu: 1000m minMemory: 1Gi maxMemory: 2Gi volumeset: capacity: 10 # GiB per replica (minimum 10) backup: enabled: false # see Backing Up; mode: logical | wal-g, provider: aws | gcp | minio ``` ### Twenty Server * `image` — The official Twenty image. The Docker tag keeps its leading `v`. * `replicas` — Number of server replicas. Anything above `1` requires `storage.type: s3`; the chart fails the render with a clear message otherwise. See [Scaling](#scaling). * `serverUrl` — The public base URL Twenty uses as its front-end origin and for auth callbacks, including the `https://` scheme. Leave it empty to use the workload's canonical endpoint; set it when you serve Twenty on a custom domain. A value that does not match the URL browsers actually use breaks authentication and CORS with opaque errors. * `dbPoolMaxConnections` — PostgreSQL connections per pool, per process. Raise it and `replicas` together with care: the database has a finite connection limit, and the worker holds its own pools. * `resources` — CPU and memory bounds for the server container. The memory ceiling leaves headroom for the first-boot migrations and upgrade backfills. ### Background Worker The worker runs the same image with `yarn worker:prod` and is always exactly one replica — it registers the repeatable cron jobs, and it owns the boot migrations whenever `twenty.replicas` is above `1`. There is no `worker.replicas` knob; only `worker.resources` is configurable. The worker exposes no port and accepts no inbound traffic. Records created in the UI enqueue jobs that it drains: with the worker stopped, jobs pile up in Redis and complete as soon as it comes back. ### Rotating the App Key `secrets.name` points at the opaque secret holding the current app key. `secrets.fallbackName` points at a second opaque secret holding the **previous** key, and exists only for the duration of a rotation — with it set, rows encrypted under the old key stay readable while new writes use the new key. ```bash theme={null} printf '%s' "$(openssl rand -base64 32)" | \ cpln secret create-opaque --name my-twenty-app-secret-2 --encoding plain -f - ``` Set `secrets.name` to the new secret and `secrets.fallbackName` to the old one. The identity is granted `reveal` on exactly those two secrets, plus the ones the template creates. The server re-runs its boot migrations during this rollout, so it takes about as long as a first install. Sessions issued under the old key keep working, and existing records stay readable. Never change `secrets.name` without setting `secrets.fallbackName` to the previous key. Stored OAuth tokens, TOTP secrets, and app variables are encrypted with the old value and become undecryptable, and every user is logged out. ### Attachment Storage `storage.type` decides where uploaded files and attachments are kept. Attachments are written to a **shared (read-write-many) volume set** mounted at `/app/packages/twenty-server/.local-storage` by both the server and the worker, so the worker's file-maintenance jobs see what the server uploaded, and vice versa. Files survive restarts, redeploys, and upgrades under the same release name. ```yaml theme={null} storage: type: local local: volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) ``` Shared volume sets support **expansion only — they cannot be snapshotted**, and they exist in a single location. For production, `storage.type: s3` is the durable choice. Local storage also works only with a single replica; the chart refuses to render `twenty.replicas` above `1` with it. AWS S3 uses a Control Plane cloud identity: no credentials are stored, and the workload's identity vends temporary credentials at runtime. This is the only supported way to reach AWS S3 — the chart rejects static keys unless `storage.s3.endpoint` is set. Create an S3 bucket. Set `storage.s3.bucket` and `storage.s3.region` to match. If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) for your AWS account. Set `storage.s3.cloudAccountName` to its name. Create an AWS IAM policy with the JSON below (replace `YOUR_BUCKET_NAME` with your bucket), then set `storage.s3.policyName` to the policy's name: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::YOUR_BUCKET_NAME", "arn:aws:s3:::YOUR_BUCKET_NAME/*" ] } ] } ``` Keep `storage.s3.auth.secretName` empty. The template then attaches the cloud account and your policy to the Twenty identity, and no access keys exist anywhere. Twenty also writes its app-registration assets into the bucket at boot, so expect a few objects there before anyone uploads an attachment. S3-compatible servers cannot federate with a cloud account, so they use static access keys held in a dictionary secret. Create the bucket on your server. For the [MinIO](/template-catalog/templates/minio) template deployed in the same GVC, the S3 API address is `http://WORKLOAD_NAME.GVC_NAME.cpln.local:9000`. Set `storage.s3.endpoint` to the S3 API address including scheme and port, and `storage.s3.bucket` to the bucket name. Create a dictionary secret with the server's credentials and set `storage.s3.auth.secretName` to its name: ```bash theme={null} cpln secret create-dictionary --name my-twenty-s3-keys \ --entry STORAGE_S3_ACCESS_KEY_ID=YOUR_ACCESS_KEY \ --entry STORAGE_S3_SECRET_ACCESS_KEY=YOUR_SECRET_KEY ``` The Twenty identity is granted `reveal` on exactly this secret. ### Scaling `twenty.replicas` sets how many server replicas run behind the endpoint. The worker stays at one replica regardless. ```yaml theme={null} twenty: replicas: 2 storage: type: s3 s3: bucket: my-twenty-bucket region: us-east-1 cloudAccountName: my-s3-cloud-account policyName: my-twenty-s3-policy ``` Boot migrations must run in exactly one container, and the template places them automatically — you do not configure this: | `twenty.replicas` | Who migrates | What the others do | | ----------------- | ----------------- | ------------------------------------------------- | | `1` (default) | The server | The worker skips migrations and cron registration | | `2` or more | The single worker | Every server replica skips both | Because the worker owns migrations at two or more replicas, a **cold** multi-replica install serves errors for a minute or two while the worker creates the schema — the servers are up before the database is ready for them. This affects first installs only; upgrades of an existing release have a populated schema already. At two replicas, a rolling upgrade served **508 of 508** health checks successfully with no failures, and force-stopping one replica served **198 of 198** requests while the replacement came back — the rollout keeps at most one replica out at a time. `twenty.replicas` above `1` requires `storage.type: s3`. The chart refuses to render the combination with local storage. ### Access * `publicAccess.enabled` — Serve the UI and API on the auto-assigned `*.cpln.app` HTTPS endpoint (default). Set to `false` for an internal-only instance: external requests are then refused with a `403`, and in-GVC callers still reach it per `internalAccess`. Firewall changes take a couple of minutes to propagate. * `internalAccess.type` — Controls which workloads can reach Twenty over the internal network: | Type | Description | | --------------- | ---------------------------------------------------------------------- | | `none` | No internal access. | | `same-gvc` | Allow access from all workloads in the same GVC (default). | | `same-org` | Allow access from all workloads in the same organization. | | `workload-list` | Allow access only from workloads listed in `internalAccess.workloads`. | The bundled Redis and the database are never publicly reachable; they accept in-GVC traffic only. ### Redis The bundled Redis carries Twenty's BullMQ job queues and its cache, and Twenty does not boot without it. * `redis.image` — The Redis image to run. * `redis.auth.password` — Password for the bundled Redis, wired into `REDIS_URL`. **Change it before installing.** Use only letters, digits, `-`, and `_`: the value is embedded in a URL. Authentication is required — an unauthenticated or wrong-password client is refused. * `redis.resources` / `redis.volumeset.capacity` — CPU and memory bounds and the volume size for the AOF file at `/data`. Queue state is restored from the AOF after a restart. Redis runs with `maxmemory-policy noeviction` and `appendonly yes`, and this is deliberate: an evicted key would silently drop queued jobs. Around a Redis restart the server logs connection-reset errors and recovers on its own. ### Database Enable exactly one of `postgres` (single instance, default) or `postgresHA` (highly available) — see [Choosing a Database Mode](#choosing-a-database-mode). In both modes, **change the database password before installing** (`postgres.config.password` / `postgresHA.postgres.password`); it seeds the database on first boot and cannot be changed by editing values afterwards. Use only letters, digits, `-`, and `_` — the password is embedded in the connection URL. Twenty connects to the single instance directly, or to the HAProxy leader endpoint in HA mode, and creates the `uuid-ossp` and `unaccent` extensions itself on first boot. `postgres.resources` / `postgresHA.resources` and the `volumeset.capacity` values (GiB, minimum 10, per replica in HA mode) size the backing store. **Template version `1.0.0` did not compact the etcd cluster inside the bundled highly available database**, so etcd's backend grows with time alone and goes read-only once it reaches its 2 GiB quota — after roughly 110 days — taking PostgreSQL failover with it. Only installs running the HA database are affected (`postgresHA.enabled`, which is off by default here); see [etcd History Compaction](/template-catalog/templates/postgres-highly-available#etcd-history-compaction) for the mechanism and the symptoms. Upgrade to `1.0.1` or later to turn compaction on: that stops further growth but cannot shrink a backend that has already grown, and a cluster that has already raised a `NOSPACE` alarm needs operator recovery rather than an upgrade. ## Connecting | What | Value | | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | | Public UI / API | `https://.cpln.app` — `status.canonicalEndpoint` of `{release}-twenty` (`cpln workload get {release}-twenty -o yaml`) | | GraphQL — workspace records | `POST https://.cpln.app/graphql` | | GraphQL — auth and core | `POST https://.cpln.app/metadata` | | Health check | `GET https://.cpln.app/healthz` — unauthenticated | | Internal (same GVC) | `http://{release}-twenty.{gvc}.cpln.local:3000` | | Login | The first account created after install — there is no seeded user | | Redis (internal) | `{release}-twenty-redis.{gvc}.cpln.local:6379`, password from `redis.auth.password` | | PostgreSQL (internal, single mode) | `{release}-postgres.{gvc}.cpln.local:5432`, credentials from `postgres.config` | | PostgreSQL (internal, HA mode) | `{release}-postgres-ha-proxy.{gvc}.cpln.local:5432`, credentials from `postgresHA.postgres` | ## Backing Up Database backups are optional and disabled by default. When enabled, a scheduled job runs inside the backing PostgreSQL store and uploads to your bucket under the configured prefix — covering every record, workspace, and user, but not the attachments on the storage volume set (use `storage.type: s3` for durable attachments). Enable with `postgres.backup.enabled` or `postgresHA.backup.enabled`, matching your database mode, and complete the storage setup for your provider **before** installing. The values below are shown under `backup.*` — set them within the enabled database block. Create an S3 bucket. Set `backup.aws.bucket` and `backup.aws.region` to match. If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) for your AWS account. Set `backup.aws.cloudAccountName` to its name. Create an IAM policy granting the required S3 actions on the bucket, and set `backup.aws.policyName` to its name: ```json theme={null} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["s3:ListBucket"], "Resource": "arn:aws:s3:::my-backup-bucket" }, { "Effect": "Allow", "Action": ["s3:PutObject", "s3:GetObject", "s3:DeleteObject"], "Resource": "arn:aws:s3:::my-backup-bucket/*" } ] } ``` Create a GCS bucket. Set `backup.gcp.bucket` to its name. If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) for your GCP project and grant its service account **Storage Object Admin** (`roles/storage.objectAdmin`) on the bucket. Set `backup.gcp.cloudAccountName` to its name. Create your bucket on the server. Set `backup.minio.bucket` to its name. Set `backup.minio.endpoint` to the S3 API address including port, and `backup.minio.accessKey` / `backup.minio.secretKey` to credentials with access to the bucket. No Cloud Account is required — the keys authenticate directly. In HA mode, `postgresHA.backup.mode` selects `logical` (scheduled `pg_dump`) or `wal-g` (continuous WAL archiving). The single-instance path takes a `postgres.backup.schedule` cron expression. The full per-provider walkthrough, including restores, lives in the backing [postgres](/template-catalog/templates/postgres) and [postgres-highly-available](/template-catalog/templates/postgres-highly-available) template documentation. ## Important Notes * **Create the app key secret before installing** — `secrets.name` must point at an existing opaque secret (plain encoding). Without it the deployment waits on a secret that does not exist and looks broken. * **The app key is effectively write-once** — rotate it only with `secrets.fallbackName` set to the previous key, or stored OAuth tokens, TOTP secrets, and app variables become undecryptable and everyone is logged out. * **Sign up immediately after install** — there is no seeded account, and on a public endpoint the first visitor to sign up becomes the workspace administrator. Set `publicAccess.enabled: false` until you are ready if that is a concern. * **First boot is slow and noisy, and that is expected** — about 4 minutes on the single-instance database and about 9 in HA mode, with `relation "core.…" does not exist` errors for roughly the first minute. Do not abort the install. * **The single-instance and HA paths run different PostgreSQL majors** — 18 and 17 respectively. Choose before you have data; switching is a dump and restore. * **`twenty.replicas` above `1` requires `storage.type: s3`** — the shared local volume set lives in one location and cannot be snapshotted, so S3 is the production recommendation regardless of replica count. * **AWS S3 is keyless only** — use a cloud account plus a bucket-scoped IAM policy. Static keys are accepted only when `storage.s3.endpoint` points at an S3-compatible server. * **Change `postgres.config.password` (or `postgresHA.postgres.password`) and `redis.auth.password` before installing** — both seed their component on first boot, and both are embedded in connection URLs, so use only letters, digits, `-`, and `_`. * **Set `twenty.serverUrl` when Twenty sits behind a custom domain**, with the scheme (`https://crm.example.com`). A mismatch with the URL browsers use breaks auth callbacks and CORS with opaque errors. * **SMTP, AI provider keys, rate limits, and OAuth/SSO are not values knobs** — configure them in **Settings → Admin Panel → Configuration Variables** inside the app. * **Foreign data wrappers ("remote objects") are unavailable** — that feature needs upstream's custom PostgreSQL image, which this template does not deploy. * **Data survives restarts and upgrades; uninstalling deletes it** — records live in the database volume set and local attachments in the storage volume set, and both go with the release. Your prerequisite app key secret is yours and survives an uninstall. * **Twenty is licensed under the AGPL** — this template deploys the open-source edition. ## External References Official Twenty documentation Configuration variables and the in-app admin panel Moving an existing deployment to a newer release Query and mutate CRM records over HTTP Source code and release notes View the source files, default values, and chart definition # Tyk Source: https://docs.controlplane.com/template-catalog/templates/tyk Deploy Tyk on Control Plane using the Template Catalog. Covers configuration, scaling, and API gateway management with Redis-backed token storage, rate limiting, and analytics. ## Overview Tyk is an open-source API management platform that controls, secures, and monitors API traffic. This template deploys a Tyk API Gateway workload on Control Plane alongside Redis and Redis Sentinel, which serve as the backing store for tokens, analytics, rate limits, and gateway state. This template does not create a GVC. You must deploy it into an existing GVC. ### What Gets Created * **Standard Workload** — Tyk API Gateway (`RELEASE_NAME-tyk-api-gateway`): the gateway process, autoscaling between `minScale` and `maxScale` replicas. API definitions and policies are mounted from Control Plane secrets at startup. * **Secret** — Gateway admin secret (`RELEASE_NAME-tyk-gateway-secret`): a dictionary secret containing the admin API key used for management operations. * **Identity & Policy** — An identity (`RELEASE_NAME-tyk-identity`) bound to the gateway workload with `reveal` access to the gateway secret, Redis auth secrets, and optionally the API definitions and policy secrets. * **Redis and Redis Sentinel** — The [Redis](/template-catalog/templates/redis) template (v3.1.1) is deployed as a dependency, creating a Redis workload, a Sentinel workload, and their associated secrets, identities, and policies. ## Prerequisites Tyk loads API definitions and policies from files at startup. You must create the corresponding Control Plane secrets **before** deploying the template. ### 1. Create the API Definitions Secret Each key in the dictionary is a filename (e.g. `app1.json`) containing a Tyk API definition object. Create the secret using `cpln apply`: ```yaml theme={null} kind: secret name: my-tyk-apis description: my-tyk-apis tags: {} type: dictionary data: app1.json: >- { "api_id": "app1", "name": "app1", "org_id": "default", "use_keyless": true, "use_jwt": false, "disable_rate_limit": true, "definition": { "location": "header", "key": "version" }, "version_data": { "not_versioned": true, "versions": { "Default": { "name": "Default", "use_extended_paths": true } } }, "proxy": { "listen_path": "/app1", "target_url": "http://app1.example-gvc.cpln.local:80", "strip_listen_path": true }, "active": true} ``` Set `apiSecretName` in your values to the name of this secret. To omit API definitions entirely, leave `apiSecretName` empty. ### 2. Create the Policies Secret The policies secret is a single opaque secret containing a JSON object where each key is a policy ID: ```yaml theme={null} kind: secret name: my-tyk-policies description: my-tyk-policies tags: {} type: opaque data: encoding: plain payload: |- { "app1-rate-limit": { "org_id": "default", "active": true, "rate": 20, "per": 100, "quota_max": 0, "quota_renewal_rate": 0, "quota_remaining": 0, "access_rights": { "app1": { "api_id": "app1", "api_name": "app1", "versions": ["Default"] } } } } ``` Set `policySecretName` in your values to the name of this secret. To omit policies entirely, leave `policySecretName` empty. ## Installation To install, follow the instructions for your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Configuration The default `values.yaml` for this template: ```yaml theme={null} image: tykio/tyk-gateway:v5.10.0 listenPort: 8080 # REQUIRED - The port exposed on the Tyk workload apiSecretName: my-tyk-apis # REQUIRED - The name of the pre-configured Control Plane secret that contains your API definitions policySecretName: my-tyk-policies # REQUIRED - The name of the pre-configured Control Plane secret that contains your policies # Note: if you wish to omit one of these secrets, leave the value empty and it will be omitted from the workload's configuration adminSecret: mysecret # REQUIRED - The value you set to be the admin API key for management of Tyk resources: cpu: 50m memory: 128Mi autoscaling: maxScale: 3 metric: cpu minScale: 1 scaleToZeroDelay: 300 target: 100 multiZone: false # OPTIONAL - Deploys replicas across multiple zones (confirm availability in your location) externalAccess: true # OPTIONAL - Set to true to expose the workload to the internet; set to false for internal-only access internalAccess: # OPTIONAL - Sets the internal firewall scope type: none # options: none, same-gvc, same-org, workload-list workloads: # Note: can only be used if type is same-gvc or workload-list #- //gvc/GVC_NAME/workload/WORKLOAD_NAME redis: redis: resources: cpu: 200m memory: 256Mi minCpu: 80m minMemory: 128Mi replicas: 2 auth: password: enabled: true value: myRedisPassword firewall: internal_inboundAllowType: same-gvc persistence: enabled: true sentinel: resources: cpu: 200m memory: 256Mi minCpu: 80m minMemory: 128Mi replicas: 3 auth: password: enabled: true value: mySentinelPassword firewall: internal_inboundAllowType: same-gvc persistence: enabled: true ``` ### API Definitions & Policies * `apiSecretName` — Name of the pre-existing Control Plane secret containing API definitions. Each key in the dictionary is a JSON filename mounted at `/opt/tyk-gateway/apps`. **Must be created before deploying.** * `policySecretName` — Name of the pre-existing Control Plane opaque secret containing the policies JSON, mounted at `/opt/tyk-gateway/policies/policies.json`. **Must be created before deploying.** Leave either value empty to omit that mount from the gateway workload. ### Admin Secret * `adminSecret` — The admin API key used for gateway management operations (e.g. creating keys, reloading APIs). **Change before deploying to production.** Stored in a dictionary secret and injected into the gateway at startup. ### Resources & Autoscaling * `resources.cpu` / `resources.memory` — CPU and memory allocated to the Tyk Gateway workload. * `autoscaling.minScale` / `autoscaling.maxScale` — Minimum and maximum number of gateway replicas. * `autoscaling.metric` — Scaling metric (`cpu` by default). * `autoscaling.target` — Target metric value that triggers a scale-up. * `autoscaling.scaleToZeroDelay` — Seconds of inactivity before scaling to zero (only applies when `minScale` is `0`). * `multiZone` — When `true`, spreads replicas across availability zones within the location. ### Access * `externalAccess` — Set to `true` to expose the gateway publicly. Set to `false` for internal-only access. * `internalAccess.type` — Controls which workloads can connect to the gateway internally: | Type | Description | | --------------- | --------------------------------------------------------------- | | `none` | No internal access allowed | | `same-gvc` | Allow access from all workloads in the same GVC | | `same-org` | Allow access from all workloads in the same organization | | `workload-list` | Allow access only from specific workloads listed in `workloads` | * `internalAccess.workloads` — List of specific workload links, used when `type` is `workload-list`. ### Redis The Redis subchart is configured under the `redis` key. See the [Redis template](/template-catalog/templates/redis) for full configuration details. Key options: * `redis.redis.replicas` — Number of Redis replicas. * `redis.redis.auth.password.enabled` / `redis.redis.auth.password.value` — Redis password authentication. **Change before deploying to production.** * `redis.redis.persistence.enabled` — Persist Redis data to a volume set. * `redis.sentinel.replicas` — Number of Sentinel replicas (3 recommended for production). * `redis.sentinel.auth.password.enabled` / `redis.sentinel.auth.password.value` — Sentinel password authentication. **Change before deploying to production.** * `redis.sentinel.persistence.enabled` — Persist Sentinel state to a volume set. ### Connecting to the Gateway Access the gateway from within the same GVC at: ```text theme={null} RELEASE_NAME-tyk-api-gateway.GVC_NAME.cpln.local:8080 ``` When `externalAccess` is `true`, the gateway is also reachable via its public Control Plane endpoint. The Tyk management API is available on the same port under the `/tyk/` path. Requests require the `x-tyk-authorization` header set to the value of `adminSecret`. ### Ports | Port | Protocol | Description | | ------ | -------- | ---------------------------------------- | | `8080` | HTTP | API traffic and management API (`/tyk/`) | ## External References Official Tyk API Gateway documentation Reference for the API definition JSON structure Guide for configuring Tyk access policies View the source files, default values, and chart definition # Umami Source: https://docs.controlplane.com/template-catalog/templates/umami Deploy Umami on Control Plane using the Template Catalog. Privacy-first, cookieless web and product analytics — a self-hosted Google Analytics alternative — with a stateless multi-replica app tier and a single-instance or highly available PostgreSQL backing store. Covers the tracking script, database modes, scaling, and optional backups. ## Overview Umami is a privacy-first, cookieless web and product analytics platform — a self-hosted, MIT-licensed alternative to Google Analytics. This template deploys the stateless Umami v3 app tier backed by PostgreSQL, serving both the analytics dashboard and the public tracking endpoint over HTTPS. You embed a small tracking script on your site; visitor events POST back to the same workload and are stored in PostgreSQL, with no cookies and no personal data collected. ### Architecture * **Umami** — A stateless `standard` workload serving the dashboard, API, and tracking/collect endpoint on port `3000`. Runs a single replica by default; set `replicas` to `2` or more for an always-on tier with zero-downtime rolling restarts. All state lives in PostgreSQL, so replicas are independent — no clustering. * **PostgreSQL (single-instance, default)** — The [postgres](/template-catalog/templates/postgres) template as a subchart: the backing store for all users, websites, sessions, and events. * **PostgreSQL (HA, optional)** — The [postgres-highly-available](/template-catalog/templates/postgres-highly-available) template instead: 3× Patroni PostgreSQL with automatic failover and an HAProxy leader endpoint, for a durable production store. ### What Gets Created * **Standard Umami Workload** — One or more stateless replicas serving the UI, API, and tracking endpoint on port `3000`. * **Database Workloads** — Single mode: one stateful PostgreSQL workload. HA mode: a stateful Patroni PostgreSQL workload, a stateful etcd workload, and a standard HAProxy leader-routing workload. * **Volume Sets** — The database subchart's persistent volumes (10 GiB by default; per replica in HA mode). Umami itself has none. * **Secret, Identity & Policy** — A template-managed `appSecret` (dictionary secret) and a least-privilege policy granting the Umami identity `reveal` on exactly the app config secret and the active database credential secret. * **Cron Backup Workload** *(optional)* — When database backups are enabled. This template does not create a GVC. You must deploy it into an existing GVC. ## Prerequisites None for a default install. For optional database backups you need a bucket and access setup for one of the supported providers — see [Backing Up](#backing-up). Install the template using your preferred method: Browse, install, and manage templates visually Manage templates from your terminal }> Declare templates in your Terraform configurations Pulumi Icon Streamline Icon: https://streamlinehq.com } > Declare templates in your Pulumi programs ## Choosing a Database Mode Exactly one of the two backing stores must be enabled — the chart enforces this at render. Umami is wired to the active database automatically. | | `postgres` (default) | `postgresHA` | | ----------------- | -------------------------------------- | ------------------------------------------------------- | | What runs | One single-replica PostgreSQL workload | 3× Patroni PostgreSQL, 3× etcd, HAProxy leader endpoint | | Database failover | None | Automatic (Patroni leader election) | | Best for | Development and lightweight installs | Production | To switch to HA mode, set `postgres.enabled: false` and `postgresHA.enabled: true`. ## Configuration Key configuration values (see the template's `values.yaml` for the complete set): ```yaml theme={null} image: ghcr.io/umami-software/umami:3.2.0 replicas: 1 # 1 = proven single-instance; 2+ = always-on, zero-downtime restarts resources: # per replica cpu: 500m memory: 512Mi minCpu: 100m minMemory: 256Mi app: # Signs auth tokens; MUST be unique per install, identical across replicas, # and stable across restarts (changing it logs everyone out). appSecret: "change-me-KJ8xQ2mZraB7vN1pLwCf5tHgUeYd0sQ4" # override: openssl rand -base64 32 disableTelemetry: true # opt out of Umami's anonymous usage telemetry tracker: scriptName: "" # custom tracker script path, e.g. "s.js" (dodges ad blockers); "" = default /script.js collectEndpoint: "" # custom collect API path, e.g. "/api/track"; "" = default /api/send postgres: # default: single-instance PostgreSQL enabled: true image: postgres:18 config: username: umami password: change-me-umami-db # change before installing database: umami volumeset: capacity: 10 # initial capacity in GiB (minimum is 10) backup: enabled: false # true = scheduled backups of the analytics DB to object storage schedule: "0 2 * * *" # daily at 2am UTC provider: aws # aws | gcp | minio aws: bucket: my-backup-bucket region: us-east-1 cloudAccountName: my-backup-cloudaccount policyName: my-backup-policy prefix: umami/backups postgresHA: # durable HA: 3-replica Patroni store with an HAProxy leader endpoint (disable postgres first) enabled: false postgres: username: umami password: change-me-umami-db # change before installing database: umami replicas: 3 volumeset: capacity: 10 # initial capacity in GiB per replica (minimum is 10) backup: enabled: false # true = scheduled backups to object storage mode: logical # logical | wal-g provider: aws # aws | gcp | minio aws: bucket: my-backup-bucket region: us-east-1 cloudAccountName: my-backup-cloudaccount policyName: my-backup-policy prefix: umami/backups publicAccess: enabled: true # HTTPS dashboard + tracking endpoint via the canonical *.cpln.app endpoint internalAccess: type: same-gvc # options: none, same-gvc, same-org, workload-list workloads: [] # only used with same-gvc / workload-list ``` ### Application * `image` — The Umami open-source container image. * `replicas` — Number of stateless app-tier replicas. `1` is the proven single-instance shape; `2` or more gives an always-on tier where rolling restarts cycle one replica at a time with no downtime. Replicas are independent and share only the database and `app.appSecret`. * `resources` — CPU and memory per replica. * `app.appSecret` — Signs auth tokens and secures sessions. It **must** be unique per installation, identical across replicas, and stable across restarts — changing it logs every user out. Generate one with `openssl rand -base64 32` and set it before installing. * `app.disableTelemetry` — When `true` (default), opts out of Umami's anonymous usage telemetry. ### Tracker * `tracker.scriptName` — Serve the tracking script under a custom path (e.g. `s.js` → `/s.js`) instead of the default `/script.js`. Useful for reducing ad-blocker interception. * `tracker.collectEndpoint` — Have the tracker POST events to a custom path (e.g. `/api/track`) instead of the default `/api/send`. Both default to `""` (standard paths). Custom paths take effect once a replica has fully booted with the new setting; a mid-rollout replica still serves the old path until it cycles. ### Access * `publicAccess.enabled` — Serve the dashboard and tracking endpoint on the canonical `*.cpln.app` HTTPS endpoint (default). **Keep this enabled for tracking to work** — browsers must reach `/script.js` and `/api/send`. Set to `false` for an internal-only instance (external requests are blocked at the edge; in-GVC callers still reach it per `internalAccess`), which stops all public data collection. * `internalAccess.type` — Internal firewall scope of the Umami workload: | Type | Description | | --------------- | ---------------------------------------------------------------------- | | `none` | No internal access. | | `same-gvc` | Allow access from all workloads in the same GVC (default). | | `same-org` | Allow access from all workloads in the same organization. | | `workload-list` | Allow access only from workloads listed in `internalAccess.workloads`. | ### Backing Store Enable exactly one of `postgres` (single-instance, default) or `postgresHA` (HA) — see [Choosing a Database Mode](#choosing-a-database-mode). In both modes, **change the database password before installing** (`postgres.config.password` / `postgresHA.postgres.password`). **Template version `1.0.0` did not compact the etcd cluster inside the bundled highly available database**, so etcd's backend grows with time alone and goes read-only once it reaches its 2 GiB quota — after roughly 110 days — taking PostgreSQL failover with it. Only installs running the HA database are affected (`postgresHA.enabled`, which is off by default here); see [etcd History Compaction](/template-catalog/templates/postgres-highly-available#etcd-history-compaction) for the mechanism and the symptoms. Upgrade to `1.0.1` or later to turn compaction on: that stops further growth but cannot shrink a backend that has already grown, and a cluster that has already raised a `NOSPACE` alarm needs operator recovery rather than an upgrade. ## Connecting | What | Value | | ------------------- | ---------------------------------------------------------------------------------------------- | | Public URL | `status.canonicalEndpoint` of `{release}-umami` (`cpln workload get {release}-umami -o yaml`) | | Dashboard / login | `https://.cpln.app/login` | | Tracking script | `https://.cpln.app/script.js` (embed on your site) | | Collect endpoint | `https://.cpln.app/api/send` (where the tracker POSTs events) | | Internal (same GVC) | `http://{release}-umami.{gvc}.cpln.local:3000` | | Default admin | `admin` / `umami` — hardcoded; change it immediately (see [Important Notes](#important-notes)) | To start collecting data, log in, add a website in the dashboard, then paste the generated `