Terraform for the devbox platform: pre-warmed cloud dev boxes claimed through a
control plane. This repo provisions the AWS infrastructure. The application code it
runs lives in the sibling repo
smoketurner/devbox, checked out alongside
this one as ../devbox.
smoketurner/devbox is a Rust Cargo workspace (../devbox/crates/) that produces
three binaries:
| Crate | Binary | Role |
|---|---|---|
devbox-cli |
devbox |
user-facing CLI (claim/connect to a box) |
devbox-agent |
devbox-agent |
on-host agent baked into the golden AMI |
devbox-server |
devbox-server |
the control-plane service |
This repo consumes those build artifacts; it does not build them:
release.yml(onv*tags) publishes thedevbox-agentbinary to GitHub Releases (devbox-agent-{aarch64,x86_64}-unknown-linux-musl+SHA256SUMS).image-builderdownloads the arm64 binary and bakes it into the golden AMI (var.devbox_agent_url, default thelatestrelease — resolved to a concrete tag at build time and always verified against that release'sSHA256SUMS; a versioned URL pins the release).deploy.yml(on push tomain) builds thedevbox-servercontainer image and pushes it to the ECR repo created bycontrol-plane, which then rolls the ECS service.
The two builders are chained through SSM Parameter Store: each publishes the id of what it produced, and the next stage resolves it. Nothing is hardcoded.
smoketurner/devbox devbox-infra (this repo)
────────────────── ────────────────────────
release.yml
└─ devbox-agent ──────▶ image-builder ──[/devbox/ami/latest]──────────┐
(GitHub Release) (golden AMI) │
│ ├─▶ pool ──▶ running
▼ AMI │ (warm dev box
snapshot-builder ─[/devbox/workspace-snapshot/─┘ ASG)
(warm /workspace) latest]
deploy.yml
└─ devbox-server ─▶ ECR ─▶ control-plane (ECS) ──▶ sets pool desired capacity
| SSM parameter | Producer | Consumers |
|---|---|---|
/devbox/ami/latest |
image-builder | snapshot-builder, pool |
/devbox/workspace-snapshot/latest |
snapshot-builder | pool |
An EC2 Image Builder pipeline on Amazon Linux 2023 (arm64/Graviton). Components install
the toolchains (Go, Rust, Node 22, Python, uv, Docker), pre-pull container images,
download devbox-agent to /usr/local/sbin/devbox-agent, and install the systemd units
and SSH config that drive the agent on a running box:
devbox-warmup.service→devbox-agent warmup(freshen/workspace, self-tagdevbox:ready=true)devbox-owner-sync.service→devbox-agent owner-sync(provision the claimant's login account)- SSHD
AuthorizedPrincipalsCommand→devbox-agent principals %u(per-claim auth against Vouch CA certs)
Publishes the resulting AMI id to /devbox/ami/latest.
Launches a throwaway builder from the golden AMI, formats a data volume, and runs
devbox-agent checkout (see scripts/clone-warm.sh) to clone the configured repos
source-only into /workspace and run each repo's .devbox/warm.sh hook — seeding the
toolchain caches so a fresh box starts warm. Snapshots the volume and publishes the
snapshot id to /devbox/workspace-snapshot/latest. Runs on a schedule.
An Auto Scaling Group whose launch template resolves the golden AMI
(resolve:ssm:/devbox/ami/latest) and attaches the latest workspace snapshot as
/workspace. Instances run the baked agent (warmup → devbox:ready=true). The control
plane sets desired capacity.
An ECS Fargate service running devbox-server from ECR, behind an NLB with TLS. Handles
auth (Vouch OIDC), claims, and reconciles the pool ASG desired capacity.
The shared VPC, subnets, and routing the other modules build on.
image-builder, snapshot-builder/scripts/clone-warm.sh, and the on-box systemd units
all call devbox-agent subcommands and rely on the DEVBOX_GITHUB_* env vars the agent
reads. A new agent binary reaches running infra only through a new golden AMI, so a
change in ../devbox that infra depends on must roll out in order:
- Merge the agent change in
smoketurner/devbox→release.ymlpublishes the binary. - Run the
image-builderpipeline →/devbox/ami/latestupdates to an AMI with it. - Only then land the infra change.
Landing infra first breaks the next build/boot (a new subcommand is command not found;
a renamed env var silently disables auth). See CLAUDE.md.
.
├── environments/ # Per-environment Terraform roots (reference modules/)
│ └── dev/
├── modules/
│ ├── image-builder/ # golden AMI (EC2 Image Builder)
│ ├── snapshot-builder/# warm /workspace EBS snapshot
│ ├── pool/ # warm-box Auto Scaling Group
│ ├── control-plane/ # devbox-server on ECS Fargate
│ └── vpc/ # shared networking
├── Makefile
└── README.md
- Standard Terraform file split per module:
main.tf,variables.tf,outputs.tf,locals.tf,data.tf,iam.tf,versions.tf, etc. - Shell scripts under
modules/*/scripts/run via SSM run-command with no shebang (AL2023/bin/shis bash). Lint withshellcheckandshfmt -i 2.
Each environment root references the shared modules/. The Makefile wraps the common
Terraform commands; its targets assume AWS_PROFILE=dev-admin and default to ENV=dev.
make fmt # terraform fmt -recursive
make validate # fmt + terraform validate
make plan
make apply