Skip to content

Container Images

Hawk doesn't publish prebuilt images for its services. Every pulumi up builds them from your checkout on your default Docker builder1 and pushes them to your stack's own ECR repositories.

Using an AI agent? Point it at the agent reference.

What happens during a deploy

flowchart LR
    subgraph local["Your machine"]
        S2["<b>2. Check for changes</b>"]
        S3["<b>3. Build</b><br/><i>default Docker builder</i>"]
    end
    subgraph aws["AWS, your stack's Region"]
        S1["<b>1. Create repositories</b>"]
        S4["<b>4. Push to ECR</b>"]
        S5["<b>5. Switch services</b><br/><i>ECS · Lambda · Batch · EKS</i>"]
    end
    NET["<b>Internet</b><br/><i>base images · GitHub</i>"]

    S1 --> S2 --> S3 --> S4 --> S5
    NET --> S3
  1. Create repositories. Pulumi creates an ECR repository for each image in your stack.
  2. Check for changes. Pulumi hashes each image's files and Dockerfile. If nothing changed, it keeps the image already in ECR.
  3. Build. Changed images build at the same time on your default Docker builder, for hawk:cpuArchitecture (arm64 unless you change it). Each build pulls its base images, installs dependencies2 and copies in its code.
  4. Push to ECR. Each image is pushed as :latest, together with a build cache that makes the next deploy faster.
  5. Switch services. ECS, Lambda, Batch and Kubernetes refer to images by digest, so they only change when an image's content does.

How long it takes

Step First deploy (cold cache) Later deploys (warm cache)
1–2. Repositories and change check Seconds Seconds
3–4. Build and push, per image 1.5–3 min in the stack's Region4; up to 35 min from home5 15 s–3.5 min3
5. Switch services Varies with ECS rollouts Varies
Whole pulumi up 30–45 min; longer from home5 2–8 min3

Images

Image Runs as Notes
api ECS service Also runs database migrations
runner, janitor Kubernetes
middleman, relay ECS services Both on by default
viewer-static ECS service Built with your stack's domain and login settings
eval-log-importer, sample-editor Batch jobs
token_broker, job_status_updated, scan_importer Lambda Lambda needs the image in ECR in the same Region
eval_log_reader, jumphost Lambda, ECS Optional

Things to know

  • The first deploy is the slow one. A new stack starts with an empty cache, so every layer is built and uploaded.
  • The build machine needs disk space and upload bandwidth. If yours is short on either, see step 5 of the deploy guide.
  • Pick your Docker host before the first deploy. Switching it later (with DOCKER_HOST) can make Pulumi replace every image, and a replace can delete an image your services still use.

  1. Your machine, unless you set up a remote one. Registry logins are read on the machine running Pulumi, even when the builder is remote. ↩

  2. Most Python images use uv sync; the Lambdas use uv pip install, the viewer pnpm, and the jumphost apk. Some also clone METR's pinned inspect_ai or inspect_scout fork from GitHub. ↩

  3. METR's own deploys on GitHub's arm64 runners, building arm64 images with the GitHub Actions cache, October 2026. ↩↩

  4. A 4-vCPU x86 EC2 instance in the stack's Region, building amd64 images (hawk:cpuArchitecture: amd64) as a remote builder with an empty cache. ↩

  5. An x86 laptop running Docker under WSL2 on home fibre, building amd64 images. Several builds failed and had to be retried. Building for a different architecture than the build machine is much slower; see Build host architecture. ↩↩