Build and use custom images
Build a reusable custom image from the Depot base image that includes your tools and dependencies.
How it works
You build a custom image in Depot CI 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.
The snapshot action can only be used in workflows running on Depot CI. It's not compatible with GitHub Actions or other CI providers.
Snapshot a sandbox to build a custom image
Create a job that runs on a standard Depot sandbox and installs the tools and dependencies you want to bake in. This is a separate workflow that only creates your custom image and pushes it to the Depot Registry. You'll need to run this workflow initially to create the image and then only when dependencies change.
To create the build image workflow:
-
Add
depot/snapshot-actionas 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 thedepot/snapshot-actionin the format:<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 or use the
depot org listcommand.
Example build image job:
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:latestUse 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 sandboximage: the Depot registry reference for your snapshot, using a tag or a pinned digest
Example specifying a custom image in a job:
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-thingAvailable 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.
Use a custom image for agent runs
Managed Agents runs can boot the same snapshot. Enter its reference in a connection's Sandbox image setting, as described in Sandbox images.
Pin a custom image by digest
Pin runs-on.image to a digest when you want each job to use the exact snapshot you reviewed. Tags such as latest or v1 can point to different snapshots after another push. A digest identifies fixed image content.
First, build a snapshot with depot/snapshot-action. Use a tag in the action's image input, for example <org-id>.registry.depot.dev/my-ci-image:latest. Then replace the tag with the snapshot's digest in jobs that consume it.
To find the snapshot's digest:
- Open Registry and select the repository used in the snapshot action.
- Select the tag you published, such as
latest. - Copy Digest from the Details section. The value starts with
sha256:.
The snapshot action also prints this manifest digest in its final Pushed to ... (sha256:...) log line.
Replace :<tag> with @sha256:<digest> in runs-on.image. For example, save this workflow as .depot/workflows/pinned-image.yml:
on:
workflow_dispatch:
jobs:
use-image:
runs-on:
size: 2x8
image: <org-id>.registry.depot.dev/my-ci-image@sha256:<digest>
steps:
- run: echo "Running from the pinned snapshot"Replace <org-id> with your organization ID and my-ci-image with your snapshot repository. Replace <digest> with the full 64-character hash after sha256: in the copied value.
To adopt a new snapshot, build it, copy its digest, and update runs-on.image in a pull request. Review that change before merging.
Keep a tag on snapshots your workflows still use and check your image retention settings. Digest pinning doesn't prevent a snapshot from being deleted.
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:
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:latestMerge 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:
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 testRun .depot/workflows/build-ci-image.yml whenever dependencies change to keep the custom image up-to-date.
Best practices
Snapshot slow-to-install software: Include software that is slow to install, such as system packages, browser binaries, and language toolchains, in your snapshot. Installing Chrome for Playwright tests and MySQL from apt are good candidates. A package directory can easily be cached, but the system changes an installer makes cannot.
Pre-pull container images: Run docker pull in the snapshot job to include those layers in the custom image, so jobs using service containers or docker compose start without pulling from a registry. Containers still need to start and pass their health checks, as normal.
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.
steps:
- uses: actions/checkout@v4
with:
clean: false