The gap between Docker Compose and production Kubernetes

Docker Compose and Kubernetes solve the same problems with different vocabulary. Once you see the translation, production deployment stops feeling impossible.

My first introduction to Kubernetes was pure pain. Tag-based lookups, opaque command line operations, limited documentation. I couldn’t sort things out and abandoned the effort for other tools.

Over time I forced myself to learn Kubernetes anyway. But deploying a Laravel app was still a special kind of pain.

I’d been using Docker Compose for years. Local development was entirely pain-free. One docker-compose up and everything worked — PostgreSQL, Redis, the works. I felt confident. Kubernetes was just layers of frustrating abstraction atop what I thought was already adequate. But I still needed it for production.

Three weeks later, I was still debugging Ingress configurations.

The False Complexity Problem

Here’s what nobody tells you about Kubernetes: most of the complexity is artificial. Not because the technology is poorly designed — it’s actually elegant once you understand it. The complexity comes from documentation written for platform engineers, not application developers.

When you’re a PHP developer who just wants to ship a Laravel app, you don’t need to understand Custom Resource Definitions. You don’t need to know about Operators or service meshes or GitOps workflows. Not yet, anyway.

You need to know how the concepts you already understand map to Kubernetes primitives.

The Translation Layer

Docker Compose and Kubernetes solve identical problems with different vocabulary. Once you see the translation, the whole system clicks.

Services in Docker Compose → Services in Kubernetes

Both define how containers talk to each other. The names match because the concept is the same.

Volumes in Docker Compose → PersistentVolumeClaims in Kubernetes

Docker Compose mounts local directories.1Sometimes you’ll mount a specific file path through your compose file. If you don’t but are still using a volume, Docker transparently mounts from a hidden directory anyway. All volumes within a Compose-managed stack are just local file mounts! Kubernetes requests storage from a provider. Same idea, different mechanism.

docker-compose.yml → Deployment + Service YAML

Your Docker Compose file describes what to run. In Kubernetes, that splits into a Deployment (what container, how many replicas) and a Service (how to reach it).

Environment Variables → ConfigMaps and Secrets

You’re already putting credentials in `.env`. Kubernetes formalizes this into ConfigMaps (non-sensitive) and Secrets (sensitive). The pattern is identical.

What Actually Changes

The biggest mental shift isn’t technical. It’s accepting that Kubernetes expects your application to be disposable.

Containers crash. Pods get evicted. Nodes fail. Kubernetes handles all of this automatically — but only if your application doesn’t fight it. That means stateless processes. External databases. No local file storage for user uploads.

If you’re already following twelve-factor app principles, you’re 90% there.

The Talk at PHP Tek

In May, I’m presenting “Kubernetes for PHP Developers: From Docker Compose to Production” at PHP Tek. The talk walks through this translation layer step by step, using real Laravel applications as examples.

I’ll show the YAML. I’ll explain the patterns. And I’ll demonstrate how tools like Displace can automate the translation entirely!

This isn’t a Kubernetes-for-platform-engineers talk. It’s for PHP developers who want to deploy their own applications without hiring a DevOps team.

If you’re coming to Tek, I’d love to see you there. If you can’t make it, I’ll be posting related content here and on both X and Mastodon over the coming months.

What’s the biggest obstacle keeping you from Kubernetes adoption?

  • 1
    Sometimes you’ll mount a specific file path through your compose file. If you don’t but are still using a volume, Docker transparently mounts from a hidden directory anyway. All volumes within a Compose-managed stack are just local file mounts!