Skip to main content
A common team workflow: on every merge to 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:
  1. Clone the repo
  2. Install system dependencies (maybe a specific Postgres version, a specific Node, a specific Python)
  3. Run migrations
  4. Seed a dev database
  5. Warm caches
  6. Realize you’re missing a native library
  7. Debug
That’s 20 minutes on a good day, much longer when setup docs drift from reality. Branch → commit → tag solves it: CI does that work once per merge, the team pulls the result.

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.
Sanity-check by restoring it:

Step 2: Write the CI update script

Every time main 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:
Or as a shell alias:
Programmatically (CI/CD, scripts, or non-Go SDKs), hit the API directly:
Either way, they land on a VM that already has today’s 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 move latest back:
The old build’s commit is still in place because you created a dated tag for it in step 6. Keep those dated tags as long as you want a rollback window, and delete the oldest ones periodically with 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 fork is 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:
After that first fork, CI treats my-app:latest exactly the same as if you’d hand-bootstrapped it.

See Also