Automating Database Migrations with Flyway and Spring Boot in a CI/CD Pipeline
Ship safe, versioned, zero-downtime database migrations with Flyway and Spring Boot — including PostgreSQL examples, multi-environment handling and a complete GitHub Actions pipeline.
Introduction
Production outages caused by *database* changes outnumber outages caused by *code* changes in most teams I've worked with. Flyway brings the same discipline to schema changes that Git brings to source: versioned, repeatable, auditable, and automated through CI/CD.
Why database migrations matter
- Reproducibility — every environment is built from the same ordered scripts.
- Auditability —
flyway_schema_historyis a permanent record. - Safety — every script runs exactly once, in order, in a transaction.
- Automation — schema changes ship with the code that needs them.
Step 1 — Add Flyway to Spring Boot
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-database-postgresql</artifactId>
</dependency>
spring:
datasource:
url: jdbc:postgresql://db:5432/orders
username: app
password: ${DB_PASSWORD}
flyway:
enabled: true
locations: classpath:db/migration
baseline-on-migrate: true
validate-on-migrate: true
Flyway runs automatically on application startup.
Step 2 — Folder structure
src/main/resources/db/migration/
V1__init_schema.sql
V2__add_orders_status_index.sql
V3__add_customers_email_unique.sql
R__refresh_reporting_views.sql
V<version>__description.sql— versioned, applied once, in order.R__<name>.sql— repeatable, re-applied whenever the file's checksum changes (good for views).U<version>__— undo migrations (paid tier only — don't rely on them; design forward).
Step 3 — A migration
```sql
-- V1__init_schema.sql
CREATE TABLE customers (
id BIGSERIAL PRIMARY KEY,
email TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);CREATE TABLE orders ( id BIGSERIAL PRIMARY KEY, customer_id BIGINT NOT NULL REFERENCES customers(id), total_cents BIGINT NOT NULL, status TEXT NOT NULL DEFAULT 'PENDING', created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); ```
-- V2__add_orders_status_index.sql
CREATE INDEX CONCURRENTLY idx_orders_status ON orders(status);
CONCURRENTLY avoids locking the table — essential for production. It also means the statement can't run in a transaction; configure that script with -- flyway:executeInTransaction=false.
Step 4 — Zero-downtime migration pattern
Never combine schema + code change in one deploy. Use expand / migrate / contract:
1. Expand — add the new column/table. Deploy. Code still uses the old shape. 2. Migrate — backfill data. Deploy code that reads/writes *both* shapes. 3. Contract — drop the old column. Deploy.
Example: renaming orders.total_cents to orders.amount_cents:
-- V10__add_amount_cents.sql
ALTER TABLE orders ADD COLUMN amount_cents BIGINT;
UPDATE orders SET amount_cents = total_cents WHERE amount_cents IS NULL;
-- V12__drop_total_cents.sql (deployed AFTER all instances read amount_cents)
ALTER TABLE orders DROP COLUMN total_cents;
Step 5 — Multi-environment handling
```yaml
# application-dev.yml
spring:
flyway:
locations: classpath:db/migration,classpath:db/dev-seed# application-prod.yml spring: flyway: locations: classpath:db/migration clean-disabled: true # CRITICAL — prevents flyway:clean wiping prod ```
Always set clean-disabled: true for non-dev environments.
Step 6 — Dockerized local Postgres
services:
db:
image: postgres:16
environment:
POSTGRES_DB: orders
POSTGRES_USER: app
POSTGRES_PASSWORD: app
ports: ["5432:5432"]
docker compose up then ./mvnw spring-boot:run — Flyway migrates the schema on boot.
Step 7 — GitHub Actions pipeline
```yaml
name: ci
on: [push]
jobs:
build:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env: { POSTGRES_DB: orders, POSTGRES_USER: app, POSTGRES_PASSWORD: app }
ports: ["5432:5432"]
options: >-
--health-cmd pg_isready --health-interval 5s --health-retries 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with: { distribution: temurin, java-version: 21, cache: maven }
- name: Validate migrations
run: ./mvnw -B flyway:validate
- name: Apply migrations (CI db)
run: ./mvnw -B flyway:migrate
- name: Tests
run: ./mvnw -B testdeploy-prod: needs: build if: github.ref == 'refs/heads/main' runs-on: ubuntu-latest environment: production steps: - uses: actions/checkout@v4 - uses: actions/setup-java@v4 with: { distribution: temurin, java-version: 21 } - name: Migrate production DB env: FLYWAY_URL: ${{ secrets.PROD_DB_URL }} FLYWAY_USER: ${{ secrets.PROD_DB_USER }} FLYWAY_PASSWORD: ${{ secrets.PROD_DB_PASSWORD }} run: ./mvnw -B flyway:migrate - name: Deploy application run: ./deploy.sh ```
Note: migrations run *before* the new application version is deployed, so the schema is always at least as new as the running code.
Common migration mistakes
- Editing an applied V-script — Flyway detects the checksum mismatch and refuses to start. Never edit history; add a new script.
- Long-running
ALTER TABLEin business hours — locks the table. UseCONCURRENTLY, batched updates, or run during a maintenance window. - Mixing DDL and DML in one script on MySQL — DDL auto-commits, so a failure halfway leaves you in a half-applied state.
- Forgetting
clean-disabled— one CLI mistake wipes prod.
Production best practices
- Use a dedicated migration user with only
CREATE/ALTERrights — not the app user. - Run
flyway:migratefrom CI/CD, not from the application on startup, for high-replica deployments. - Keep migrations small — one logical change per V-script.
- Add a
-- @author / @ticketheader so the audit trail is meaningful.
Related tutorials
Architecture
Flyway Migrations in CI/CD
TL;DR
Key takeaways
- Understand the core concepts behind Automating Database Migrations with Flyway and Spring Boot in a CI/CD Pipeline in a production context.
- Apply the patterns to real DevOps & CI/CD systems, not just toy examples.
- Recognize the trade-offs, failure modes, and operational concerns before adopting them.
- Get a clear path to the next step — related tutorials, tools, and reference architectures.
Avoid these
Common mistakes
1. Copy-pasting code without understanding the trade-offs
It's tempting to ship a snippet from a blog post into production, but DevOps & CI/CD patterns only work when the failure modes are understood. Always reason about timeouts, retries, and consistency.
2. Skipping observability from day one
Structured logs, metrics, and traces are not optional. Wire them in before you ship — debugging DevOps & CI/CD systems without them is painful and expensive.
3. Optimizing too early
Premature caching, sharding, or microservice extraction adds operational cost. Validate the bottleneck with real measurements first.
4. Ignoring security defaults
Secrets in env files, open management ports, missing RBAC — these are the most common production incidents. Treat security as part of the definition of done.
Ship it safely
Production best practices
Apply these before promoting Automating Database Migrations with Flyway and Spring Boot in a CI/CD Pipeline to a real production environment.
Scalability
Design DevOps & CI/CD services to scale horizontally. Keep request handlers stateless, push session and cache state to external stores (Redis, the database), and benchmark p95/p99 latency under realistic load before tuning.
Monitoring & Observability
Emit metrics (RED/USE), structured JSON logs, and distributed traces from day one. Wire dashboards and alerts to SLOs you actually care about — error rate, latency, saturation — not vanity metrics.
Logging
Log with correlation IDs, never log secrets or PII, and centralize logs (ELK, Loki, CloudWatch). Use levels deliberately: INFO for state changes, WARN for recoverable issues, ERROR for incidents.
Security
Apply least-privilege IAM, rotate secrets through a vault, validate every input, and patch dependencies on a schedule. For HTTP services, enable TLS everywhere and set sensible security headers.
Testing
Layer unit, integration, and contract tests. Run them in CI on every PR, and add smoke tests post-deploy. For DevOps & CI/CD systems, also run chaos and load tests before a major release.
Reliability & Rollouts
Ship with health checks, readiness probes, graceful shutdown, and a rollback strategy. Prefer canary or blue/green deploys over big-bang releases.
Questions
Frequently asked questions
Is this tutorial up to date?
Yes. This tutorial was last reviewed and updated on May 22, 2026. We revisit popular DevOps & CI/CD tutorials regularly to keep them aligned with current best practices.
What level is this tutorial aimed at?
It is written for working developers with some backend experience. Beginners can still follow along, and senior engineers will find production-grade patterns and trade-off discussions.
Do I need to follow every step in order?
The walkthrough is sequential because each step depends on the previous one. If you only need a specific concept, the table of contents at the top of the article lets you jump straight to that section.
Where can I find the source code?
Code samples are inlined in the tutorial. When a companion repository is published it will be linked at the top of this page.
Go deeper
Further reading
More From the Channel
Follow the full tutorial series on YouTube
The MasterLabSystems channel publishes in-depth, project-based tutorials on Java, Spring Boot, microservices, Docker, Kubernetes, AWS and DevOps — the same topics covered on this site, with full code walkthroughs.
Stay in the Loop
Get the next tutorial in your inbox
next tutorial →
Serverless Java: Spring Boot on AWS Lambda with GraalVM
Related tutorials
CI/CD Pipeline with GitHub Actions and Docker
Build a complete CI/CD pipeline that tests, builds and pushes a Spring Boot Docker image on every push using GitHub Actions.
GitOps with ArgoCD — The Modern Kubernetes Deployment Strategy
A complete, production-grade guide to GitOps with ArgoCD on Kubernetes — workflow, architecture, multi-environment promotion, auto-sync, rollbacks and Spring Boot deployments.
Infrastructure as Code with Terraform — Deploy AWS Resources Like a Pro
Master Terraform for AWS: workflow, state management, modules, VPC + EC2 + RDS + S3, GitHub Actions CI/CD pipeline, security and production best practices.
