Versioning and compatibility
Planeon releases are versioned with a single vMAJOR.MINOR.PATCH number
(for example v1.4.2) that ties together the git tag, the six published
container images, and the OpenAPI contract for that release. This page is
the policy those numbers are governed by: what changes at each level, how
the image tags behave, and how the API and database schema stay compatible
across an upgrade.
Semantic versioning
- MAJOR bumps for breaking changes — anything an upgrade can't absorb without a deliberate step on your part (a removed API field, a config variable that no longer works the way it used to).
- MINOR bumps for backward-compatible feature additions.
- PATCH bumps for backward-compatible bug fixes.
Pre-release versions append a hyphenated suffix, e.g. v1.0.0-rc1 — see
Pre-releases below.
Container image tags
Every tagged release publishes six multi-arch (linux/amd64 +
linux/arm64) images to Docker Hub under the planeon org — api,
worker, web, gateway, migrate, guacd. Each image gets one or more
tags, depending on whether the release is a pre-release:
| Tag | Published for | Behavior |
|---|---|---|
vX.Y.Z | every tagged release, including pre-releases | Immutable. Always the exact build for that version; never overwritten. |
vX | releases only, not pre-releases | Moving. Repointed to the newest release within major X each time a new minor or patch ships. |
latest | releases only, not pre-releases | Moving. Repointed to the newest release overall. |
Pre-releases
A version tag that contains a hyphen (v1.0.0-rc1, v2.1.0-beta2, …) is a
pre-release. Pre-releases publish only the exact vX.Y.Z image tag —
they never move vX or latest, so nothing tracking a moving tag can ever
land on a release candidate by surprise. The corresponding GitHub Release is
also marked as a prerelease.
What production should pin
Production deployments pin the exact vX.Y.Z tag — this is what
deployments/production/compose.yaml does via its PLANEON_VERSION
variable — rather than tracking vX or latest. An upgrade should be
something you choose and test, not something that happens the next time a
container restarts and re-pulls a moving tag.
API compatibility
The OpenAPI contract (api/openapi/openapi.yaml) is backward-compatible
for the entire lifetime of a major version. Within v1.x:
- Existing endpoints, fields, and enum values keep their meaning.
- New endpoints, fields, and enum values may be added.
- Nothing already published is removed or renamed.
Field and endpoint removals or renames wait for the next major version. A
client built against v1.0.0's contract keeps working, unmodified, against
every later v1.x release.
Database migrations
Migrations are append-only and roll-forward-only: once a migration file
ships in a release, it is never edited or deleted, and there is no down
migration to reach for. CI enforces this on every pull request — a change
that edits or deletes an already-shipped migration file under migrations/
fails the build.
Migrations are also never applied automatically by the api or worker
processes at startup. Upgrading applies pending migrations as its own
explicit step (docker compose run --rm migrate), gated before the service
images are rolled. See Upgrading for the
full backup → migrate → gate → roll sequence, and its
Rollback section for how to undo an
upgrade — by restoring the pre-upgrade backup, since there is no
down-migration to fall back on.
Never re-tag a release
A published vX.Y.Z — the git tag and the matching image tags alike — is
permanent. If a release ships with a bug, fix it and publish a new patch
version; never force-push the git tag or rebuild and re-push the same image
tag with different content. Anything pinned to vX.Y.Z, in production or
anywhere else, needs to be able to trust that it never silently changes
underneath it.