Skip to content

Migrating to calServer 6.0

6.0 is a version and consolidation cut: the version number catches up with the name the documentation has been using all along, and the version spaces across all layers converge on a single source. Nothing is removed functionally. The legacy teardown is marked and scheduled — see Deprecations.

Background and rationale: ADR-012.


First: back up

Required, not a formality. A rollback via /admin/updates restores the containers, not the database schema. Crossing a major boundary without a backup leaves you with no way back for the data.

# PostgreSQL (V2 default)
docker exec calserver-postgres \
  pg_dump -U calserver calserver_v2 > backup-before-6.0.sql

# MySQL (V1 and transitional installations)
export MYSQL_PWD="$(sed -n 's/^DB_ROOT_PASSWORD=//p' /var/calserver/.env | head -1)"
docker exec -e MYSQL_PWD mysql_db \
  mysqldump --single-transaction --routines --triggers \
  -u root calserver > backup-v1-before-6.0.sql
unset MYSQL_PWD

Database names and users come from the host .env: PG_DATABASE / PG_USERNAME for V2 (without an entry, calserver_v2 and calserver apply), DB_DATABASE for V1. The MySQL container is named mysql_db.

Do not fall back to -p for the mysqldump

Without -i, docker exec attaches no stdin. The password prompt of -p then receives nothing, mysqldump connects with an empty password and reports Access denied ... (using password: NO). -it is the wrong way out: the pseudo-terminal puts CRLF line endings into the redirection, and the dump looks right but is broken on restore.

Hence -e MYSQL_PWD without =value — Docker takes the value from the calling environment, so the password never appears in the host's process list. --single-transaction gives a consistent snapshot without locking the running V1 (all tables are InnoDB); it is the same flag set V2's own backup run uses (app/Services/Backup/DatabaseDumper.php).

Then check the last line:

tail -1 backup-v1-before-6.0.sql   # must be "-- Dump completed on ..."

If the disk fills up during the dump, a plausibly sized file is still there, just without an end. And be able to restore the backup, not just take it: replay it once in a test environment before it matters.


Order

  1. V2 first, via /admin/updates. The updater recreates calserver-api-v2 and calserver-frontend; migrations run automatically on container start.
  2. V1 afterwards, via deploy.sh on the host. The updater deliberately leaves the V1 containers alone, so after step 1 there is a version offset. The admin UI shows the running V1 tag.
cd /var/calserver
./update.sh          # pull the V1 image
./deploy.sh          # recreate the V1 containers

The major gate

From 6.0 on, crossing a major boundary no longer runs as an ordinary update. On /admin/updates the button reads "Prepare major update" instead of "Update", followed by two steps:

  1. Read the migration notes and the releases between the running and the target version, along with the notes on the V1 offset and the rollback limit.
  2. Confirm the backup and type the target version. Only then can the update start.

Technically both sides require the confirmation independently: the updater sidecar answers 409 without confirm_major, the V2 API answers 422.

Fleet-wide switch

ALLOW_MAJOR_UPDATE in the host .env Effect
unset (default) major update requires the confirmation in the UI
true major update runs without a prompt (unattended fleets)
false major updates are blocked, even when confirmed

false is the emergency stop for managed fleets: customers see 6.0 but cannot apply it until the operator releases it.


Beta channel

Releases now go through the beta channel first. To test 6.0 before the stable release, set this in the host .env:

UPDATE_CHANNEL=beta

The beta channel accepts tags with a -beta or -rc suffix in addition to stable ones. A beta system should run a real semver tag as V2_IMAGE_TAG (v5.64.922), not develop-latest — otherwise the running version cannot be determined and the UI explicitly marks the comparison as imprecise.

Update ALLOWED_TAG_PATTERNS on existing systems

If you set ALLOWED_TAG_PATTERNS yourself in the host .env, extend the pattern with the pre-release suffix, or the check finds a beta tag and applying it fails with 403:

ALLOWED_TAG_PATTERNS=^v\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$|^(develop|release|master|beta)-latest$|^latest$

Without a custom entry the new default applies and there is nothing to do.


Behavioural changes

Small, but visible to API clients:

What Before From 6.0
meta.api_version on 17 endpoints "2.0" "2.0.0" (from config/api.php, as everywhere else already)
GET /admin/information/version → app_version "2.0.0" (from composer.json) product version, e.g. "6.0.0"
GET /app/version → app_version product version unchanged
dcc:release in the DCC XML export always "1.0.0" product version
Version log update_logs — new field is_major

The API contract is unchanged. The route prefix /api/v2 does not move with the product major, and meta.api_version only rises when the contract itself changes. Existing integrations need no adjustment.


What 6.0 does not do

  • Nothing is removed. V1CompatResponse, the X-REST header auth, the MD5 password path and HasFieldMapping are marked deprecated and scheduled for 7.0 — see Deprecations.
  • V1 (Yii) is not switched off. The sunset ahead of the Yii 1 EOL on 2026-12-31 is a separate step.
  • There is no database switch as part of the update.

If something goes wrong

  1. Rollback via /admin/updates: every completed or failed history entry offers the way back to the previously running tag. The way back across the major boundary is deliberately not blocked.
  2. Restore the schema: the rollback brings the containers back, not the schema. That is what the backup is for.
  3. If the update hangs in "applying", check on the host:

    docker compose ps calserver-updater
    docker compose logs --tail=100 calserver-updater
    

Details on the update system: Update guide.