# Build and use custom images (https://depot.dev/docs/ci/how-to-guides/custom-images)

Build a reusable custom image from the Depot base image that includes your tools and dependencies.

## How it works

A custom image in Depot CI is a snapshot of a sandbox environment. You build the custom image using a job that runs only your setup steps on the Depot base image. After your setup steps complete, a Depot snapshot action captures the state of the sandbox environment and pushes it to the Depot registry as a reusable image. Any job can then use that snapshot as its starting image, skipping the setup steps entirely.

<NoteCallout>
  The snapshot action can only be used in workflows running on Depot CI. It's not compatible with GitHub Actions or
  other CI providers.
</NoteCallout>

## Snapshot a sandbox to build a custom image

Build a custom image using a job that runs on a standard Depot sandbox and installs the tools and dependencies you want to bake in. The job creates your custom image and pushes it to the Depot Registry. Use a separate workflow for the build image job, and then use the resulting image in one or more other workflows. This pattern is usually the most efficient because you run the image build workflow once to create the image and then only when dependencies change. If your use case differs, you can also run the build image job in the same workflow as other jobs that subsequently use it.

<NoteCallout>
  Actions that set or change environment variables (such as actions/setup-node) need to be in both the build image
  workflow and the workflows that use the custom image. See [Environment variable changes don't persist in custom
  images](#environment-variable-changes-dont-persist-in-custom-images).
</NoteCallout>

To create the build image workflow:

* Add [`depot/snapshot-action`](https://github.com/depot/snapshot-action) as a step (after your setup steps). It captures the full state of the sandbox environment and pushes it to the Depot registry as a reusable image.
* Include `image` (required) as the input for the `depot/snapshot-action` in the format:

  ```yaml
  <org-id>.registry.depot.dev/<repo>:<tag>
  ```

  This URL is the path in the Depot registry where the image will be stored and how you'll reference the image in your CI workflows.

  You can copy your organization ID from the [Depot dashboard](/orgs) or use the `depot org list` command.

Example build image job:

```yaml
jobs:
  build-image:
    runs-on: depot-ubuntu-latest
    steps:
      - name: Install dependencies
        run: |
          sudo apt-get update
          sudo apt-get install -y your-tool-here
      - uses: depot/snapshot-action@v1
        with:
          image: <org-id>.registry.depot.dev/my-ci-image:latest
```

## Use a custom image in a job

Any job in any workflow on Depot CI can specify your custom image. The custom image is in the Depot registry (`registry.depot.dev`). Images from external registries aren't supported.

To run a job on a custom image, specify `runs-on` with `size` and `image` keys (both required).

* `size`: the size of the sandbox
* `image`: the Depot registry URL of the image (defined in the build image workflow)

The `runs-on.image` value must be a Depot registry URL for an image created using the `depot/snapshot-action` as described in [Snapshot a sandbox to build a custom image](#snapshot-a-sandbox-to-build-a-custom-image). You can't use any other images or artifacts, even if they exist in the Depot registry.

Example specifying a custom image in a job:

```yaml
jobs:
  use-image:
    runs-on:
      size: 2x8
      image: xk7m4hnp2q.registry.depot.dev/my-ci-image:latest
    steps:
      - uses: actions/checkout@v4
      - run: your-tool-here do-the-thing
```

Available values for `size`:

| Size     | CPUs | Memory |
| -------- | ---- | ------ |
| `2x8`    | 2    | 8 GB   |
| `4x16`   | 4    | 16 GB  |
| `8x32`   | 8    | 32 GB  |
| `16x64`  | 16   | 64 GB  |
| `32x128` | 32   | 128 GB |
| `64x256` | 64   | 256 GB |

For sandbox information and pricing, see [Depot CI sandboxes](/docs/ci/overview#depot-ci-sandboxes).

## Environment variable changes don't persist in custom images

For actions that set or change environment variables, you need to include the steps in both the image build workflow and the workflows that use the custom image.

Snapshots capture only the sandbox filesystem. Environment variables exist only in the running process (not the filesystem) so they aren't included in the snapshot.

For example, some actions (like `actions/setup-node`) download tools and configure `PATH` to find them. The downloaded tools persist in the snapshot, but the `PATH` changes don't. When a new job starts from the custom image, it's a fresh environment with default environment variables. The tools are present but can't be found without the `PATH` updates.

To restore the environment variables, include the action in both workflows:

* In the image build workflow, the action downloads the tool and bakes it into the filesystem.
* In the workflow that uses the custom image, the action detects the tool is already on disk, skips the download, and only sets up the environment variables.

Steps that install tools with `apt-get install` don't need to be in both workflows because they install to system paths that are already on `PATH` by default.

## Full example

Build the image once in a dedicated workflow. Run the workflow again whenever dependencies change. Reference the custom image in your regular CI workflows.

For example, create `.depot/workflows/build-ci-image.yml`:

```yaml
on:
  workflow_dispatch:

jobs:
  build-image:
    runs-on: depot-ubuntu-latest
    steps:
      - name: Install dependencies
        run: |
          sudo apt-get update
          sudo apt-get install -y \
            python3 python3-pip \
            nodejs npm \
            postgresql-client
      - name: Install Python packages
        run: pip3 install pytest boto3 requests
      - uses: depot/snapshot-action@v1
        with:
          image: xk7m4hnp2q.registry.depot.dev/my-ci-image:latest
```

Merge the new workflow into your default branch. Run `.depot/workflows/build-ci-image.yml` once to build the custom image.

Reference the image in your regular CI workflow. For example, in `.depot/workflows/ci.yml`:

```yaml
on:
  push:
    branches: [main]
  pull_request:

jobs:
  test:
    runs-on:
      size: 2x8
      image: xk7m4hnp2q.registry.depot.dev/my-ci-image:latest
    steps:
      - uses: actions/checkout@v4
      - run: pytest tests/
      - run: npm test
```

Run `.depot/workflows/build-ci-image.yml` whenever dependencies change to keep the custom image up-to-date.

## Best practices

**Set `clean: false` when pre-cloning a repository**: If your custom image includes a pre-cloned copy of your repository, set `clean: false` on `actions/checkout` so it skips running `git clean -ffdx` before fetching. Without `clean: false`, checkout removes untracked files from the pre-cloned repo (like installed dependencies or build artifacts), negating the benefit of pre-cloning.

```yaml
steps:
  - uses: actions/checkout@v4
    with:
      clean: false
```

## For AI Agents

The full site index is at [llms.txt](https://depot.dev/llms.txt). Append `.md` to any documentation, blog, changelog, or customer URL to fetch its markdown source directly.