Java.DBMigrationTools.How do you run a migration tool in a CI/CD pipeline?

Core idea

Treat migrations as a separate, run-once step that runs before (or alongside) the app rollout and is safe to re-run. Use the migration tool’s locking/versioning so only one job changes the schema.

Typical patterns

  1. Dedicated migration job (recommended)
    CI runs a specific job (Flyway/Liquibase/Prisma/Alembic/etc.) using DB creds from secrets. Gate the deploy on success.
  2. Kubernetes Job / Helm hook
    Ship migrations as a discrete container that runs to completion before/with the release (with proper hooks to order it).
  3. App runs migrations on startup
    Fine for small apps/single instance; risky for fleets/shared DBs—avoid in most teams.

Example: GitHub Actions — Flyway (PostgreSQL)





name: deploy
on: [push]
jobs:
  migrate-db:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run Flyway migrations
        run: |
          docker run --rm \
            -v "$PWD/db/migrations:/flyway/sql" \
            -e FLYWAY_URL="${{ secrets.DB_URL }}" \
            -e FLYWAY_USER="${{ secrets.DB_USER }}" \
            -e FLYWAY_PASSWORD="${{ secrets.DB_PASSWORD }}" \
            flyway/flyway:10.18.2 migrate
  deploy-app:
    needs: migrate-db
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      # build & deploy your app here

Example: GitLab CI — Liquibase

stages: [migrate, deploy]

migrate_db:
  stage: migrate
  image: liquibase/liquibase:4.29
  script:
    - liquibase \
        --url="$DB_URL" \
        --username="$DB_USER" \
        --password="$DB_PASSWORD" \
        --changelog-file=changelog/db.changelog-master.yaml \
        --log-level=info update
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'
  variables:
    DB_URL: $PROD_DB_URL
    DB_USER: $PROD_DB_USER
    DB_PASSWORD: $PROD_DB_PASSWORD

deploy_app:
  stage: deploy
  needs: ["migrate_db"]
  script:
    - ./deploy.sh

Example: Kubernetes Job (works with any tool)

apiVersion: batch/v1
kind: Job
metadata:
  name: app-db-migrate
spec:
  template:
    spec:
      restartPolicy: OnFailure
      containers:
        - name: migrate
          image: flyway/flyway:10.18.2
          envFrom:
            - secretRef: { name: app-db-credentials }
          volumeMounts:
            - name: migration-sql
              mountPath: /flyway/sql
          args: ["migrate"]
      volumes:
        - name: migration-sql
          configMap:
            name: app-migrations

Deployment ordering with Helm hooks

# templates/migrate-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: app-db-migrate
  annotations:
    "helm.sh/hook": pre-install,pre-upgrade
    "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
# ...

Rollbacks & zero-downtime

  • Prefer expand/contract:
    • Expand: add new tables/cols (nullable/backfilled), write code compatible with old+new.
    • Deploy app using only backward-compatible reads/writes.
    • Contract: remove old columns in a later release after traffic migration.
  • Keep down/rollback scripts only if your tool/team can truly support backward schema downgrades; otherwise use forward-fixes.
  • For large data changes: run backfills as separate, resumable jobs (chunked, idempotent) rather than inside schema migrations.

Safety checklist

  • Migrations are idempotent and use tool locking (Flyway/Liquibase do this).
  • One pipeline is allowed to run migrations per environment (use environments/branches/protected runners).
  • DB creds via secrets, never in repo.
  • Backups or point-in-time restore configured.
  • Wrap DDL in transactions when supported (e.g., Postgres). For MySQL, be careful with non-transactional DDL.
  • Add runtime gates: run migrations on staging first; promote to prod on success.
  • Monitor: alert on migration failures and long-running locks.

Tool-specific one-liners

  • Flyway: flyway -url=jdbc:postgresql://... -user=... -password=... migrate
  • Liquibase: liquibase --url=... --username=... --password=... update
  • Prisma: npx prisma migrate deploy
  • Django: python manage.py migrate
  • Rails: bundle exec rails db:migrate
  • Alembic (SQLAlchemy): alembic upgrade head
  • Knex: npx knex migrate:latest
This entry was posted in Без рубрики. Bookmark the permalink.