GitHub Codespaces Prebuilds: Faster Starts, Real Costs, and the Gotchas Teams Miss
GitHub Codespaces prebuilds can cut cold-start latency from minutes to seconds, but they introduce Actions costs, secret injection gaps, and regional limits. Here's how to evaluate whether they pay off for your team.
14 Sept 2025, 04:26 UTC

The Cold-Start Problem
You push a branch, open a Codespace, and wait. Two to five minutes later the terminal finally shows your prompt — dependencies installed, build artifacts cached, post-create scripts done. Multiply that by every developer, every branch switch, every morning. The latency is real, and it adds up.
GitHub Codespaces prebuilds promise to shrink that wait to 10–30 seconds by building a ready-to-use image on every push. The mechanism is straightforward: a GitHub Actions workflow runs on a hosted Linux runner (4 vCPU, 16 GB RAM), executes your devcontainer.json lifecycle up to postCreateCommand, and pushes the resulting image to the GitHub Container Registry (ghcr.io). When a developer creates a Codespace on a matching branch, region, and devcontainer hash, Codespaces pulls that image instead of building from scratch.
How Prebuilds Actually Work
Prebuilds are triggered by push (default) or on a schedule. The workflow runs only the initializeCommand, onCreateCommand, and postCreateCommand hooks — postStartCommand and postAttachCommand are skipped because they require a running container and user session. Secrets from the repository or organization are not injected during the prebuild; they become available only at Codespace creation time. This means any npm token, cloud credential, or private registry password referenced in postCreateCommand will cause the prebuild to fail.
Images are stored per region (East US, West Europe, Southeast Asia as of 2024). A developer in an unsupported region falls back to an on-demand build, losing the latency benefit. The prebuild image includes the base image layers, installed packages, and any artifacts produced by postCreateCommand (compiled binaries, generated code, downloaded dependencies).
Configuration Essentials
Enable prebuilds in .devcontainer/devcontainer.json:
{
"name": "My Project",
"image": "mcr.microsoft.com/devcontainers/base:ubuntu-22.04",
"features": {
"ghcr.io/devcontainers/features/node:1": { "version": "20" }
},
"postCreateCommand": "npm ci",
"prebuild": {
"enabled": true,
"trigger": "push"
}
}
For monorepos with multiple devcontainer files, use a matrix via the configurations array (set at the organization or repository level in the Codespaces prebuild settings). Each configuration entry points to a distinct devcontainer.json path and gets its own prebuild image.
Set "updateOnPush": true (default) to rebuild when the devcontainer hash changes. If you prefer nightly builds to avoid rebuild storms from frequent dependency updates, change trigger to "schedule" and define a cron expression in the prebuild policy.
Worked Example: Measuring the Difference
Create a test repository with the devcontainer.json above. Push to main. In the Actions tab you’ll see a workflow named “Codespaces Prebuild”. Once it succeeds, an image appears at ghcr.io/<owner>/<repo>/codespaces/<hash>.
Now measure cold-start time before and after the prebuild is ready. Run this from a machine with the GitHub CLI (gh) authenticated and Codespaces access:
# Create a Codespace on a branch that has NO prebuild yet (e.g., a fresh feature branch)
gh codespace create --repo owner/repo --branch feature-no-prebuild --machine basicLinux32gb --idle-timeout 5m
# In the opened terminal, note the timestamp when postCreateCommand finishes:
date +%s.%N
# Exit and delete that Codespace
gh codespace delete --codespace --force
Repeat the same steps on main (which has a prebuild). Compare the timestamps. Expect a drop from ~180 seconds to ~20 seconds for a typical Node.js project with npm ci. The gh codespace create command requires codespace scope token and write access to the repository. The --idle-timeout 5m avoids leaving a running instance; adjust as needed.
Cost and Adoption Trade-offs
Prebuilds consume GitHub Actions minutes (Linux runner at ~$0.008/min) and package storage (~$0.005/GB-month). A 5 GB image rebuilt on every push to a busy branch can cost more than on-demand builds if the team creates fewer than ~5–10 Codespaces per day per branch. Check the breakdown in Settings → Billing → GitHub Codespaces → Prebuilds.
Common adoption patterns that work:
- Enable prebuilds on the default branch and long-lived release branches.
- Use
trigger: "schedule"with a nightly cron for feature branches that change infrequently. - Pin base images (e.g.,
ubuntu-22.04digest) and group Dependabot/Renovate PRs to limit devcontainer hash churn. - Monitor the Prebuilds dashboard (Codespaces → Prebuilds) for success rate, storage growth, and compute minutes.
Limitations You’ll Hit
- Secrets absent at prebuild time: Workaround — bake non-sensitive defaults in the image, inject real secrets at Codespace creation via Codespace secrets (user or repo level).
- No GPU or ARM runners: Prebuilds run only on Linux x64 hosted runners. If your devcontainer requires GPU or ARM, you’ll fall back to on-demand builds.
- Regional mismatch: Verify developer locations against
gh api /user/codespaces/prebuilds --jq '.prebuilds[].region'. Unsupported regions negate the benefit. - Organization policies: Spending limits, allowed images, or disabled Actions can block prebuild workflows. Confirm admin settings before rollout.
Actionable Next Steps
- Pick one repository with a stable
devcontainer.jsonand moderate Codespace usage (5+ creates/day). - Add the
prebuildblock withtrigger: "push"and push tomain. - Watch the Actions workflow complete and note the image size in ghcr.io.
- Run the cold-start measurement commands above on a branch without prebuild and on
main. - Check the Prebuilds dashboard after a week: compute minutes, storage, success rate.
- If the break-even looks favorable and no secret/region blockers appear, expand to other long-lived branches. For feature branches, switch to scheduled nightly prebuilds.
Prebuilds are a lever, not a free lunch. They shift cost from developer wait time to Actions minutes and registry storage. Measure first, then decide.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.