Migrate a legacy Docker Compose installation
Use this guide when the existing installation has adocker-compose.yml with
services such as app, db, and minio, or when it has historically been
updated with docker compose pull.
This is a one-time layout migration. It is not the same as a routine upgrade of
an installation that already uses docker-compose.prod.yml and
./casebender upgrade.
Do not continue without a tested PostgreSQL restore and a verified attachment backup. Never run./casebender init, replace the existing.env, rotate an existing encryption or audit key, or rundocker compose down -v.
What changes
Docker Compose prefixes named volumes with the Compose project name. Keeping
the same installation directory and project name normally lets the signed
bundle reuse
pgdata and redis_data. A different directory or -p value
creates different volume names and can make the application appear empty even
though the original data still exists.
Phase 1: Inventory the existing installation
Run these commands from the existing installation directory:- the Compose project name and installation directory;
- the exact CaseBender image tags;
- the PostgreSQL image and major version;
- the actual volume names mounted at
/var/lib/postgresql/data,/data, and/app/apps/web/app/secret; - whether attachments use MinIO, local storage, or an external provider;
- the current database user, database name, and internal hostname;
- the current TLS and reverse-proxy configuration.
.env, license keys, encryption keys, database URLs, or backup
contents into tickets or chat.
Phase 2: Create and test the recovery set
PostgreSQL
Create a logical backup using the legacydb service:
Configuration, TLS, and secrets
.env;- TLS certificates, private keys, and custom trust stores;
- the exact license secret and license blob;
- field and credential encryption keys;
- webhook, connector-signing, OAuth, authentication, and audit secrets;
- integration configuration and proxy/egress settings;
- the current Compose file and image inventory.
LICENSE_SECRET_KEY is absent from .env, preserve the existing value
before stopping app:
LICENSE_SECRET_KEY entry during the
environment transformation. Do not print it.
Attachments
IfSTORAGE_PROVIDER=minio, back up the bucket through the S3/MinIO API. A
tarball or direct copy of MinIO’s internal miniodata layout is a disaster
recovery snapshot, not a valid local-storage migration.
Keep both:
- a snapshot or archive of the original
miniodatavolume; and - an object-level export made with
mc mirroror your approved S3 backup process.
Phase 3: Check alert-promotion compatibility
The new release enables alert-to-case promotion by default. Keep it explicitly disabled during a legacy-data migration:- the promotion table is missing;
- any dual-parent observable exists; or
- backfill verification is not clean.
Phase 4: Choose the attachment target
Option A: Continue using an external MinIO or S3-compatible service
This minimizes application-level storage change. The object store must remain reachable from the newweb container. Set:
MINIO_ENDPOINT is a hostname, not a URL. Use a trusted TLS endpoint for
production. The signed bundle does not run an embedded MinIO container, so an
external endpoint must exist before cutover.
Option B: Migrate MinIO objects to local /data
Create the target volume with the same Compose project name:
mc mirror or an equivalent object-level export to write each bucket object
under the same object key in casebender_data. Do not copy MinIO’s internal
volume files directly. Set:
miniodata backup through the rollback window.
Phase 5: Prepare .env for the signed bundle
Start from the existing .env; do not start from .env.example and do not run
./casebender init.
The release’s
./casebender upgrade copies
CASEBENDER_RELEASE_VERSION and CASEBENDER_REGISTRY from the verified
release.env, and backfills missing audit and integration secrets. Never
replace an existing value during a routine migration.
Update NO_PROXY for the new internal names, including postgres, redis,
web, api, worker, and other enabled services.
Phase 6: Rehearse the complete migration
Restore copies of PostgreSQL, attachments,.env, and secrets into an isolated
network. Use the same PostgreSQL major version as the source.
In rehearsal:
- verify and extract the signed bundle;
- preserve the intended Compose project name;
- apply the
.env, TLS, database-hostname, Redis, storage, and license changes; - run preflight;
- start the pinned release;
- verify migrations and service health;
- compare baseline database and attachment counts;
- validate login, cases, alerts, tasks, attachments, evidence, credentials, integrations, audit writes, and backups;
- complete the alert-promotion backfill checks before enabling promotion;
- record the actual recovery point and recovery time.
Phase 7: Production cutover
- Announce a maintenance window and stop inbound integrations and user writes.
- Take fresh final PostgreSQL and attachment backups.
-
Preserve the legacy Compose file as
docker-compose.legacy.yml. - Verify and copy the new bundle files into the same installation directory.
-
Preserve
.envand apply the reviewed transformation. -
Install trusted TLS files at:
-
Stop the legacy stack without deleting volumes:
-
Run:
-
Keep
ENTERPRISE_ALERT_PROMOTION=disableduntil post-migration schema and backfill verification passes. Then remove the override or set it toenabled, recreate the caller services, and test a non-critical promotion.
Phase 8: Validate and close the migration
Confirm:- every expected container is healthy;
- the deployment remains
ACTIVEand existing users can sign in; - baseline users, organizations, cases, alerts, tasks, audit records, and attachment counts match;
- representative attachments and evidence download correctly;
- stored integration credentials still decrypt and a safe connection test succeeds;
- Redis, worker queues, and the alert-promotion outbox are healthy;
- updating a non-critical alert creates an audit entry without an integrity error;
- creating and merging a non-critical alert into a case succeeds after promotion is enabled;
- the deployment can produce a new backup.
.env,
database dump, object-store backup, and volume snapshots until the approved
rollback window closes.
Rollback
Before starting the signed bundle, restart the legacy Compose file against the untouched volumes if rehearsal or preparation fails. After database migrations run:- use
./casebender rollback --confirm-schema-compatibleonly when the release notes explicitly permit image-only rollback; - otherwise stop the new stack, restore the matching pre-migration PostgreSQL
and attachment backups, restore the legacy
.envand Compose file, and start the previous pinned images.