Containers Didn't Fix Your Database Problem. They Just Moved It.
Let's be honest about something the DevOps marketing materials glossed over: containerization was a revolution for stateless services. For databases, it was mostly a repackaging job.
That's not a knock on Docker or Kubernetes — those tools are genuinely impressive and solve real problems. But there was a period, roughly 2016 through 2020, where the industry narrative went something like: containerize everything, adopt infrastructure-as-code, and your deployment problems will largely sort themselves out. A lot of teams bought that story. A lot of those same teams are now running PostgreSQL or MySQL in Kubernetes and still feeling vaguely terrified every time a migration hits production.
The fragility didn't go away. It just got new clothes.
What Containers Actually Solved (And What They Didn't)
Containerization genuinely improved deployment consistency for application layers. When your app runs in a container, you get reproducible environments, clean dependency isolation, and a much more reliable path from development to production. Those are real wins.
But databases are stateful. That single word is the source of most of the mismatch between the containerization promise and the database deployment reality.
A stateless service can be killed and restarted without consequence. If a container dies, you spin up another one — same image, same behavior, clean slate. Your database cannot work that way. The data is the whole point. The persistent volume underneath your containerized Postgres instance is carrying years of production history, and no amount of elegant YAML configuration changes that fundamental truth.
Schema migrations don't become safer because they're running in a container. Data loss doesn't become recoverable because your orchestration platform has a health check configured. Locking behavior on large tables isn't affected by whether the database is running on bare metal or inside a Kubernetes pod.
The Illusions That Infrastructure-as-Code Creates
Here's where it gets a little uncomfortable. Infrastructure-as-code is a genuinely good practice — defining your infrastructure in version-controlled configuration files is better than the alternative. But it creates a specific kind of false confidence around databases.
When your database deployment is defined in a Helm chart or a Terraform module, it looks controlled. It looks like the thing is understood and managed. And for the infrastructure layer — the compute, the networking, the storage provisioning — it probably is.
But the data layer? The schema? The migration history? That's almost always a separate concern, handled by a separate tool, often with much weaker guarantees. The Helm chart tells you how to run the database. It doesn't tell you what happens to your 800GB production dataset when you apply a migration that adds a non-nullable column to a heavily-written table.
Teams conflate infrastructure reproducibility with deployment safety. They're not the same thing.
Where Teams Are Still Flying Blind
Migration timing and production load. Containerized or not, most teams still don't have a great answer for how to time schema migrations against real production traffic patterns. The migration runner fires as part of the deployment, the table lock contention spikes, and suddenly the app is timing out. This isn't a container problem — it's a process problem that containers didn't address.
Persistent volume management. Storage in Kubernetes is much better than it used to be, but it's still genuinely complex. Teams running databases in Kubernetes often have shaky answers to questions like: how long does a volume snapshot take, does it affect I/O during the snapshot, and what's the exact recovery procedure if a node fails mid-migration? That uncertainty is fragility.
Rollback in a stateful world. Rolling back a stateless application container is trivial — point to the previous image tag and redeploy. Rolling back a database migration is not trivial, and containerization doesn't make it so. Teams that think their Kubernetes rollback capability extends to their database layer are carrying a dangerous misconception.
Configuration drift between environments. Infrastructure-as-code helps, but database configuration — buffer pool sizes, connection limits, timeout values, replication settings — drifts between environments constantly. A containerized database that performs fine in staging may behave very differently in production because the resource constraints, the storage class, or the network topology is different in ways the YAML doesn't capture.
What Actually Needs to Happen After You've Containerized
If you've already containerized your database layer, or you're planning to, here's what the work looks like after the initial setup is done.
Treat migrations as a first-class deployment concern, separate from application deployment. Your migration runner should have its own pipeline, its own validation steps, and its own rollback procedure. Bundling it into the app deployment because it's convenient is how you get coupling that bites you later.
Build and test your recovery procedures regularly. Not just backups — the full recovery flow. How long does it take? What's the impact on running workloads? Does it actually work? If you haven't run a real recovery drill in the last six months, you don't actually know.
Get honest about your persistent volume story. Document exactly what happens to your data in every failure scenario your orchestration platform can produce. Node failure, pod eviction, volume detachment — walk through each one and make sure the answer isn't 'we think it's fine.'
Stop letting infrastructure maturity substitute for deployment process maturity. A beautiful Kubernetes setup with a sloppy migration process is still a sloppy migration process. The tooling doesn't do the thinking for you.
Containerization was worth doing. It probably made your overall deployment posture better in meaningful ways. But if you're still feeling fragile every time you touch the database layer, that's not a sign the tools failed you. It's a sign there's still real work left to do — and the container runtime isn't going to do it.