Docker Compose to Kubernetes migration is not a YAML conversion project. It is an application-architecture migration: state, health checks, configuration, traffic, secrets, scaling, and rollback all need new ownership before production traffic moves.
Kompose can translate a Compose file into Kubernetes objects, but Kubernetes documentation treats it as a conversion aid, not a production-readiness system. Generated manifests still need review for storage, probes, exposure, resource requests, update strategy, and security.
Raff Technologies managed Kubernetes removes the need to operate the control plane yourself, but it does not remove application migration work. The safest path is:
inventory → separate state → map services → add probes/resources → validate traffic → test recovery → cut over → observe → decommission Compose
If you are still deciding whether Kubernetes is justified, start with Kubernetes vs Docker Compose for Small Teams. This guide assumes the decision to migrate has already been made.
Start with a Compose workload inventory
Before converting anything, inventory the running application.
A Compose file may describe containers, ports, networks, volumes, environment variables, and health checks, but production behavior often depends on things outside the file.
Record:
- images and commands;
- internal and public ports;
- named volumes and bind mounts;
- environment variables and secrets;
- health checks;
- databases and caches;
- scheduled tasks;
- background workers;
- DNS and TLS;
- object storage;
- external APIs;
- backup jobs;
- CI/CD credentials;
- host cron jobs;
- host filesystem dependencies.
A useful migration table is:
| Compose concern | What to capture | Kubernetes destination |
|---|---|---|
| service | image, command, replicas | Deployment, StatefulSet, Job, or external service |
| published port | public or internal purpose | Service + Gateway/Ingress/LoadBalancer decision |
| named volume | owner, size, backup | PVC, object storage, managed service, or external storage |
| bind mount | why host filesystem is needed | image content, ConfigMap, Secret, PVC, or redesign |
| environment | config vs secret | ConfigMap, Secret, or external secret workflow |
| healthcheck | what healthy means | startup, readiness, liveness probes |
| depends_on | startup expectation | retries, readiness, service discovery |
| database | data lifecycle and recovery | managed DB, external DB, or deliberate StatefulSet design |
| cron | schedule and concurrency | CronJob |
| one-off task | retries and completion | Job |
The first deliverable is not Kubernetes YAML.
It is a map of what moves, what remains external, and which Compose assumptions must disappear.
Do not move hidden host dependencies into Kubernetes unchanged
Compose deployments often work because one VM quietly provides extra services.
Examples include:
- uploads under /srv/app;
- TLS certificates mounted from the host;
- cron scripts outside Compose;
- local backup directories;
- application sessions stored on disk;
- host-level firewall rules;
- a reverse proxy configured outside the Compose file.
Kubernetes will not discover these dependencies automatically.
For every host dependency, choose one destination:
- bake it into the container image;
- move it to ConfigMap or Secret;
- move it to persistent storage;
- move it to object storage or another managed service;
- remove the dependency entirely.
A successful migration ends with the old VM no longer being a hidden runtime dependency.
Separate state before making Pods replaceable
Kubernetes assumes Pods can be recreated.
That makes state the highest-risk part of most Compose migrations.
Classify every writable path:
Disposable runtime data
Examples:
- temporary files;
- caches that can be rebuilt;
- transient build output.
These usually do not need persistent storage.
Configuration
Configuration belongs in controlled deployment configuration, ConfigMaps, Secrets, or application defaults rather than mutable container filesystems.
Durable filesystem data
Uploads or application data that genuinely require mounted filesystem semantics may need a PersistentVolumeClaim.
Use Kubernetes Persistent Storage before mapping Compose volumes directly to PVCs.
Database state
A database should move into Kubernetes only if the team intentionally wants to own:
- persistent storage;
- backup consistency;
- restore;
- upgrades;
- failover;
- resource guarantees;
- maintenance.
For many small teams, keeping the database managed or external reduces migration risk.
The target architecture may become:
Managed Kubernetes ├── web Pods ├── API Pods └── worker Pods ↓ managed database ↓ object storage / persistent storage
The objective is not "move every Compose container into Kubernetes."
It is "move the application into an operating model where compute can be replaced safely."
Map Compose services to the right Kubernetes workload type
Not every Compose service becomes a Deployment.
Use workload behavior:
| Workload | Kubernetes starting point |
|---|---|
| stateless web/API | Deployment |
| long-running worker | Deployment |
| stateful identity/order requirement | StatefulSet |
| scheduled task | CronJob |
| one-time migration/task | Job |
| external managed DB/cache | no in-cluster workload required |
A StatefulSet is not a generic choice for anything with data.
Use it when stable Pod identity, ordered lifecycle, or per-replica persistent storage is genuinely required.
Keep the target architecture simpler than the source where possible.
Convert Compose health checks into separate Kubernetes probes
Compose commonly has one health check.
Kubernetes separates health into three signals.
Startup probe — has the application completed startup?
Readiness probe — should this Pod receive traffic?
Liveness probe — is the process stuck badly enough that Kubernetes should restart it?
Do not copy one Compose health command into all three.
For every service, define:
| Probe | Question |
|---|---|
| startup | How long can this service legitimately take to initialize? |
| readiness | What must be true before production traffic reaches it? |
| liveness | What proves the process cannot recover without restart? |
Be careful with dependency checks.
A temporary database outage may justify failing readiness, but making liveness fail on every dependency issue can create restart loops that make recovery harder.
Also remove startup-order assumptions from Compose depends_on. Kubernetes workloads can restart independently at any time. Applications should retry dependencies and tolerate temporary unavailability.
Replace Compose networking with Kubernetes Services and deliberate traffic entry
Compose service names provide convenient discovery on a Docker network.
Kubernetes uses Services to provide stable identities to replaceable Pods.
For each connection, classify it as:
- internal service-to-service traffic;
- external dependency;
- public application traffic;
- administrative access.
Most application Services should remain internal.
A common target is:
Internet ↓ Gateway API or existing Ingress ↓ ClusterIP Service ↓ application Pods ↓ private Services / managed dependencies
Do not turn every published Compose port into a public Kubernetes Service.
Re-evaluate why the port existed.
For new HTTP/HTTPS routing designs, evaluate Gateway API where your cluster implementation supports the required features. Existing Ingress remains valid, but Kubernetes has frozen the Ingress API and new routing functionality is developed through Gateway API.
Use Kubernetes Services Explained for Service types and Kubernetes Load Balancer vs Ingress Controller for the exposure decision.
Configuration and secrets need a new ownership model
Compose deployments frequently rely on environment variables and .env files.
During migration, separate:
Non-secret application configuration
Good candidates for ConfigMaps or deployment configuration include:
- feature switches;
- service URLs;
- environment names;
- application tuning values.
Secrets
Credentials, tokens, API keys, and private connection strings require controlled secret handling.
Do not copy a production .env file into Git-tracked Kubernetes manifests.
Kubernetes Secrets provide a cluster object for sensitive values, but teams still need:
- RBAC;
- rotation;
- access ownership;
- safe CI/CD injection;
- protection of kubeconfig and administrative credentials.
The migration is a good time to remove long-lived secrets from application images and source repositories.
Set requests before sizing the worker pool
Compose can run several containers on one VM without explicit CPU or memory requests.
Kubernetes scheduling works differently.
For every production workload, define initial CPU and memory requests based on observed behavior or conservative estimates.
Then calculate the cluster.
Use Kubernetes Cluster Sizing to include:
- Node Allocatable;
- minimum replica demand;
- system overhead;
- rollout overlap;
- one-worker failure or maintenance reserve;
- growth headroom.
Do not choose worker plans first and then force workloads to fit.
Calculate workload requirements first.
Keep node-pool design simple during migration
A migration does not need a new node pool for every Compose service.
Start with one general pool unless a workload class clearly needs:
- a different CPU/memory shape;
- dedicated capacity;
- a separate scaling pattern;
- stronger compute placement;
- different maintenance behavior.
Then use Kubernetes Node Pools.
Too many pools during migration create extra variables when the team is already changing networking, storage, scheduling, and deployment behavior.
Use Kompose as a starting point, not as the migration strategy
Kompose remains useful.
The Kubernetes documentation shows the basic workflow:
kompose convert kubectl apply -f <generated-output>
It can convert common Compose concepts into Kubernetes resources.
But generated output must be reviewed.
Check at least:
- workload type;
- Service exposure;
- volumes;
- probes;
- resource requests;
- Secrets;
- update strategy;
- security context;
- labels;
- naming;
- storage lifecycle.
Kompose can accelerate the first draft.
It cannot decide whether a database should remain external, whether a service should be public, what readiness means, or whether rollback is safe.
That is why this page should own migration architecture, not compete for the generic Kompose keyword.
Build the target environment before changing production traffic
The Kubernetes environment should be functional before cutover.
Validate:
Cluster
- worker capacity is sufficient;
- node pools are correct;
- required storage exists;
- cluster access works;
- monitoring is available.
Workloads
- images deploy reproducibly;
- Pods reach Ready;
- replicas schedule correctly;
- Jobs/CronJobs behave correctly;
- Secrets and ConfigMaps are present.
Networking
- internal Services resolve;
- public routes work;
- TLS is valid;
- required external APIs are reachable;
- private dependencies are not accidentally public.
State
- persistent data is present;
- databases are reachable;
- uploads behave correctly;
- backup and restore are validated.
Operations
- logs are visible;
- failed Pods are diagnosable;
- a new image can roll out;
- rollback has been tested.
Do this before changing the production DNS or routing path.
Use an explicit production cutover gate
Production should not move because the new cluster "looks fine."
Use a written gate.
| Gate | Acceptance condition |
|---|---|
| Images | versioned and reproducible |
| State | every durable path has an owner |
| Database | backup, restore, and schema compatibility are understood |
| Replicas | multi-replica workloads do not depend on local state |
| Probes | startup, readiness, and liveness behave correctly |
| Resources | CPU and memory requests are set |
| Secrets | no production secrets are embedded in images/manifests |
| Traffic | public and internal paths are clearly separated |
| DNS/TLS | production hostnames and certificates work |
| Jobs | workers, CronJobs, and migrations have explicit ownership |
| Observability | logs, metrics, events, and application errors are visible |
| Rollout | new versions can deploy safely |
| Rollback | previous app and traffic path remain recoverable |
| Data | there is one authoritative write path |
| Cutover | owner, window, checks, and abort criteria are written |
Every item needs a pass/fail condition.
Avoid split-brain data during cutover
The hardest migration problem is often not containers.
It is data ownership.
If both Compose and Kubernetes accept writes against independent state, rollback becomes dangerous.
Prefer one authoritative write path.
Possible strategies include:
- keep the database external and shared during the compute migration;
- briefly stop writes while final data synchronization occurs;
- switch application traffic atomically enough that only one environment accepts writes;
- design replication explicitly if dual-running is required.
Do not improvise this during the migration window.
If the database schema also changes, verify that the previous application version can still operate against the new schema before treating application rollback as safe.
Keep the Compose environment recoverable after cutover
Kubernetes Deployments can roll back application revisions, but migration rollback is broader.
Keep the old Compose environment recoverable until the new deployment has passed an agreed observation period.
That can mean:
- keep the VM powered on but outside the active traffic path;
- preserve image versions and configuration;
- prevent it from creating conflicting writes;
- keep a documented DNS/routing reversal procedure;
- define who can order rollback.
Do not destroy the old environment after the first successful HTTP request.
Decommission it only after the new environment has proven:
- traffic;
- state;
- jobs;
- monitoring;
- deployment;
- backup;
- recovery.
How Raff Kubernetes changes the migration boundary
Raff Managed Kubernetes currently provides:
- a $0 standard control plane;
- optional three-master HA for $30/month;
- worker nodes starting at $9.99/month;
- Standard 2 vCPU / 4 GB workers at $17.99/month;
- multiple node pools;
- autoscaling;
- private VPC networking;
- a managed public endpoint with DNS and TLS;
- built-in monitoring and pod logs;
- dedicated Kubernetes storage at $0.10/GB-month.
Use the Raff Kubernetes product page for live pricing and platform capabilities.
For migration planning, this means the team can focus on application workloads and worker capacity without building and operating its own Kubernetes control plane.
It does not mean the platform can infer:
- which Compose volume contains critical data;
- whether a health endpoint is a readiness or liveness signal;
- whether a port should be public;
- whether a schema migration is reversible;
- whether an application can run multiple replicas safely.
Those remain application migration decisions.
Recommended migration sequence
For a production Compose workload, use this order:
Phase 1 — Inventory
Document services, ports, state, secrets, jobs, external dependencies, and host assumptions.
Phase 2 — Separate state
Move or deliberately map databases, uploads, and persistent filesystem data.
Phase 3 — Build Kubernetes workload definitions
Choose Deployment, StatefulSet, Job, and CronJob objects intentionally.
Phase 4 — Add production controls
Define probes, resource requests, Services, routing, Secrets, and storage.
Phase 5 — Create the cluster
Size workers and node pools from the actual workload model.
Phase 6 — Validate without production traffic
Test application behavior, state, monitoring, rollout, and recovery.
Phase 7 — Cut over
Switch one authoritative traffic/write path with explicit validation and abort criteria.
Phase 8 — Observe
Keep Compose recoverable while Kubernetes proves normal operations.
Phase 9 — Decommission
Remove the old environment only when it is no longer a runtime or recovery dependency.
Final recommendation
Do not migrate Docker Compose to Kubernetes by starting with YAML conversion.
Start with the application.
Inventory its dependencies, separate persistent state, translate health into Kubernetes probes, replace host networking assumptions with Services and deliberate public routing, set resource requests, and define rollback before production traffic moves.
Use Kompose when it saves time generating a first draft, then review every generated resource as production code.
A successful migration is complete when the application can be scheduled, replaced, deployed, observed, and recovered without depending on the old Compose host.
Continue with Kubernetes Cluster Sizing, Kubernetes Node Pools, Kubernetes Persistent Storage, and Managed vs Self-Managed Kubernetes for the decisions behind the target platform.