main, your CI builds a fresh VM with the latest code, dependencies, and migrations applied. It moves my-app:latest to point at this new build. Everyone on the team runs one command to get a fully warm dev environment that matches trunk.
This guide shows how to wire that up end to end.
Why this pattern
Without it, every developer’s first interaction with a fresh checkout is:- Clone the repo
- Install system dependencies (maybe a specific Postgres version, a specific Node, a specific Python)
- Run migrations
- Seed a dev database
- Warm caches
- Realize you’re missing a native library
- Debug
The moving pieces
Prerequisites
- Vers CLI installed on the CI runner (or the Vers SDK in whatever language CI uses)
- A Vers API key stored as a CI secret
- A first commit to seed the repo (see below)
Step 1: Seed the repo (one time)
Do this once, locally. Get a VM fully set up for your app (code cloned, deps installed, DB migrated, caches warm) and commit it.Step 2: Write the CI update script
Every timemain changes, CI runs roughly this. The script is plain bash so it translates cleanly to GitHub Actions, GitLab CI, Buildkite, whatever.
If starting from a fresh root VM is closer to what you want (say, you changed a system package), swap step 1 for
vers run --vm-alias "$BUILD_LABEL" and run a full bootstrap in step 2. The pattern is otherwise identical.Step 3: Team members consume it
One command:main built, migrations applied, caches warm. No apt-get. No “did you install libpq?”. No “have you rebased?”.
Rollback
A bad build that somehow made it past smoke tests doesn’t require a new CI run; just movelatest back:
vers repo tag delete.
Scaling considerations
A few things to decide as this workflow grows:- How long to retain dated snapshots. Each commit uses real storage. Keep a sliding window of recent builds (maybe 7 days) and a handful of blessed historical versions (
v1.0,v2.0, quarter-end snapshots). - Whether to run CI builds in parallel for different configurations. Every combination that matters gets its own repo or its own tag.
my-app:latest-x86,my-app:latest-arm64.my-app:staging,my-app:production. - Who can push to
latest. All org members can update any tag in the org’s repos right now; there’s no per-tag ACL. Lock down which API keys CI uses and protect them like you would a deploy key. - Whether to publish. If your team includes contractors with separate Vers orgs, making the repo public and having them
vers repo forkis the mechanism. The fork is copy-on-write so it’s still seconds.
Hybrid with a supported image
If your dev VM is really “a supported pi-agent or language image plus our repo,” you can fork once at seed time and let CI take it from there:my-app:latest exactly the same as if you’d hand-bootstrapped it.
See Also
- Repositories and Tags - the full model
vers repo tag updatevers execute- run commands on a VM without SSH- Forking a supported image - seed your repo from a Vers-maintained one