How to Separate Database Migrations From App Startup During Container Rollouts

Eva Wong is the Technical Writer and resident tinkerer at ZimaSpace. A lifelong geek with a passion for homelabs and open-source software, she specializes in translating complex technical concepts into accessible, hands-on guides. Eva believes that self-hosting should be fun, not intimidating. Through her tutorials, she empowers the community to demystify hardware setups, from building their first NAS to mastering Docker containers.

Duplicate database migrations are easier to prevent when schema changes run as one explicit deployment step instead of inside every app container startup.

The preventive design is to give migrations one owner, one credential set, and one completion signal before new replicas begin serving traffic. Keep web and worker containers free to restart without acquiring schema-change privileges, make the rollout wait for a successful migration job, and design schema changes so the old and new app versions can briefly coexist. That removes the race condition instead of merely hoping every replica notices the same migration history first.

Remove Migration Commands From Normal App Startup

Inspect the image entrypoint, Compose command, worker command, health wrapper, and deployment script for automatic migration calls. The application should be able to restart without mutating the schema unless that restart is the deliberately chosen migration step.

An Octopus deployment article argues that migrations need a separate lifecycle rather than being coupled to every microservice process start.

Keep the migration binary available where needed, but do not invoke it from both the web entrypoint and a second job. One explicit owner is easier to audit than several startup paths that all depend on framework locking.

Run One Pre-Deploy Migration Job

Create a one-shot job that uses the same migration files as the release and exits successfully only after the target database reaches the expected schema state. Make the application rollout depend on that result.

A current rollout guide shows how one job runs before rollout instead of letting every replica race at startup.

Do not scale the migration task like the app service. The job should have one execution owner per target database, a bounded timeout, logs, and a clear failed state that blocks the new app version.

Gate Application Startup on Migration Success

Keep new web and worker containers waiting until the migration stage reports success, but do not make each waiting container rerun the migration itself. The dependency is on the result, not on executing the schema change again.

Andrew Lock’s deployment pattern uses app pods wait for migration while the migration logic remains centralized.

For a small home-server stack, the same principle can be implemented with a dedicated Compose service and a controlled deploy script. Keep the mechanism simple enough that a failed migration visibly stops the rollout.

Use Backward-Compatible Schema Changes During Overlap

Rolling deployments can temporarily run old and new application versions against one database. Avoid a migration that removes or renames a field before the old version has stopped using it.

A recent zero-downtime migration guide recommends expand before you contract so additive schema work lands before destructive cleanup.

Split large changes into expand, backfill, switch, and contract phases when necessary. The migration job should not create a schema that only the new container understands while old replicas are still serving requests.

Keep Migration Credentials Out of App Replicas

Use a database account with schema-change privileges only for the one-shot migration stage when practical. Normal app containers should keep the narrower read and write permissions needed for application data.

A Liquibase deployment article describes database changes belong in automation with controlled, repeatable change application.

This separation makes accidental migration execution less likely even if an app process is restarted or duplicated. Store the elevated credential in the deployment secret path rather than the ordinary long-running service environment.

Verify the Rollout Cannot Apply the Batch Twice

Test the deployment in a disposable database or restored snapshot by starting multiple app replicas, restarting them, and rerunning the deployment command. The migration stage should report the existing schema state without changing it a second time.

JetBrains summarizes the operational rule as run migrations as deployment step before normal application startup.

The prevention policy is complete when app restarts cannot mutate schema, one failed migration blocks the release, and repeated rollout execution leaves the database unchanged. The related ZimaSpace article on duplicate migration diagnosis is the recovery branch if duplicate execution has already occurred.

Support & Tips

More to Read

Get More Builds Like This

Stay in the Loop

Get updates from Zima - new products, exclusive deals, and real builds from the community.

Stay in the Loop preferences

We respect your inbox. Unsubscribe anytime.