Labels icon
WorkingDirectory icon
Clone icon
CLI icon
Docker icon
If icon
Log icon
Exit icon
Commands icon
Query icon
Pause icon
Assert icon
Webhook icon

Deploy Liquibase changesets on a Git push, after a rehearsal, an approval and post-deployment checks with automatic rollback

Rehearse Liquibase changesets and their rollback on a scratch copy, approve the SQL, apply to Postgres, run checks, roll back to a tag on failure.

Categories
Infrastructure

A Liquibase changelog in Git is usually deployed by a CI job that runs liquibase update and hopes. This flow deploys it the careful way, on every push that touches the changelog: it rehearses the pending changesets on a scratch copy of the production schema, including their rollback, shows an approver the exact SQL, tags production, applies the changesets, runs your post-deployment checks against the real data, and rolls back to the tag if a check fails.

This blueprint was created by kruxarth.

How it works

  1. on_push (Webhook) receives the Gitea push event. Only pushes to branch with a commit that touches changelog_dir start an execution. The commit SHA becomes the commit input; the repository URL always comes from git_url.
  2. clone checks out that commit, and plan runs liquibase update-sql against production. The script, a copy without Liquibase's bookkeeping, and the changelog files are kept with the execution, so every later step uses exactly the files that were rehearsed.
  3. Nothing pending: the execution ends with outcome: up-to-date.
  4. rehearsal_copy recreates the scratch database from the production schema and the databasechangelog rows (no business data). rehearse runs liquibase update-testing-rollback there: apply, roll back, apply again. rehearsal_checks runs your check query on the result.
  5. show_plan logs the SQL, and approve (Pause) waits for APPLY or REJECT and a note. REJECT ends in WARNING; no answer within approval_window cancels the execution.
  6. rollback_point picks the tag, tag_prod runs liquibase tag, and update_prod runs liquibase update.
  7. applied reads the changesets recorded after the tag, checks runs your check query on production, and verify (Assert) fails unless every rehearsed changeset was executed and no check returned a row.
  8. Any failure after the tag runs liquibase rollback --tag in the errors branch, then checks that no changeset is left after the tag, and labels the execution outcome: rolled-back.

What you get

  • A changeset that fails, or whose rollback block fails, is caught on the scratch copy before anyone is asked to approve.
  • The approver sees the SQL that will run, and the decision and note stay in the execution.
  • Checks on real data after the apply. A changeset that runs fine but leaves wrong data, or fails halfway on production data, is rolled back to the tag automatically.
  • Labels outcome, commit, pushed_by, pending and rollback_tag on every execution.

Who it's for

  • Platform, DBA and backend teams that keep a Liquibase changelog in Git and deploy it to PostgreSQL.
  • Teams that want an approval and a record for production schema changes without building a release pipeline for them.

Why orchestrate this with Kestra

  • The Git server calls Kestra directly, and concurrency (limit 1) queues a second push until the first deployment is finished or rejected.
  • Pause with onResume inputs gives an approval form in the UI and the API, and the errors branch makes the rollback part of the flow, not a runbook.
  • Liquibase, pg_dump and the JDBC checks each run in their own container or plugin, and the whole process is one readable YAML file.

Prerequisites

  • Docker on the Kestra host with its socket available to Kestra. The task containers use the host network, so localhost is the Docker host.
  • A changelog repository with db/changelog.yaml and db/verify.sql: one SQL query whose every row is a failed check, for example SELECT 'orders with a negative total' AS failed_check, count(*) FROM orders WHERE total_cents < 0 HAVING count(*) > 0. Use UNION ALL to add checks.
  • The production PostgreSQL user that owns the schema (prod_user), and a scratch server where scratch_user has CREATEDB. The flow drops and recreates scratch_db on every run.
  • A Gitea (or Forgejo) push webhook, content type JSON, to http://<kestra>/api/v1/main/executions/webhook/company.platform/liquibase-migrate-on-push-with-rehearsal/<MIGRATIONS_WEBHOOK_KEY>. A Kestra on a private or loopback address needs [webhook] ALLOWED_HOST_LIST in Gitea's app.ini.

Secrets

  • MIGRATIONS_WEBHOOK_KEY: the key in the webhook URL. Anyone who knows it can start a deployment of git_url.
  • GIT_TOKEN: a read-only token of the git_username account, to clone the repository.
  • PROD_DB_PASSWORD: password of prod_user.
  • SCRATCH_DB_PASSWORD: password of scratch_user.

Quick start

  1. Add the four secrets, set the variables (git_url, prod_db, scratch_db, users), and save the flow.
  2. Add the webhook in the repository settings, then push a commit that adds a changeset.
  3. The execution pauses. Read the SQL in the show_plan log, click Resume, choose APPLY and add a note.
  4. When it succeeds, the labels show outcome: applied and the tag to roll back to by hand.

How to extend

  • Notify the approvers from onPause, or post the result back to the commit status.
  • Rehearse with data by dropping --schema-only, if your data may be copied.
  • Add --context-filter or --label-filter per environment and call the flow as a subflow for each one.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.