DBAtlas · Database operations atlas

GitOps & blue-green / zero-downtime deployment

Symptom

Goal: Deploy schema and application changes without downtime; approach zero downtime with expand-contract migrations, blue-green environments, feature flags and automatic rollback.

1. GitOps: make Git the single source of truth

Define infrastructure, schema migrations and the application version in Git; deployment is triggered by a merge to the repository (GitOps). A reconciler (Argo CD/Flux or CI/CD) applies the desired state to the real environment. Every change is then auditable, repeatable and can be reverted to a previous commit when needed.

PostgreSQL

# repo/
#   migrations/  (Vxxx__*.sql)
#   k8s/         (Deployment, Service)
#   flags/       (feature flag config)

2. Change the schema backward-compatibly (expand-contract)

For zero downtime, do not change the schema in one breaking step; split it into two phases. EXPAND: add the new structure (new column/table) so that old and new code can run at the same time (nullable column, with default, dual writes). AFTER the application has moved to the new structure, CONTRACT: drop the no-longer-used old column/table in a separate migration. Old and new application versions then run side by side without problems during deployment.

PostgreSQL

-- EXPAND (V5): backward-compatible addition
ALTER TABLE customer ADD COLUMN email text;

-- for a while the code writes/reads both columns

-- CONTRACT (V9): drop the old column
ALTER TABLE customer DROP COLUMN mail_old;

3. Put risky behavior behind a feature flag

Run the new code path (such as reading the new column) conditionally behind a feature flag; turn the flag on when the schema is ready and off without touching the deployment if something goes wrong. A flag separates code deployment (deploy) from feature activation (release). The expand phase ships, but the behavior change is enabled in a controlled way and can be reverted quickly.

PostgreSQL

-- application layer (pseudocode)
-- if flag('new_email_field'):
--     SELECT email FROM customer ...
-- else:
--     SELECT mail_old FROM customer ...

4. Set up the blue-green environment

Keep two identical environments: 'blue' (live) and 'green' (new version). Deploy the new application version to green and validate it before it receives real traffic; when ready, switch the router/traffic to green. Key point: the database is usually SHARED, so the schema must be compatible with both versions (expand-contract ensures this). Even though the connection target changes at cutover, schema compatibility means no downtime.

PostgreSQL

# traffic is switched to green (new version)
# shared DB: schema must be compatible with both blue and green

5. Validate the deployment with monitoring (canary/observation)

Before switching all traffic, monitor metrics such as error rate, latency, database error/lock counts and replication lag; optionally send a small percentage of traffic (canary) to green and observe. If health indicators are good, increase traffic gradually; if bad, do not proceed. Monitoring is the input to the automatic rollback decision.

PostgreSQL

-- live health queries
SELECT count(*) FROM pg_stat_activity WHERE wait_event_type='Lock';
SELECT * FROM pg_stat_replication;

6. Prepare automatic rollback

If metrics exceed the threshold, the deployment must roll back automatically: in blue-green, quickly switch traffic back to blue (the old version). Application rollback is instant because blue is still running. On the schema side, rollback is usually NOT NEEDED: thanks to expand-contract the new schema is also compatible with the old code, so rolling back the application is enough. That is why schema changes should stay independent of application deployment and always be backward compatible.

PostgreSQL

# traffic instantly returns to blue (application rollback)
# schema is not rolled back: expand-contract is compatible with old code
# if truly needed: write a new corrective migration
Open in DBAtlas →