Intermediate

Kubernetes for Developers: Moving from Docker Compose to Kubernetes

Migrate from Docker Compose to Kubernetes with Kompose and Skaffold through hands-on practice.

Table of Contents


1. Course Overview

This course is aimed at developers who want to move from running their containers with Docker Compose locally to running them in Kubernetes — whether for development, staging, testing, or other scenarios.

Prerequisites

SkillDetail
Command-line toolsUsing terminals, shells
DockerContainers, images, builds (Dockerfile)
Kubernetes core conceptsPods, Deployments, Services, ConfigMaps, Secrets

Course Path

flowchart LR
    A[Docker Compose\nand Kubernetes Review] --> B[Compose → K8s\nMapping]
    B --> C[Kompose\nAutomatic Conversion]
    C --> D[Skaffold\nLive Development]
    D --> E[Hands-On Practice\nFull Project]

Demo Projects

  • angular-jumpstart — Angular application + Node.js backend (2 services)
  • CodeWithDanDockerServices — Multi-service application: nginx + node + mongo + redis (4 services)

2. Docker Compose vs Kubernetes Comparison

2.1 Docker Compose Review

Docker Compose is a tool for defining and running multi-container Docker applications. With a single YAML file, you configure all application services and start them with a single command.

Typical Multi-Container Application Architecture

graph TD
    Client["Client / Browser"] --> Proxy["Reverse Proxy\n(nginx)"]
    Proxy --> App["Frontend App\n(JavaScript)"]
    App --> API1["Backend API 1\n(Node.js)"]
    App --> API2["Backend API 2"]
    API1 --> DB["Database\n(MongoDB / PostgreSQL)"]
    API1 --> Cache["Cache\n(Redis)"]
    API2 --> DB

Example docker-compose.yml (2-service application)

version: "3.8"
services:
  nginx:
    container_name: nginx
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
    networks:
      - app-net

  node:
    container_name: node-server
    image: my-node-app:latest
    ports:
      - "3000:3000"
    env_file:
      - .env
    networks:
      - app-net

networks:
  app-net:
    driver: bridge

Docker Compose Capabilities

FeatureDocker Compose
Building imagesdocker compose build
Starting containersdocker compose up
Stopping and cleanupdocker compose down
Viewing logsdocker compose logs
Basic scaling✅ (single node only)
Multi-node scaling
Auto-healing
Rolling updates

2.2 Kubernetes Review

Kubernetes is an open-source system for automating deployment, scaling, and management of containerized applications.

Kubernetes Architecture

graph TD
    Dev["Developer\nkubectl"] -->|commands| Master["Master Node\n(Control Plane)"]
    Master --> W1["Worker Node 1"]
    Master --> W2["Worker Node 2"]
    Master --> W3["Worker Node 3"]
    W1 --> P1["Pod\n[Container]"]
    W1 --> P2["Pod\n[Container]"]
    W2 --> P3["Pod\n[Container]"]
    W3 --> P4["Pod\n[Container]"]

Key Kubernetes Resources

ResourceRole
PodBasic unit — runs one or more containers
DeploymentManages Pods, rolling updates, scaling
ReplicaSetEnsures the desired number of Pods is running
ServiceExposes Pods on the network (ClusterIP, NodePort, LoadBalancer)
ConfigMapStores non-sensitive configuration (key/value)
SecretStores sensitive data (passwords, tokens)
PersistentVolumeClaimClaims persistent storage
IngressHTTP/HTTPS routing rules to Services
NamespaceLogical isolation of resources

Sample Deployment YAML

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:alpine
          ports:
            - containerPort: 80

2.3 Docker Compose → Kubernetes Mapping

The main migration challenge is moving from a single file (docker-compose.yml) to multiple YAML files in Kubernetes.

Mapping Diagram

flowchart LR
    subgraph Compose["docker-compose.yml"]
        S["service: nginx\n- image\n- ports\n- volumes\n- environment\n- networks"]
    end

    subgraph K8s["Kubernetes Manifests"]
        D["Deployment\n(Pod template, replicas,\nimage, ports)"]
        SVC["Service\n(ClusterIP / NodePort /\nLoadBalancer)"]
        CM["ConfigMap\n(environment variables)"]
        SEC["Secret\n(sensitive data)"]
        PVC["PersistentVolumeClaim\n(persistent volumes)"]
    end

    S -->|"containers + replicas"| D
    S -->|"ports exposure"| SVC
    S -->|"env_file / environment"| CM
    S -->|"secrets"| SEC
    S -->|"volumes"| PVC

Detailed Mapping Table

Docker Compose ConceptKubernetes EquivalentNotes
services:Deployment + Service1 Compose service → at least 2 K8s files
image:spec.containers[].imageIdentical
ports:Service.spec.ports + containerPortSplit between Service and Deployment
volumes: (bind mount)hostPath volumeAvoid in production
volumes: (named volume)PersistentVolumeClaimRecommended for persistent storage
environment:ConfigMap + env in containerFor non-sensitive values
env_file:ConfigMap or SecretSecrets for sensitive values
networks: (bridge)Not neededK8s handles intra-cluster networking natively
depends_on:initContainers or health checksNo direct equivalent in K8s
build:CI/CD pipeline or SkaffoldK8s does not build images
restart: alwaysrestartPolicy: AlwaysDefault behavior in Deployment
scale:spec.replicasK8s supports multi-node scaling
container_name:metadata.name (Pod)Pod name is managed by Deployment

Side-by-Side Comparison: nginx service

Docker Compose:

services:
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    environment:
      - API_URL=http://node:3000
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
    networks:
      - app-net

Kubernetes — Equivalent Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:alpine
          ports:
            - containerPort: 80
          env:
            - name: API_URL
              valueFrom:
                configMapKeyRef:
                  name: nginx-config
                  key: API_URL
          volumeMounts:
            - name: nginx-conf
              mountPath: /etc/nginx/nginx.conf
              subPath: nginx.conf
      volumes:
        - name: nginx-conf
          configMap:
            name: nginx-configmap

Kubernetes — Equivalent Service:

apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:
  selector:
    app: nginx
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
  type: LoadBalancer   # or NodePort for local development

Kubernetes — ConfigMap for environment variables:

apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  API_URL: "http://node:3000"

Before/After Migration Architecture

graph TB
    subgraph Before["BEFORE — Docker Compose (local machine)"]
        DC["docker-compose.yml\n(1 file)"]
        DC --> C1["Container: nginx"]
        DC --> C2["Container: node"]
        DC --> C3["Container: mongo"]
        DC --> C4["Container: redis"]
        C1 & C2 & C3 & C4 --> BN["Bridge Network\napp-net"]
    end

    subgraph After["AFTER — Kubernetes Cluster"]
        NS["Namespace: production"]
        NS --> ND["Deployment: nginx\n+ Service: nginx"]
        NS --> NOD["Deployment: node\n+ Service: node"]
        NS --> MOD["Deployment: mongo\n+ Service: mongo\n+ PVC: mongo-data"]
        NS --> RD["Deployment: redis\n+ Service: redis"]
        NS --> CM2["ConfigMap: app-config"]
        NS --> SEC2["Secret: mongo-secret"]
        ND & NOD & MOD & RD -.->|"ClusterIP\nDNS-based"| KN["Kubernetes\nCluster Network"]
    end

    Before -->|"kompose convert\nor skaffold init"| After

3. Migration with Kompose

3.1 Kompose Overview

Kompose = Kubernetes + Docker Compose (creative name fusion).

Kompose is a tool to help users familiar with Docker Compose move to Kubernetes. It takes a Docker Compose file and translates it into Kubernetes resources.

Why use Kompose?

  • Avoids manually rewriting all Kubernetes YAML files
  • Fast and official conversion
  • Generates Deployments and Services by default
  • Supports volume types (PVC, hostPath, emptyDir)
  • Can generate DaemonSets and other resources via flags

Kompose Workflow:

flowchart LR
    A["docker-compose.yml"] -->|"kompose convert"| B["nginx-deployment.yaml"]
    A -->|"kompose convert"| C["nginx-service.yaml"]
    A -->|"kompose convert"| D["node-deployment.yaml"]
    A -->|"kompose convert"| E["node-service.yaml"]
    A -->|"kompose convert"| F["[other resources]\nConfigMap, PVC..."]

3.2 Installing Kompose

macOS

# Download the binary
curl -L https://github.com/kubernetes/kompose/releases/latest/download/kompose-darwin-amd64 -o kompose

# Set executable permissions
chmod +x kompose

# Move to PATH
sudo mv ./kompose /usr/local/bin/kompose

# Verify
kompose version

Linux

curl -L https://github.com/kubernetes/kompose/releases/latest/download/kompose-linux-amd64 -o kompose
chmod +x kompose
sudo mv ./kompose /usr/local/bin/kompose

Windows (PowerShell)

# Download from GitHub Releases
# Place kompose.exe in a folder on your PATH
# https://github.com/kubernetes/kompose/releases

kompose version

3.3 The kompose convert Command

Basic Command

# Convert docker-compose.yml in the current directory
kompose convert

Available Flags

FlagShorthandDescription
--file-fSpecifies an alternative Compose file (e.g., docker-compose.prod.yml)
--out-oOutput directory for generated YAML files
--stdoutPrints YAML to console instead of files
--volumesVolume type: persistentVolumeClaim (default), hostPath, emptyDir
--replicasNumber of replicas for Deployments
--controllerController type: deployment (default), daemonset, replicationcontroller
--with-kompose-annotationAdds Kompose annotations to generated YAML

Usage Examples

# Convert with specific file and output to a directory
kompose convert -f docker-compose.yml -o ./k8s/

# Print to console for review before saving
kompose convert --stdout

# Convert using hostPath instead of PVC
kompose convert --volumes hostPath -o ./k8s/

# Convert with 3 replicas
kompose convert --replicas 3 -o ./k8s/

# Full help
kompose convert --help

3.4 Kompose in Action

Demo Project: angular-jumpstart

This project contains two services in its docker-compose.yml:

  • nginx — Serves the JavaScript frontend (Angular)
  • node — Node.js backend API
# 1. Navigate to the project directory
cd angular-jumpstart

# 2. Create the output folder
mkdir output

# 3. Run the conversion
kompose convert -f docker-compose.yml -o ./output/

# Result:
# INFO Kubernetes file "output/nginx-service.yaml" created
# INFO Kubernetes file "output/node-service.yaml" created
# INFO Kubernetes file "output/nginx-deployment.yaml" created
# INFO Kubernetes file "output/node-deployment.yaml" created

Deploying the Generated Resources

# Apply all generated files
kubectl apply -f ./output/

# Check Deployments
kubectl get deployments

# Check Pods
kubectl get pods

# Check Services
kubectl get services

3.5 Analyzing the Generated YAML

Service Generated by Kompose (before cleanup)

apiVersion: v1
kind: Service
metadata:
  annotations:
    kompose.cmd: kompose convert -f docker-compose.yml -o ./output/
    kompose.version: 1.28.0 (c4137012e)
  creationTimestamp: null        # Remove or set a valid date
  labels:
    io.kompose.service: nginx    # Kompose label — can be renamed
  name: nginx
spec:
  ports:
    - name: "80"
      port: 80
      targetPort: 80
  selector:
    io.kompose.service: nginx
status:
  loadBalancer: {}               # Default type = ClusterIP
apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
    - name: http
      port: 80
      targetPort: 80
  selector:
    app: nginx
  type: LoadBalancer

Deployment Generated by Kompose (before cleanup)

apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    kompose.cmd: kompose convert
    kompose.version: 1.28.0
  creationTimestamp: null
  labels:
    io.kompose.service: nginx
  name: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      io.kompose.service: nginx
  strategy: {}
  template:
    metadata:
      annotations:
        kompose.cmd: kompose convert
        kompose.version: 1.28.0
      creationTimestamp: null
      labels:
        io.kompose.service: nginx
    spec:
      containers:
        - image: nginx:alpine
          name: nginx
          ports:
            - containerPort: 80
          resources: {}
      restartPolicy: Always
status: {}
ElementRecommended Action
annotations: kompose.*Remove (unnecessary metadata in production)
creationTimestamp: nullRemove
labels: io.kompose.serviceReplace with app: <name>
strategy: {}Configure: type: RollingUpdate with maxSurge and maxUnavailable
resources: {}Define CPU/memory requests and limits
replicas: 1Adjust as needed
Service typeVerify whether ClusterIP, NodePort, or LoadBalancer is appropriate

Full Example After Cleanup (nginx + node application)

nginx-deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:alpine
          ports:
            - containerPort: 80
          resources:
            requests:
              cpu: "100m"
              memory: "128Mi"
            limits:
              cpu: "500m"
              memory: "256Mi"
      restartPolicy: Always

4. Migration with Skaffold

4.1 Skaffold Overview

Skaffold = local Kubernetes development.

Skaffold handles the workflow for building, pushing, and deploying your application, allowing you to focus on what matters most: writing code.

flowchart TD
    Dev["Developer\n(modifies code)"] -->|"change\ndetection"| Skaffold
    Skaffold -->|"rebuild"| Builder["Build\n(Docker image)"]
    Builder -->|"push"| Registry["Image Registry\n(local or remote)"]
    Registry -->|"redeploy"| K8s["Kubernetes Cluster\n(local: Docker Desktop\nor minikube)"]
    K8s -->|"Pods updated"| App["Application\n(accessible)"]

Docker Compose vs Skaffold Comparison

FeatureDocker ComposeSkaffold
Building images
Running containers✅ (local)✅ (in Kubernetes)
Live reload on code change✅ (watch)✅ (rebuild + redeploy)
File syncing without rebuild
Deploy to remote cluster
Multi-service orchestrationBasicVia Kubernetes
Convert to K8s manifests✅ (via internal Kompose)

Important note: Skaffold uses Kompose internally for Docker Compose → Kubernetes manifest conversion.


4.2 Installing Skaffold

macOS

# Via Homebrew (recommended)
brew install skaffold

# Via curl
curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/latest/skaffold-darwin-amd64
sudo install skaffold /usr/local/bin/

# Verify
skaffold version

Linux

curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/latest/skaffold-linux-amd64
sudo install skaffold /usr/local/bin/

Windows

# Via Chocolatey
choco install skaffold

# Verify
skaffold version

4.3 Skaffold Commands

Main Commands

CommandDescription
skaffold initInitializes a Skaffold project (generates skaffold.yaml)
skaffold runBuild, push and deploy once
skaffold devDevelopment mode: watches for changes and redeploys automatically
skaffold buildBuild images only
skaffold deployDeploy only (no build)
skaffold deleteRemoves deployed resources

Important Flags for skaffold init

# Initialize from a Docker Compose file
skaffold init --compose-file docker-compose.yml

# Initialize from existing Kubernetes manifests
skaffold init -k ".k8s/*.yaml"

# With Dockerfile artifact specifications
skaffold init \
  --compose-file docker-compose.yml \
  -a '{"builder":"Docker","payload":{"path":"./docker/nginx/Dockerfile"},"image":"my-nginx"}' \
  -a '{"builder":"Docker","payload":{"path":"./docker/node/Dockerfile"},"image":"my-node"}'

4.4 skaffold init in Action

Project angular-jumpstart

# Step 1: Initialize from Docker Compose
skaffold init \
  --compose-file docker-compose.yml \
  -a '{"builder":"Docker","payload":{"path":"./Dockerfile.nginx"},"image":"nginx-app"}' \
  -a '{"builder":"Docker","payload":{"path":"./Dockerfile.node"},"image":"node-app"}'

# Result: creates skaffold.yaml and K8s manifests in .k8s/

Generated skaffold.yaml File

apiVersion: skaffold/v4beta6
kind: Config
metadata:
  name: angular-jumpstart
build:
  artifacts:
    - image: nginx-app
      context: .
      docker:
        dockerfile: Dockerfile.nginx
    - image: node-app
      context: .
      docker:
        dockerfile: Dockerfile.node
manifests:
  rawYaml:
    - .k8s/*.yaml

4.5 skaffold dev in Action

# Launch development mode
skaffold dev -f skaffold.yaml

What happens during skaffold dev:

sequenceDiagram
    participant Dev as Developer
    participant Skaffold as Skaffold
    participant Docker as Docker Build
    participant K8s as Kubernetes

    Dev->>Skaffold: skaffold dev
    Skaffold->>Docker: Build initial images
    Docker-->>Skaffold: Images built
    Skaffold->>K8s: kubectl apply (manifests)
    K8s-->>Skaffold: Deployments stable
    Skaffold-->>Dev: Application accessible ✅

    loop File Watching
        Dev->>Dev: Code modification
        Skaffold->>Docker: Rebuild image
        Docker-->>Skaffold: New image
        Skaffold->>K8s: Automatic redeployment
        K8s-->>Skaffold: Pod updated
        Skaffold-->>Dev: Changes visible ✅
    end

    Dev->>Skaffold: Ctrl+C
    Skaffold->>K8s: Resource cleanup

File Syncing (Rebuild Optimization)

For cases where rebuilding a full image is too slow (e.g., Angular with npm install), Skaffold offers file sync — syncing only modified files without a rebuild:

# In skaffold.yaml
build:
  artifacts:
    - image: my-node-app
      context: .
      docker:
        dockerfile: Dockerfile
      sync:
        infer:
          - "src/**/*.js"   # Sync these files without rebuild
          - "src/**/*.ts"

Useful Commands During skaffold dev

# View logs in real time
skaffold dev --tail

# Disable automatic cleanup on exit
skaffold dev --cleanup=false

# Force port-forwarding
skaffold dev --port-forward

5. Full Hands-On Practice

5.1 Conversion with Kompose

Project: CodeWithDanDockerServices

This more complex project contains 4 services: nginx, node, mongo, redis.

# Project available at: github.com/DanWahlin/CodeWithDanDockerServices

docker-compose.yml (excerpt with 4 services):

version: "3.8"
services:
  nginx:
    image: codewithdan/nginx:latest
    ports:
      - "80:80"
    networks:
      - app-net

  node:
    image: codewithdan/node-service:latest
    ports:
      - "3000:3000"
    environment:
      - MONGO_URI=${MONGO_URI}
      - REDIS_URL=${REDIS_URL}
    depends_on:
      - mongo
      - redis
    networks:
      - app-net

  mongo:
    image: mongo:6
    ports:
      - "27017:27017"
    volumes:
      - mongo-data:/data/db
    environment:
      - MONGO_INITDB_ROOT_USERNAME=${MONGO_USER}
      - MONGO_INITDB_ROOT_PASSWORD=${MONGO_PASSWORD}
    networks:
      - app-net

  redis:
    image: redis:alpine
    ports:
      - "6379:6379"
    networks:
      - app-net

volumes:
  mongo-data:

networks:
  app-net:
    driver: bridge

Conversion Procedure

# PowerShell: set required environment variables
$env:MONGO_URI = "mongodb://admin:secret@mongo:27017/appdb"
$env:REDIS_URL = "redis://redis:6379"
$env:MONGO_USER = "admin"
$env:MONGO_PASSWORD = "secret"

# Verify kompose is installed
kompose version

# Convert with output to a directory
kompose convert -f docker-compose.yml -o ./output/

# Generated files:
# output/nginx-deployment.yaml
# output/nginx-service.yaml
# output/node-deployment.yaml
# output/node-service.yaml
# output/mongo-deployment.yaml
# output/mongo-service.yaml
# output/redis-deployment.yaml
# output/redis-service.yaml
# output/mongo-data-persistentvolumeclaim.yaml

Result: PersistentVolumeClaim for MongoDB

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mongo-data
  labels:
    app: mongo
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi

Secret for MongoDB (created manually)

# Create the Secret for MongoDB credentials
kubectl create secret generic mongo-secret \
  --from-literal=MONGO_INITDB_ROOT_USERNAME=admin \
  --from-literal=MONGO_INITDB_ROOT_PASSWORD=secret

Or in YAML:

apiVersion: v1
kind: Secret
metadata:
  name: mongo-secret
type: Opaque
stringData:
  MONGO_INITDB_ROOT_USERNAME: admin
  MONGO_INITDB_ROOT_PASSWORD: secret    # In production: use a vault

Full Deployment

# Apply the Secret first
kubectl apply -f mongo-secret.yaml

# Apply all manifests
kubectl apply -f ./output/

# Check status
kubectl get all

# Check PVCs
kubectl get pvc

# Check Pod logs
kubectl logs deployment/node

5.2 Using Skaffold

skaffold.yaml for CodeWithDanDockerServices

apiVersion: skaffold/v4beta6
kind: Config
metadata:
  name: codewithdan-services
build:
  artifacts:
    - image: codewithdan/nginx
      context: .
      docker:
        dockerfile: docker/nginx/Dockerfile
    - image: codewithdan/node-service
      context: .
      docker:
        dockerfile: docker/node/Dockerfile
    - image: codewithdan/mongo
      context: .
      docker:
        dockerfile: docker/mongo/Dockerfile
    - image: codewithdan/redis
      context: .
      docker:
        dockerfile: docker/redis/Dockerfile
manifests:
  rawYaml:
    - .k8s/*.yaml

Initialization and Launch

# Initialize from the existing docker-compose.yml
skaffold init \
  --compose-file docker-compose.yml \
  -a '{"builder":"Docker","payload":{"path":"docker/nginx/Dockerfile"},"image":"codewithdan/nginx"}' \
  -a '{"builder":"Docker","payload":{"path":"docker/node/Dockerfile"},"image":"codewithdan/node-service"}' \
  -a '{"builder":"Docker","payload":{"path":"docker/mongo/Dockerfile"},"image":"codewithdan/mongo"}' \
  -a '{"builder":"Docker","payload":{"path":"docker/redis/Dockerfile"},"image":"codewithdan/redis"}'

# Create the Mongo Secret before launching
kubectl apply -f mongo-secret.yaml

# Launch in development mode
skaffold dev -f skaffold.yaml

# Or run once
skaffold run -f skaffold.yaml

6. Summary and Resources

Full Migration Flow

flowchart TD
    Start["Docker Compose\nApplication"] --> Assess["Analyze\ndocker-compose.yml"]
    Assess --> HasSecrets{"Has sensitive\nvariables?"}
    HasSecrets -->|Yes| CreateSecrets["Create\nKubernetes Secrets"]
    HasSecrets -->|No| ChooseTool
    CreateSecrets --> ChooseTool["Choose migration\ntool"]

    ChooseTool --> Kompose["Kompose\n(static conversion)"]
    ChooseTool --> Skaffold["Skaffold\n(dev workflow)"]

    Kompose -->|"kompose convert\n-f docker-compose.yml\n-o ./k8s/"| GeneratedYAML["Generated\nKubernetes YAML"]
    Skaffold -->|"skaffold init\n--compose-file"| SkaffoldYAML["skaffold.yaml\n+ K8s YAML"]

    GeneratedYAML --> Review["Review\nand cleanup\nYAML"]
    SkaffoldYAML --> Review

    Review --> Cleanup["Remove unnecessary\nKompose annotations\nConfigure resources,\nstrategy, labels"]
    Cleanup --> Apply["kubectl apply\n-f ./k8s/"]
    Apply --> Test["Validation\ntesting"]
    Test --> Done["✅ Application\nin Kubernetes"]

Migration Tool Comparison

CriteriaKomposeSkaffold
Main purposeFile conversionDevelopment workflow
Key commandkompose convertskaffold init, skaffold dev
Generates K8s manifests✅ (via internal Kompose)
Live development
File syncing
Auto-rebuild on change
ComplexityLowMedium
Use caseOne-shot migrationContinuous development on K8s

Skaffold Alternatives

Other tools offer similar workflows:

ToolHighlight
SkaffoldKompose integration, file sync, multi-cluster
TiltWeb UI, multi-services, highly configurable
DraftAutomatic language detection, CI/CD optimized
GardenDependency graph, integrated testing

Quick Reference Commands

# ─── Kompose ──────────────────────────────────────────────
# Basic conversion
kompose convert

# Convert to a specific directory
kompose convert -f docker-compose.yml -o ./k8s/

# Print to console
kompose convert --stdout

# With specific volume type
kompose convert --volumes hostPath

# ─── Skaffold ─────────────────────────────────────────────
# Initialize from Compose
skaffold init --compose-file docker-compose.yml

# Initialize from existing manifests
skaffold init -k ".k8s/*.yaml"

# Development mode (live reload)
skaffold dev

# One-shot deployment
skaffold run

# Cleanup resources
skaffold delete

# ─── kubectl (post-migration) ─────────────────────────────
# Deploy all manifests
kubectl apply -f ./k8s/

# Check everything
kubectl get all

# Follow logs
kubectl logs -f deployment/my-app

# Describe a Pod for debugging
kubectl describe pod <pod-name>

# Access a Pod interactively
kubectl exec -it <pod-name> -- /bin/sh

Post-Migration Best Practices

  1. Standardized labels — Replace io.kompose.service labels with app: <name> and optionally version, tier, environment
  2. Resources requests/limits — Always define CPU and memory limits to avoid starved pods
  3. Health checks — Add livenessProbe and readinessProbe for critical services
  4. Secrets management — Never store credentials in plain text; use Kubernetes Secrets, HashiCorp Vault, or cloud-native solutions
  5. Namespace isolation — Use Namespaces to separate dev/staging/production
  6. Rolling update strategy — Configure maxSurge and maxUnavailable for zero-downtime deployments
  7. Service type review — Verify whether ClusterIP, NodePort, or LoadBalancer is appropriate for each service

Additional Resources

ResourceLink
Kompose Documentationhttps://kompose.io
Kompose GitHub Repositoryhttps://github.com/kubernetes/kompose
Skaffold Documentationhttps://skaffold.dev
angular-jumpstart Projecthttps://github.com/danwahlin/angular-jumpstart
CodeWithDanDockerServices Projecthttps://github.com/DanWahlin/CodeWithDanDockerServices
Bloghttps://codewithdan.com

Search Terms

kubernetes · developers · moving · docker · compose · containers · skaffold · kompose · generated · migration · commands · comparison · resources · action · application · architecture · cleanup · deployment · mapping · service · angular-jumpstart · changes · codewithdandockerservices · command

Interested in this course?

Contact us to book it or get a custom training plan for your team.