Upgrades
Brutor upgrades are deliberately boring: change one version pin, pull, restart. The control plane migrates the database schema automatically and safely; your job is to pin versions, read the release notes, and keep a rollback point.
How versioning works
Section titled “How versioning works”Every service image — core proxy, control plane, Admin Console, Portal, KB services, skill runner — is published under the same tag, pinned by a single variable:
BRUTOR_VERSION=0.9.68All components in one deployment should run the same version. Mixed versions mostly work across small gaps (the proxy reads whatever schema the control plane has migrated to), but they are not a supported steady state — treat a version skew as a transient condition during rollout only.
Before upgrading, read the release notes for every version between yours and the target on the component repositories’ GitHub releases pages — schema-touching and behavior-changing releases are called out there.
Who touches the schema (and who never does)
Section titled “Who touches the schema (and who never does)”- The control plane owns the schema. On startup it runs Alembic migrations automatically, guarded by a PostgreSQL advisory lock — so multiple workers or instances starting simultaneously don’t race; one runs the migrations, the rest wait. Fresh, legacy and already-managed databases are detected and handled.
- The core proxy never alters the schema. It only reads (config) and writes rows (telemetry, usage) into tables the control plane created.
This gives you the safe ordering rule: upgrade and start the control plane first, let it finish migrating, then roll the core proxy and the UIs. In a single-host Compose deployment the dependency ordering in the bundle handles this; when you orchestrate services independently (ECS, Kubernetes), sequence the control plane deployment ahead of the proxy.
Upgrade procedure (Docker Compose)
Section titled “Upgrade procedure (Docker Compose)”-
Snapshot the database volume. Migrations are forward-only — this is your rollback point (see below).
Terminal window docker compose stopdocker run --rm \-v brutor-postgres-data:/data \-v "$(pwd)/backups:/backup" \alpine tar czf /backup/postgres-$(date +%Y%m%d-%H%M%S).tar.gz -C /data .(A
pg_dumpwhile the stack is running works too; the volume snapshot is the belt-and-braces option before major upgrades.) -
Bump the pin.
.env BRUTOR_VERSION=0.9.69 -
Pull and restart.
Terminal window docker compose pulldocker compose up -dThe control plane boots, takes the advisory lock, applies any pending Alembic revisions, then serves traffic; the other services start against the migrated schema.
-
Verify.
Terminal window docker compose logs control-plane | grep -i alembic # migrations applied cleanlycurl http://localhost:8100/health # {"status": "healthy", ...}curl http://localhost:5050/healthThen log into the Admin Console and confirm Mission Control shows fresh traffic.
Rolling back
Section titled “Rolling back”The supported rollback is: restore the pre-upgrade database snapshot and the previous BRUTOR_VERSION together.
docker compose downdocker volume rm brutor-postgres-datadocker volume create brutor-postgres-datadocker run --rm \ -v brutor-postgres-data:/data \ -v "$(pwd)/backups:/backup" \ alpine tar xzf /backup/postgres-YYYYMMDD-HHMMSS.tar.gz -C /data# .env: BRUTOR_VERSION back to the previous versiondocker compose up -dAnything written between the snapshot and the rollback (usage rows, config changes) is lost — which is why you snapshot immediately before upgrading, not nightly-and-hope.
Upgrading on ECS / Kubernetes
Section titled “Upgrading on ECS / Kubernetes”The same logic, expressed in your orchestrator:
- Snapshot the database (RDS/Aurora snapshot).
- Deploy the control plane with the new image tag; wait until it’s healthy (migrations complete — the advisory lock makes concurrent instances safe).
- Deploy the core proxy, then the UIs and KB services.
- Roll back = restore the DB snapshot + redeploy the previous tag, together.
