> ## Documentation Index
> Fetch the complete documentation index at: https://docs.controlplane.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Using the CLI in Containers

> 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

<Warning>
  Never bake credentials into container images. Pass tokens at runtime via environment variables or secret managers.
</Warning>

## Installation methods

<Tabs>
  <Tab title="npm (Node.js base)">
    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
    ```
  </Tab>

  <Tab title="Binary (lightweight)">
    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/2505100923-6cd84ed9/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
    ```

    <Note>
      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.
    </Note>

    **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
    ```

    <Tip>
      Multi-stage builds keep the final image small by excluding download tools.
    </Tip>
  </Tab>
</Tabs>

## Pin the CLI version

Always pin the CLI version for reproducible builds:

<CodeGroup>
  ```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/2505100923-6cd84ed9/cpln-linux.tgz
  ```
</CodeGroup>

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
```

<Warning>
  Never print `CPLN_TOKEN` in logs or commit it to version control.
</Warning>

## Using cpln in automation

Once installed, execute CLI commands from your application code:

<Tabs>
  <Tab title="Node.js">
    ```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);
    });
    ```
  </Tab>

  <Tab title="Python">
    ```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)
    ```
  </Tab>

  <Tab title="Go">
    ```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)
        }
    }
    ```
  </Tab>

  <Tab title="Bash">
    ```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
    ```
  </Tab>
</Tabs>

## Common workflows

### Build and push images

```bash theme={null}
cpln image build --name <image-name>:<image-tag> --push
```

### 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

<AccordionGroup>
  <Accordion title="cpln: not found">
    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`).
  </Accordion>

  <Accordion title="Authentication failures">
    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
  </Accordion>

  <Accordion title="Docker registry authentication issues">
    Re-run Docker login in your container:

    ```bash theme={null}
    cpln image docker-login
    ```

    Ensure the service account has access to the image registry.
  </Accordion>
</AccordionGroup>

<Info>
  For more troubleshooting help, see the [Troubleshooting](/cli-reference/using-cli/troubleshooting) page.
</Info>

## Best practices

<AccordionGroup>
  <Accordion title="Use multi-stage builds">
    Keep final images small by using multi-stage builds to separate download/build tools from the runtime image.
  </Accordion>

  <Accordion title="Pin the CLI version">
    Always specify an exact CLI version for reproducible builds:

    <CodeGroup>
      ```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/2505100923-6cd84ed9/cpln-linux.tgz
      RUN curl -fsSL "${CPLN_BINARY_URL}" -o /tmp/cpln.tgz && \
          tar -xzf /tmp/cpln.tgz -C /usr/local/bin
      ```
    </CodeGroup>
  </Accordion>

  <Accordion title="Never bake credentials">
    Pass tokens at runtime via environment variables or secret managers. Never include them in the image.
  </Accordion>

  <Accordion title="Set CPLN_ORG and CPLN_GVC">
    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
    ```
  </Accordion>

  <Accordion title="Verify installation in Dockerfile">
    Add a verification step:

    ```dockerfile theme={null}
    RUN cpln --version
    ```

    This catches installation issues during the build.
  </Accordion>
</AccordionGroup>

## Next steps

<CardGroup cols={2}>
  <Card title="CI/CD Usage" href="/cli-reference/ci-cd-development/ci-cd" icon="code">
    Complete CI/CD automation guide
  </Card>

  <Card title="Installation" href="/cli-reference/installation" icon="download">
    CLI installation methods
  </Card>

  <Card title="Authentication" href="/cli-reference/get-started/authentication" icon="key">
    Authentication strategies
  </Card>

  <Card title="Profiles" href="/cli-reference/get-started/profiles" icon="user">
    Profile management
  </Card>
</CardGroup>
