Bite-sized how-to | ~20 min setup
Database schema changes are often done manually — someone runs SQL scripts directly against the database. This approach is risky, hard to track, and difficult to reverse when something goes wrong.
Harness Database DevOps treats your database schema like application code — version-controlled in Git, deployed through a pipeline, and reversible with a rollback. Every schema change is tracked, tagged, and auditable through the Migration State view.
A real-world scenario using the e-commerce products table:
- Apply V1 (Base Schema) — create the
productstable with 4 columns - Apply V2 (Add Column) — add a
discount_percentagecolumn — app starts lagging - Apply V3 (Data Type Change) — change
pricefromDECIMALtoFLOAT— pricing precision lost - Rollback V3 → V2 — revert the data type change, restore price precision
- Rollback V2 → V1 — remove the discount column, restore base schema
Key teaching point: Database and application rollbacks must happen simultaneously to keep them in sync.
DB Schema — a Harness entity that maps to a Git repository containing your changelog file. Defines what schema changes exist and in what order.
DB Instance — connects a DB Schema to an actual database via a JDBC connector. One schema can have multiple instances (dev, staging, prod).
Apply Pipeline — runs the DBSchemaApply step to deploy pending changesets to the database. Liquibase tracks which changesets have already run.
Rollback Pipeline — runs the DBSchemaRollback step to revert the database to a previous tagged state.
Migration State — the Harness view showing every deployed changeset with its deployment tag. Use this to find the rollback tag.
Rollback Tag — to rollback a changeset, find the row below it in Migration State and copy its tag. This is the database state before that changeset was applied.
dbdevops-tidbits-apply-rollback/
├── .harness/
│ ├── apply-pipeline.yaml — Apply Schema pipeline
│ └── rollback-pipeline.yaml — Rollback Schema pipeline (tag as runtime input)
├── sql/
│ └── V1__create_products_table.sql — base schema (SQL file)
├── changelog.yml — Liquibase changelog (includes SQL + 2 YAML changesets)
└── k8s/
└── postgres.yaml — PostgreSQL deployment on Kubernetes
- A Harness account with the Database DevOps module enabled
- A Kubernetes cluster with a Harness delegate running inside it
- A GitHub connector pointing to this repository
kubectlinstalled and configured to connect to your cluster
kubectl create namespace dbdevops-demo
kubectl apply -f k8s/postgres.yaml
kubectl get pods -n dbdevops-demoVerify the pod is in Running state.
- Go to Project Settings → Connectors → + New Connector → JDBC
- Name:
postgres-dbdevops - JDBC URL:
jdbc:postgresql://postgres.dbdevops-demo.svc.cluster.local:5432/appdb - Username:
appuser - Password:
apppassword - Delegate: select your delegate
- Test and save
- Go to Database DevOps → DB Schemas → + Add New DB Schema
- Name:
ecommerce-products-schema - Migration Type: Liquibase Compatible
- Click Continue
- Source: Connect to External Changelog
- Connector: your GitHub connector
- Click Continue
- Path to Root Changelog:
changelog.yml - Click Add Schema
- In the DB Schema, click + New
- Name:
ecommerce-db-instance - Branch:
main - Connector:
postgres-dbdevops - Click Add New DB Instance
Apply Pipeline:
- Go to Database DevOps → Pipelines → + Create a Pipeline
- Use the YAML from
.harness/apply-pipeline.yaml - Update the placeholders:
| Placeholder | Value |
|---|---|
YOUR_PROJECT_ID |
Your Harness project identifier |
YOUR_ORG_ID |
Your Harness org identifier |
YOUR_DOCKER_CONNECTOR |
Your Docker Hub connector identifier (used to pull the Harness Liquibase plugin image) |
YOUR_K8S_CONNECTOR |
Your Kubernetes connector identifier |
YOUR_DB_SCHEMA_ID |
The DB Schema identifier (e.g., ecommerceproductsschema) |
YOUR_DB_INSTANCE_ID |
The DB Instance identifier (e.g., ecommercedbinstance) |
Rollback Pipeline:
- Create a second pipeline using
.harness/rollback-pipeline.yaml - Update the same placeholders
- The
tag: <+input>means Harness will prompt for the rollback tag each time you run it
Run 1 — Apply V1 (Base Schema)
- Run the Apply pipeline
- Verify:
kubectl exec -it <postgres-pod> -n dbdevops-demo -- psql -U appuser -d appdb -c "\d products" - Expected: 4 columns (id, name, price DECIMAL, stock_quantity)
Run 2 — Apply V2 (Add Column)
- Run the Apply pipeline again
- Expected: 5 columns (+ discount_percentage)
Run 3 — Apply V3 (Data Type Change)
- Run the Apply pipeline again
- Expected: price column is now
double precision(FLOAT)
Run 4 — Rollback V3 → V2
- Go to Migration State → find the row below
change-price-to-float→ copy its tag - Run the Rollback pipeline → enter that tag → Run
- Expected: price reverts to
DECIMAL(10,2), discount_percentage remains
Run 5 — Rollback V2 → V1
- Go to Migration State → find the row below
add-discount-percentage→ copy its tag - Run the Rollback pipeline → enter that tag → Run
- Expected: discount_percentage removed, back to 4 columns