Skip to main content

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:

TagPublished forBehavior
vX.Y.Zevery tagged release, including pre-releasesImmutable. Always the exact build for that version; never overwritten.
vXreleases only, not pre-releasesMoving. Repointed to the newest release within major X each time a new minor or patch ships.
latestreleases only, not pre-releasesMoving. 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.