> ## Documentation Index
> Fetch the complete documentation index at: https://controlplanecorporation-majid-docs-content-expansion.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

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

<Tip>
  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`.
</Tip>

### Build Options

<Tabs>
  <Tab title="With Dockerfile">
    ```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)  |

    <Info>
      When `--dir` is specified, the Dockerfile in that directory is used by default. Use `--dockerfile` to override this behavior.
    </Info>
  </Tab>

  <Tab title="With Buildpacks">
    ```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)           |

    <Info>
      For language-specific requirements and conventions, see the [Buildpacks Guide](/guides/buildpacks).
    </Info>
  </Tab>

  <Tab title="Without Docker (remote)">
    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.

    <Info>
      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/<name>`.
    </Info>

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

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

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

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

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

### 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=<id>[,src=<source>]`, 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/<id>`:

```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/<name>`         | An [opaque secret](/guides/create-secret/opaque) in your org, mounted whole                                                                               |
| `cpln://secret/<name>.<field>` | One field of a structured secret, such as a [dictionary](/guides/create-secret/dictionary) entry or a [userpass](/guides/create-secret/userpass) password |
| `<path>`                       | A file on this machine, exactly as docker reads it                                                                                                        |
| omitted, or `env=<VAR>`        | An environment variable on this machine, exactly as docker reads it                                                                                       |

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

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

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

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

<Info>
  For cross-org copies with different credentials, use `--to-profile`. See the [Copy Images guide](/guides/copy-image).
</Info>

## 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 '<token>' --password-stdin
```

See [CI/CD Usage](/cli-reference/ci-cd-development/ci-cd) for complete automation setup.

## Troubleshooting

<AccordionGroup>
  <Accordion title="Docker is not installed or the daemon is not running">
    Start Docker, or build without it:

    ```bash theme={null}
    cpln image build --name my-app:v1 --remote
    ```
  </Accordion>

  <Accordion title="The folder exceeds the 500 MB (or 20,000 entry) limit">
    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.
  </Accordion>

  <Accordion title="unknown shorthand flag: 'f' in -f">
    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
    ```
  </Accordion>

  <Accordion title="Authentication failed or 403">
    Re-run `cpln image docker-login` to refresh credentials and double check that you don't have typos in the org name.
  </Accordion>

  <Accordion title="--secret requires a Dockerfile build">
    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`.
  </Accordion>

  <Accordion title="Build secrets require Docker Buildx">
    Local builds mount secrets through Buildx, which is not installed. Install the plugin as shown above, or build remotely with `--remote`.
  </Accordion>

  <Accordion title="The mounted secret is empty">
    The `id` in `--secret id=<id>` and the `id` in `RUN --mount=type=secret,id=<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.
  </Accordion>

  <Accordion title="secret &#x22;...&#x22; is a dictionary secret, so name the field you need">
    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.
  </Accordion>

  <Accordion title="Push denied">
    Verify you have push permission on images. Check your policies or refresh your service account token.
  </Accordion>

  <Accordion title="Image too large">
    Optimize your Dockerfile:

    * Use multi-stage builds
    * Start from smaller base images
    * Remove unnecessary files
  </Accordion>
</AccordionGroup>

## Learn More

<CardGroup cols={2}>
  <Card title="Buildpacks Guide" href="/guides/buildpacks" icon="cubes">
    Language-specific conventions for building without Dockerfiles
  </Card>

  <Card title="Push Images" href="/guides/push-image" icon="upload">
    Detailed guide for building and pushing images
  </Card>

  <Card title="Pull Images" href="/guides/pull-image" icon="download">
    Configure workloads to pull from private registries
  </Card>

  <Card title="Image Command Reference" href="/cli-reference/commands/image" icon="book">
    Full command documentation
  </Card>
</CardGroup>
