Configuring Remote Docker Builder Profiles
A builder profile pins a name, vCPU size, architecture, and disk to one Docker builder VM. Field settings, the API calls that manage it, and session costs.
A remote Docker builder profile is one configuration record with four fields, a name, a vCPU count, an architecture, and a disk size, and it maps to one dedicated builder VM whose persistent layer cache sits on that disk. Create a profile once in the dashboard or with POST /builder-profiles, then every job that passes the same profile-name reaches the same VM and the same cache.
This guide covers the build symptoms that trace back to a profile field rather than to a Dockerfile, what each field controls and where the catalog caps it, the API calls that create, read, and reset a profile with their request shapes, and a session-minute model comparing one shared profile against several dedicated ones. For the wider picture start at Docker builds on GitHub Actions, and per-minute rates are on the pricing page.
Diagnosis
Five failures look like build problems and are profile settings. Read the profile back before you edit any YAML.
| Symptom | Profile field | What to check |
|---|---|---|
exec format error on a multi-platform build | architecture | The profile has to carry both architectures. A multi-arch build runs one builder instance per architecture. |
| Every build cold after a workflow edit | name | The cache belongs to the profile. A renamed or retyped profile-name reaches a different profile and an empty cache. |
| Builds crawl when the team pushes together | cpu | The documented recommended minimum is roughly 8 vCPU and 16GB memory per concurrent build job. |
| Hit rate falls as branches accumulate | storage | One disk holds the layer cache for every image built on that profile. |
| First build after a quiet stretch runs cold | last_used_at | The builder cache has a TTL of 10 days, and an unused profile resets automatically. |
Read the profile with a name filter, which is faster than paging the whole list:
curl -s -H "Authorization: Bearer $WARPBUILD_API_KEY" \
"https://api.warpbuild.com/api/v1/builder-profiles?name=api-amd64&per_page=30&page=1"The response carries a builder_profiles array plus page, per_page, total_items, total_pages, and next_page. Compare the cpu, architecture, and storage values in that array against the table above before you conclude that the Dockerfile is at fault.
Fix
Set the four fields against the workload rather than against a default. Each one has a hard constraint from the builder catalog.
| Field | Type | What it controls | Constraint |
|---|---|---|---|
name | string | The value the workflow passes as profile-name | Has to match the workflow input character for character |
cpu | integer | vCPU on the builder VM, and therefore the per-minute rate | 16, 32, and 64 on every architecture; 96 and 192 are amd64 only |
architecture | string | Which architectures the profile can build | arm64 and multi-arch profiles cap at 64 vCPU |
storage | integer | Disk in GB holding the persistent layer cache | Catalog pairs: 100GB at 16 vCPU, 200GB at 32 and 64, 600GB or 2TB at 96 and 192 |
Size cpu from peak concurrency rather than from average load, at the documented recommended minimum of roughly 8 vCPU and 16GB per concurrent build job. Four builds that regularly overlap on one profile want the 32 vCPU, 64GB size.
The workflow side is one input. Nothing else changes when you move a build to a different profile:
jobs:
build:
runs-on: warp-ubuntu-latest-x64-2x
steps:
- uses: actions/checkout@v4
- uses: Warpbuilds/build-push-action@v6
with:
context: .
push: true
platforms: linux/amd64,linux/arm64
tags: ghcr.io/acme/api:${{ github.sha }}
profile-name: api-multiarch
timeout: 600000Two notes on the job runner. This job belongs on a Linux runner, since macOS runners do not support nested virtualization and cannot run Docker. On a runner WarpBuild does not operate, add api-key: ${{ secrets.WARPBUILD_API_KEY }} so the action can request the builder assignment.
Configuration
Create an API key with the ci and cache scopes first, as described in the automation documentation, and export it as WARPBUILD_API_KEY.
Create a profile with the four fields in the request body:
curl -s -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $WARPBUILD_API_KEY" \
-d '{"name":"api-amd64","cpu":32,"architecture":"amd64","storage":200}' \
https://api.warpbuild.com/api/v1/builder-profilesThe response is the stored profile. These are the fields worth keeping:
| Response field | Use |
|---|---|
id | Path segment for the read, cache reset, and delete calls |
name | The profile-name value your workflows pass |
cpu, storage | The size that sets the per-minute rate and the cache disk |
architecture | Whether a multi-platform build will run or fail |
last_used_at | How close the profile is to the 10 day cache TTL |
created_at, updated_at, organization_id | Ownership and audit trail |
Read one profile back by id:
curl -s -H "Authorization: Bearer $WARPBUILD_API_KEY" \
"https://api.warpbuild.com/api/v1/builder-profiles/$BUILDER_PROFILE_ID"Reset the cache on that profile when a base image change leaves a large volume of dead layers, or when the disk is close to full. The call takes an optional force query parameter and returns a success message:
curl -s -X POST \
-H "Authorization: Bearer $WARPBUILD_API_KEY" \
"https://api.warpbuild.com/api/v1/builder-profiles/$BUILDER_PROFILE_ID/cache/reset"DELETE /builder-profiles/{builder_profile_id} removes the profile. Wait for every build on it to finish first.
One shape of the API matters for planning. It offers create, list, read, reset cache, and delete, and no update call, so changing cpu or architecture through the API means creating a second profile and switching profile-name to it. The new profile starts empty, so make that switch where one cold build is acceptable. Remote Docker builders sit alongside snapshot runners, CI observability, an MCP server, and the Action Debugger in the WarpBuild product surface, and each of those has the same style of endpoint in the API reference. The action inputs, the bake action, the CLI flow, and the session billing rules are in the remote Docker builders documentation.
Cost or Time Model
The comparison that decides profile layout is one shared profile against several dedicated ones, measured in builder session minutes. The arithmetic below runs on stated assumptions rather than measurements, so replace each number with your own before acting on it.
- Five service images, 400 image builds per month across all five.
- A warm build occupies its builder for 4.0 minutes, and the builder is assigned 0.5 minutes before the build step, so a build on its own holds a session for 4.5 minutes.
- Overlapping builds on one profile are billed as one session, from the first job start to the last job completion, with starts staggered by 1.0 minute.
- A session covering
noverlapping builds therefore runs 4.5 + (n - 1) minutes, which is 1 + 3.5/n builder minutes per build.
| Option | Profile size | Rate per minute | Builds per session | Session minutes per month | Monthly cost |
|---|---|---|---|---|---|
| A. One shared profile | 32 vCPU, 64GB, 200GB | $0.12 | 4 | 750 | $90.00 |
| B. Five dedicated profiles | 16 vCPU, 32GB, 100GB | $0.06 | 2 | 1,100 | $66.00 |
Option A merges more builds into each session and still costs more, because the size it needs to hold four concurrent builds bills at twice the rate. Setting the per-build figures equal gives the crossover: option B costs 2.75 builder minutes at $0.06, or $0.165 per build, and option A matches when 1 + 3.5/n equals 1.375, which lands at about nine builds per session. The shared profile has to merge roughly nine builds into every session before it draws level with five dedicated 16 vCPU profiles at two.
Sizing pushes that crossover further out. At the recommended minimum of roughly 8 vCPU per concurrent build job, nine overlapping builds want more than 64 vCPU, and 64 vCPU bills at $0.24 per minute. A shared profile kept in size for its peak concurrency gives back the rate advantage it was chasing. See concurrent Docker builds on GitHub Actions for how to measure real overlap, and can two jobs share one Docker builder for what sharing does to a single build.
Two facts push the other way. A shared profile keeps one cache, so five images built from the same base layers reuse each other's work, while five profiles each pay the base layer cost once. Disk works the same way in reverse: five 100GB profiles hold 500GB of layers in total against 200GB on the shared profile, so the split option starts evicting later as branches accumulate. Split profiles when images have different base layers, and share when they do not.
Then measure. GET /reports/billing/docker-builder takes start_date, end_date, and a profiles filter, and returns per-session billing rows plus a summary for the range, which replaces the assumed session counts above with real ones.
Rates here are the builder profile list rates from the pricing page and the remote Docker builders documentation, checked on 2026-08-13, and the full size table is on the remote Docker builder catalog page.
FAQ
Can I change the size of an existing builder profile?
The builder profile API exposes create, list, read, reset cache, and delete. There is no update call, so a change to cpu or architecture through the API means creating a second profile and pointing profile-name at it. The second profile starts with an empty cache, so schedule the switch where one cold build is acceptable.
Does every builder profile keep its own layer cache?
Yes. A profile maps to one dedicated builder VM and the cache lives on that VM's disk, so two profiles never share layers. Every job that passes the same profile-name reaches the same cache, whichever repository it runs in.
How do I find out how many builder session minutes a profile used?
Call GET /reports/billing/docker-builder with start_date and end_date, and filter by profile. The response carries a session list and a summary for the range, which is the measured replacement for the assumed session counts in any cost model.
Start with $10 in free credits
Change the runner label in your workflow and keep the rest of your GitHub Actions setup. Runner time is billed per minute.