Database migrations & stateful safety
Safely back up and migrate a persistent database (MySQL/Postgres) volume around a deploy — using Openship's pre-deploy backup trigger and post-deploy jobs.
If a service in your project has a Docker volume (MySQL, Postgres, Redis, Qdrant…), two things about deploys are worth understanding:
- A container's
docker-entrypoint-initdb.dscripts only run on first initialization — i.e. when the volume is empty. Once the volume has data, those scripts never run again, so you cannot rely on them to apply a schema migration to an existing database. - On a full-stack deploy, a stateful container may be recreated. Its named volume is preserved (the data survives), but recreate is still a high-stakes moment for production.
Openship gives you two building blocks to make this safe — a backup before the deploy and a migration after it — plus a way to avoid touching the database at all.
Don't touch the database on an unrelated change
The simplest safety measure: only deploy the services that changed. A backend code change shouldn't recreate your database container.
openship deploy --service-ids api,webServices you don't name carry forward on their existing containers, untouched. See Compose / multi-service.
Back up before every deploy (rollback safety net)
Create a backup policy for the stateful service and enable its “trigger on pre-deploy” option. By default, Openship waits for every backup run to succeed before building or starting the new release, including when a previous build is reused. This keeps startup migrations from overlapping the dump's database locks. The existing deployment remains in place while the backup runs.
- Set it up in Backups (see Backups & restore); pick an S3/volume destination and turn on the pre-deploy trigger.
- Deployment logs show each backup's status, elapsed time and uploaded bytes, with progress updates every 30 seconds. Long backups continue automatically; Stop deployment remains available.
- Once all backup workers have finished cleanup, a failed backup opens the deployment decision dialog: Retry backup retries the failed sources; Continue without backup explicitly bypasses them for this deployment; Stop deployment ends the attempt. A policy that could not start uses the same dialog. The decision and backup progress remain in the deployment logs. Skipping does not disable the policies or turn a failed run into a restore point.
- Without a response, the dialog times out and stops deployment (five minutes by default, configurable
with
OPENSHIP_PROMPT_TIMEOUT_MS). A missing/unreadable backup or a worker still running at the six-hour ceiling stops the deploy without offering a bypass: its database locks may still be held. - Runs are grouped as
pre_deployin backup history. Project policies wait for every service's run. - PostgreSQL dumps must exit successfully. A dump smaller than 1% of the median of up to three recent comparable successful backups is rejected before it becomes a restore point. Comparisons use the same policy, destination, service, database and compression format. If a reduction of more than 99% is intentional, verify the database and create a new policy to establish a fresh baseline. This size check complements regular restore testing; it does not replace it.
Run a migration after the deploy
For applying a schema/data migration to an existing volume, use a Job that fires when the deploy finishes:
- Go to Jobs (see Scheduled jobs) and create a command job.
- Set its trigger to the “Deploy succeeded” event.
- Point the command at your migration tool. Examples:
Postgres (dbmate / Flyway / plain psql):
# a one-shot migrator container on the same network as your db service
docker run --rm --network <project-network> \
-e DATABASE_URL="postgres://user:pass@db:5432/app" \
ghcr.io/amacneil/dbmate:latest upMySQL (apply a versioned file):
docker run --rm --network <project-network> \
mysql:8 sh -c 'mysql -h db -u user -ppass app < /migrations/0007.sql'Order of operations
The safe sequence is: pre-deploy backup (automatic, if the policy toggle is on) → new version deploys →
post-deploy migration job runs against the now-updated volume. If the migration fails, restore the
pre_deploy snapshot from the backup history.
What Openship does NOT do (yet)
There is no built-in gating pre-deploy migration step that blocks the app from starting until a
migration completes — the post-deploy job runs after the new container is live. If your app strictly
requires the schema to be migrated before it boots, run the migration as its own deploy step in your image
(e.g. an entrypoint that runs migrate then exec the app) or keep the app tolerant of a brief
migration window. A first-class gating pre-deploy hook is on the roadmap.
Connect to Cloud & migrate
Link the copy of Openship you run yourself to Openship Cloud, move a single project up or bring it home, and (at a high level) open your instance up to a whole team.
Teams & members
Invite other people into your Openship workspace, give them the right role, and understand the difference between your personal space and a shared team.