>
Home

Latest Post

Database Schema Migrations in 2026 – Survey

What is the best way to manage database schema migrations in 2026?

Since this sort of thing is getting easier with AI tooling, I spent some time doing a survey across a bunch of recognizable multi-contributor open source projects to see how they do database schema change management.

Biggest takeaway: the framework provided by your programming language is the most common pattern. After that seems to be custom project-specific code. Even while Pramod Sadalage and Martin Fowler’s twenty-year-old general evolutionary pattern is followed, I was surprised to see very few occurrences of the specific tools they listed in their 2016 article about Evolutionary Database Design. Those tools might be used behind some corporate firewalls, but they aren’t showing up in collaborative open source projects.

Second takeaway: it should be obvious that we still have schema migrations with document databases and distributed NoSQL databases; but lots of interesting illustrations here of what it looks like in practice to deal with document models and NoSQL schemas as they change over time. My recent comment on an Adam Jacob LinkedIn post:“life is great as long as changing your schema can remain avoidable (ie. requiring some kind of migration).”

What about the method of triggering the schema migrations? The most common pattern is that the application process itself triggers schema migration. After that we have kubernetes jobs.

The rest of this blog post is the supporting data I generated with some AI tooling. I made sure to include links to source code, for verifying accuracy. I spot checked a few and they were all accurate – but I didn’t go through every single project.

If you spot errors, please let me know!! I’ll update the blog.


A survey of how major open-source projects handle database schema migrations. Each project includes a real code example and how migrations are triggered during upgrades.


Kubernetes Migration Trigger Methods

Projects with no official Helm chart or k8s support (Mastodon, Discourse, Sentry, Zulip, NetBox, Metabase, Lemmy, MediaWiki, Matrix Synapse†, CHT Core, Signal Server, Firefox, Chromium, Signal Desktop, FDB Record Layer, RxDB) are omitted.

Trigger MethodProjects
Dedicated k8s Job (Helm hook)GitLab (post-deploy), Airflow (post-install/upgrade), Superset (post-install/upgrade), Temporal (pre-deploy), Kong (pre-install), Jaeger (pre-deploy), ThingsBoard (install only; upgrades require a separate manual pod)
Init container in pod specGitea (official chart runs gitea migrate in init container before main container starts)
App process migrates on pod startupGhost, Backstage, Keycloak, Grafana, Mattermost, Odoo, Parse Server, Appsmith, Rocket.Chat, Graylog
Triggered by action against running processWordPress (first admin HTTP request), Kubernetes (StorageVersionMigration CRD triggers in-cluster controller), Dgraph (POST /admin API call; async index rebuild)
Manual operator actionCalico (calico-upgrade CLI), Neo4j-Migrations (neo4j-migrations migrate CLI), Nextcloud (occ upgrade via exec or Job), Zipkin (SQL DDL applied before deploy), APISIX (no tooling; manual etcd data transformation)
No migration neededCortex (schema versioned in YAML config; new period appended and deployed, old data untouched)

† Matrix Synapse has no official Helm chart from Element; the widely-used community chart (ananace/matrix-synapse) relies on in-process startup migration.


Part 1: Relational

1A. External Frameworks

ProjectsLanguageFrameworkTrigger
GitLab, Mastodon, DiscourseRubyRails ActiveRecordGitLab: dedicated k8s Job (Helm). Mastodon: manual two-phase CLI; no official Helm chart. Discourse: launcher script runs rake db:migrate during rebuild; no official Helm chart.
Sentry, Zulip, NetBoxPythonDjango MigrationsSentry: sentry upgrade CLI (acquires distributed lock; post-deployment migrations must be run separately); official self-hosted is docker-compose only, no official Helm chart. Zulip: scripts/upgrade-zulip script; no official Helm chart, typically deployed on VMs. NetBox: container entrypoint script runs manage.py migrate on container start (netbox-docker); no official Helm chart.
Airflow, SupersetPythonAlembicBoth: dedicated k8s Job as Helm post-install/post-upgrade hook.
Ghost, BackstageJavaScript, TypeScriptKnex.jsBoth: app code calls migration runner on startup. Both have official Helm charts (Bitnami for Ghost, backstage/charts for Backstage); migrations run in-process at pod startup, no separate job.
Keycloak, MetabaseJava, ClojureLiquibaseBoth: app code calls Liquibase on startup. Keycloak: DefaultJpaConnectionProviderFactory; official Helm chart (Bitnami) and k8s Operator exist, auto-migrates at pod startup. Metabase: setup-db! (custom Clojure macros wrap Liquibase changesets); no official Helm chart.
LemmyRustDieselApp code calls run_pending_migrations() on startup (before pool is returned). No official Helm chart; typically deployed via docker-compose.
GiteaGoXORMOfficial Helm chart exists; init container explicitly runs gitea migrate before the main container starts (not relying on auto-migration). AUTO_MIGRATION=false can disable the in-process fallback.
NextcloudPHPDoctrine DBALocc upgrade CLI or web-based updater; not automatic. Official Helm chart exists (nextcloud/helm); init containers only wait for DB readiness. occ upgrade must be run manually (e.g., exec into pod).

1B. Custom Systems

Continue reading

What is Ardent?

ADJECTIVE:
1. Warmth of feeling; passionate
2. Strong enthusiasm or devotion; fervent
3. Burning/fiery or glowing/shining
(American Heritage Dictionary)

Social

As of 2025: I'm on LinkedIn most. Also Slack and Discord but don't have Discord invite links handy. I check Twitter/X on occasion. Haven't been on IRC regularly since the old days, before the PG folks moved to Libera. I've de-supported all other (old) social accounts listed here, but I'll keep them handy for the Zombie Apocalypse.

LinkedIn: linkedin.com/in/ardentperf/
Slack: jer_s@pgtreats.info/slack-invite

Twitter/X: jer_s
IRC: jer_s@FreeNode (#postgresql, #ansible, #oracle, ##oracledb)
AIM, MSN, Google: jeremy.schneider@ardentperf.com
Yahoo: ardentperf
ICQ: 614052660

Disclaimer

This is my personal website. The views expressed here are mine alone and may not reflect the views of my employer.

contact: 312-725-9249 or schneider @ ardentperf.com


https://about.me/jeremy_schneider

oaktableocmaceracattack

(a)

Enter your email address to receive notifications of new posts by email.

Join 77 other subscribers