Reproducibility and upgrades
A workbench should be reproducible, the same yml building the same container every time, and the features inside it should not be weeks out of date. Those two goals pull against each other, so Monoceros splits them along one line: the yml.
Where your yml names a version, you decided, and nothing moves it behind your
back. Where it names none, there is nothing to hold on to, so apply keeps that
part current for you.
New to Monoceros? Start with Installation.
Two layers, kept current differently
Section titled “Two layers, kept current differently”- The runtime base image (Debian, Node, system tooling) is pinned in the yml and moves only when you upgrade. That is what makes a workbench reproducible.
- The features carry no version in your yml, because a pinned cloud client
goes wrong on its own as the API moves. They are brought up to date on every
apply, before you get a shell. - The service images (Postgres, Keycloak, Redis) sit in between. Your yml
carries the tag, so you can see it and change it, and
upgrademoves it to the one the catalog ships today. That is how a security fix in an upstream image reaches a workbench you built months ago. Anapplyleaves it alone, because your data lives there.
Agent plugins work like the features: they live outside the image in ~/.claude,
and every apply pulls their marketplace again and updates them.
Why the base is pinned
Section titled “Why the base is pinned”Without a pin, the base image follows a floating tag and is re-derived on every
apply. A routine apply - adding a repo, tweaking a port - could then pull a
newer image and change the toolchain under a container that already holds real
work. That is silent, non-reproducible drift.
So init writes the current runtime version into the yml, version only:
schemaVersion: 1name: acmeruntimeVersion: 1.8.0apply builds against exactly that version. The same yml yields the same
container on any machine.
Why features need refreshing at all
Section titled “Why features need refreshing at all”A feature installs its tool at build time, and that lands in a cached image
layer. The cache key is the install instruction (install the latest), not the
version it resolved to, so once the layer is built the version is frozen. Every
later apply builds from that layer, and every new workbench with the same
features reuses it, even months later. This is not specific to Monoceros or to
dev-container features; it is how layer caching works for any build-time
install.
The second half of it costs more. An apply recreates the container from the
image, so a tool that updated itself while you worked loses that update and
drops back to the build-time version. Without a refresh, a re-apply would take
away capabilities you had been using the day before.
So each of those features checks itself when the container starts, and installs
only when there is something newer. The usual apply costs a version lookup and no
download, and apply tells you what it found:
Features refreshed • claude 2.1.247 (updated from 2.1.240) • gh 2.60.1 (updated from 2.55.0)Nothing here can fail your apply. With no network it says so and carries on with the tool from the image.
What upgrade is for
Section titled “What upgrade is for”applykeeps your features current and otherwise builds from the cache. It never bumps the runtime and never moves a service image on an existing workbench.monoceros upgrademoves the parts that hold state or sit underneath everything: it bumps the runtime base when a newer one exists, rebuilds the feature layers from scratch, brings the services up to the catalog’s images, and prunes the stale images it replaced.
monoceros upgrade acmeThe staleness reminder
Section titled “The staleness reminder”The features keep themselves current, but the base image and the service images
wait for you to say so. apply prints a one-line reminder when the last
upgrade was a long time ago:
Base image and service images last updated 34 days ago. Run `monoceros upgrade` to move them.The threshold is 30 days by default; set upgrade.staleDays in
monoceros-config.yml to change it.
Keeping the CLI itself current
Section titled “Keeping the CLI itself current”That reminder is about what is inside a workbench. The monoceros CLI
binary is a separate thing, and it keeps itself honest too: commands check
(at most once a day, in the background, never blocking) whether a newer
version is published, and print a one-line notice when there is one:
⬆ Monoceros 1.31.0 is available (you have 1.30.0). Update: curl -fsSL https://raw.githubusercontent.com/getmonoceros/workbench/main/installer/install.sh | bashUpdating is the same install script you used the first time - it re-runs the
checks (Docker, Node, completion) and pulls the new CLI. To silence the
notice, set MONOCEROS_NO_UPDATE_NOTIFIER=1 in your shell. It is also quiet
automatically in non-interactive contexts (CI, pipes).
Scope of a global upgrade
Section titled “Scope of a global upgrade”monoceros upgrade with no name refreshes every running workbench. Stopped
or never-built ones are skipped, so an upgrade never starts a container you had
stopped or builds a config you only init-ed. Name one to refresh it
explicitly: monoceros upgrade <name>.
See also
Section titled “See also”monoceros upgrade- the command reference.monoceros apply- the reproducible build, with current tools.