Releases and updates
How the image is built, and how a server moves between versions.
How a build happens
Every push to main runs .github/workflows/publish-image.yml:
- Checks —
npm run typecheckandnpm run lint. A failure publishes nothing. - Build and push to
ghcr.io/reelmza/opencbt, tagged with the full commit sha andlatest. Av1.2.3git tag also publishes1.2.3and1.2. - Prune — old versions are deleted, keeping the newest few, because private package storage on the free plan is 500 MB and an image is around 260 MB.
Layers are cached between runs, so a build that only changes application code takes a few minutes.
Tokens
Each server needs a read-only token to pull:
docker login ghcr.io -u YOUR-GITHUB-USERNAME # paste a read:packages tokenUse a fine-grained token limited to the opencbt package, one per box, so a
school's server can be cut off on its own. GitHub does not accept account
passwords for the registry.
Updating a server
cd ~/deploy # or ~/schools/<name>
docker compose -f compose.onsite.yml pull
docker compose -f compose.onsite.yml up -dThe app applies any database migrations as it starts, then serves. Only the layers that changed are downloaded, so a code-only update is a few megabytes.
Never on an exam morning. A school's .env should pin OPENCBT_IMAGE to a
version you have already run somewhere else:
OPENCBT_IMAGE="ghcr.io/reelmza/opencbt:sha-9f3c1d2..."Rolling back
Set OPENCBT_IMAGE to the previous tag and bring it up again. The limit is the
database: a release that migrated the schema cannot be undone by running the old
image, because the old code does not know the new shape. Two habits keep this
cheap:
- take a backup before updating a school's server;
- read the release's commits for a migration before deciding to update mid-term.
Offline installs
A school with no usable internet can be fed the image by hand:
# on a machine that has it
docker pull ghcr.io/reelmza/opencbt:latest
docker save ghcr.io/reelmza/opencbt:latest | gzip > opencbt.tar.gz
# on the school's server, from a flash drive
gunzip -c opencbt.tar.gz | docker loadThen set OPENCBT_IMAGE to that tag in the school's .env. No docker login
is needed on that box at all.
What is in the image
The app and the worker, the built Next.js app, the production dependencies,
Prisma's migration tooling and the PostgreSQL 16 client (for the backup button).
About 260 MB to download, 1.3 GB unpacked. No credentials: every secret lives in
the .env beside the compose file.
The worker
Both processes run the same image; the worker is the same image started with
worker. It builds exports and takes backups, so a deployment without it shows
results it cannot hand over. Every compose file here already includes it.