Skip to main content

Command Palette

Search for a command to run...

Migrating my Docker Stack to Kubernetes (Conceptual)

From Docker Compose thinking to Kubernetes reality

Updated
5 min read
Migrating my Docker Stack to Kubernetes (Conceptual)

Series: Containers, Actually: Building Real Local Dev Environments

Kubernetes is often presented as a replacement for Docker Compose.

That framing is wrong—and expensive.

The correct framing is this:

Docker Compose teaches you how to model a system.
Kubernetes teaches you how to operate that system at scale.

If your Compose setup is clean, intentional, and boring, Kubernetes becomes understandable.
If your Compose setup is chaotic, Kubernetes amplifies that chaos.

This article explains how to mentally migrate your stack before you ever migrate code.


First: What Migration Is (and Is Not)

Migrating to Kubernetes is not:

  • Rewriting your app

  • Replacing Docker

  • Copy-pasting YAML from tutorials

  • Turning everything into microservices

Migrating is:

  • Moving orchestration responsibility

  • Making implicit assumptions explicit

  • Adopting declarative operations at a higher level

Your containers don’t disappear.
Your control plane changes.


The Key Insight: Compose and Kubernetes Are Isomorphic

Most developers think Compose and Kubernetes are fundamentally different.

They aren’t.

They describe the same ideas at different scales.

Docker Compose conceptKubernetes concept
serviceDeployment / Pod
containerContainer
networkService + DNS
volumePersistentVolume
environment variablesConfigMap / Secret
depends_onProbes + retries
restart policyPod lifecycle

Once you see this mapping, Kubernetes stops feeling alien.


From docker-compose.yml to Kubernetes Objects

Let’s start with a simplified Compose service.

Compose Example

services:
  app:
    image: myapp:1.0
    environment:
      DB_HOST: mysql
    depends_on:
      - mysql

This already encodes:

  • A runtime image

  • Configuration

  • A dependency

Kubernetes just forces you to separate concerns explicitly.


Kubernetes Deployment (Conceptual Equivalent)

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: app
  template:
    metadata:
      labels:
        app: app
    spec:
      containers:
        - name: app
          image: myapp:1.0
          env:
            - name: DB_HOST
              value: mysql

What changed?

  • Replicas are explicit

  • Lifecycle is managed

  • Restarts are policy-driven

What didn’t change?

  • The container

  • The runtime

  • The configuration intent


Networking: From Docker DNS to Kubernetes Services

In Compose, this just works:

DB_HOST=mysql

Because Docker creates a network and DNS.

Kubernetes requires you to declare that explicitly.

Kubernetes Service

apiVersion: v1
kind: Service
metadata:
  name: mysql
spec:
  selector:
    app: mysql
  ports:
    - port: 3306

Now:

  • mysql resolves via cluster DNS

  • Traffic is load-balanced

  • Pods can come and go safely

The mental model is identical.
Only the declaration is more verbose.


Volumes: The Biggest Conceptual Shift

This is where migrations usually hurt.

Compose Volume

volumes:
  mysql-data:

services:
  mysql:
    volumes:
      - mysql-data:/var/lib/mysql

Simple. Implicit. Local.

Kubernetes Persistence (Conceptual)

Kubernetes splits this into:

  • PersistentVolume (storage)

  • PersistentVolumeClaim (request)

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi

Then mount it:

volumeMounts:
  - mountPath: /var/lib/mysql
    name: mysql-storage

This forces you to answer questions Compose let you ignore:

  • Where does storage live?

  • How big is it?

  • Who owns it?

  • Can it move?

Painful—but necessary at scale.


Environment Variables Become ConfigMaps and Secrets

Compose:

environment:
  APP_ENV: production

Kubernetes splits this intentionally.

ConfigMap

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  APP_ENV: production

Secret

apiVersion: v1
kind: Secret
metadata:
  name: app-secrets
stringData:
  DB_PASSWORD: supersecret

Then injected into pods.

This separation enforces discipline:

  • Config is visible

  • Secrets are controlled

  • Changes are auditable

Kubernetes is opinionated for a reason.


depends_on Does Not Exist (And That’s Good)

Compose:

depends_on:
  - mysql

This only controls startup order—not readiness.

Kubernetes removes this abstraction entirely.

Instead, you must design for eventual consistency.

Readiness Probes

readinessProbe:
  tcpSocket:
    port: 3306
  initialDelaySeconds: 10

This shifts responsibility:

  • From the orchestrator

  • To the application

If your app can’t handle dependencies being temporarily unavailable, Kubernetes will surface that weakness immediately.

This is painful—and healthy.


Scaling: From Manual to Declarative

Compose scaling:

docker-compose up --scale app=3

Kubernetes scaling:

replicas: 3

Plus autoscaling:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler

Scaling stops being an action.
It becomes a policy.


What Kubernetes Adds (That Compose Never Will)

Kubernetes earns its complexity by adding:

  • Self-healing

  • Declarative desired state

  • Rolling updates

  • Resource quotas

  • Namespaces

  • RBAC

  • Observability hooks

If you don’t need these, Kubernetes is overkill.

If you do need them, Compose cannot grow into them.


When Migration Makes Sense

You should consider Kubernetes when:

  • You need horizontal scaling

  • You run multiple environments continuously

  • You require zero-downtime deploys

  • You need strong isolation and governance

  • Your team can support the operational cost

You should not migrate just because:

  • “Everyone uses Kubernetes”

  • Compose feels “too simple”

  • You want resume keywords

Complexity should be earned.


A Pragmatic Migration Path

A sane progression looks like this:

  1. Docker Compose (local + CI)

  2. Compose + CI/CD parity

  3. Multi-env Compose

  4. Kubernetes for staging

  5. Kubernetes for production

At no point do you throw away Docker knowledge.

Kubernetes does not replace Compose thinking.
It extends it.


The Final Insight

If migrating to Kubernetes feels overwhelming, the problem is rarely Kubernetes.

It’s usually that:

  • System boundaries were unclear

  • State was implicit

  • Dependencies were fragile

  • Environments were inconsistent

Kubernetes exposes those issues. It doesn’t create them.


Final Takeaway

Docker Compose teaches you to design systems.
Kubernetes teaches you to operate systems under pressure.

If you master the first, the second becomes survivable.

Kubernetes is not a destination.
It’s a responsibility.

And responsibility should only be taken on deliberately.

Containers, Actually: Building Real Local Dev Environments

Part 1 of 18

This series explores full-stack local development with Docker—from core concepts and best practices to a real Windows implementation. The goal is to understand how things run, why they work, and how to build reproducible production environment.

Up next

Security Considerations in Local Containers

Building safer habits before production