Ravindra BagaleCourses & study guides मराठी

Chapter 67: Docker Swarm vs Kubernetes: the Same App on Both, Side by Side

67.5 Choosing and migrating: when Swarm fits, when Kubernetes wins, and moving a stack across

Why: the problem

Comparisons are only useful if they end in a decision. This last lesson of the chapter turns the measurements from 67.4 into a simple decision checklist, looks at how real companies chose, and then does the practical job many DevOps engineers are hired for: migrating an existing Swarm stack to Kubernetes without downtime. Why does this matter in practice? Plenty of Indian startups and agencies started on Swarm between 2017 and 2020 because it was the easiest way to run Docker on three servers. As they grew, they hit the same walls: no autoscaling for festival sales, stateful services pinned to one machine, hiring engineers who only know Kubernetes, and cloud providers offering EKS, GKE and AKS but nothing for Swarm. The migration project lands on the DevOps team, and interviewers love to ask how you would run it. What is the decision rule? Choose Swarm when the team is small, the services are few and mostly stateless, the hosts are on-premise or at the edge, nobody needs autoscaling or custom resources, and simplicity beats features. Choose Kubernetes when you run on a public cloud with a managed service, need autoscaling, many teams share the platform, you need policy and RBAC per team, or you want the ecosystem of Helm charts, operators, GitOps with Argo CD and service meshes. In 2026 almost every job description in India lists Kubernetes, and very few list Swarm, which is a career factor too. How do we migrate? In four phases. First, inventory the stack: every service, port, secret, config, volume and constraint. Second, convert the Compose file with Kompose as a starting point and fix the gaps the same way as in 66.5. Third, run both platforms in parallel behind one load balancer or DNS name and move traffic gradually with weighted records. Fourth, move the data, then remove the Swarm. We do the first three phases in the lab with the shop stack from 67.2.

What: every term in plain English

  • Inventory — List of every service, port, secret, config, volume and constraint.
  • Kompose — Converts Compose and stack files into Kubernetes manifests.
  • Parallel run — Both platforms serve traffic during the move.
  • Weighted DNS — Route 53 weighted records split traffic by percentage.
  • Cutover — Moment when all traffic goes to the new platform.
  • Data migration — Moving database contents, often with pg_dump or replication.
  • Rollback plan — A tested way back to Swarm if the move fails.
  • K3s — Small certified Kubernetes distribution, a common Swarm replacement at the edge.
  • Managed Kubernetes — EKS, GKE or AKS, where the cloud runs the control plane.
  • Decision record — Short document explaining why a tool was chosen.
Score your project with the decision checklist. Inventory the Swarm stack with docker CLI. Convert stack.yml with Kompose. Fix the Kompose output. Apply to the kind cluster. Run both and split traffic with a small proxy. Copy the database with pg_dump.
Migration path from a Swarm stack to Kubernetes

How: step by step

  1. Score your project with the decision checklist. Swarm or Kubernetes.
  2. Inventory the Swarm stack with docker CLI. inventory.txt.
  3. Convert stack.yml with Kompose. Manifests.
  4. Fix the Kompose output. Secret, StatefulSet, probes.
  5. Apply to the kind cluster. App runs.
  6. Run both and split traffic with a small proxy. 90/10 then 50/50.
  7. Copy the database with pg_dump. Data moved.
  8. Cut over and write the decision record. DECISION.md.

Write the decision record even when the answer seems obvious, because the next engineer will ask why. A good one fits on a page: context (three servers on-premise, eight services, team of four), options considered (stay on Swarm, K3s, EKS), decision (EKS with Karpenter), consequences (higher monthly cost for the control plane, new skills to learn, autoscaling for sale days, managed upgrades). Many teams at Razorpay and Freshworks keep these records in an adr folder next to the code. The record also protects you in the opposite case: if a small team stays on Swarm, writing down that the decision will be revisited when the service count passes fifteen or when the first autoscaling need appears turns a vague worry into a clear trigger.

Example 1: Decision checklist and stack inventory

Goal. Decision checklist and stack inventory.

decide.sh

score=0
ask() { printf '%s [y/n] ' "$1"; read a; [ "$a" = y ] && score=$((score+$2)); }
ask 'Running on AWS, GCP or Azure with a managed option?' 2
ask 'Need autoscaling for traffic peaks?' 2
ask 'More than 15 services or more than 2 teams?' 2
ask 'Stateful services that must move between nodes?' 1
ask 'Need RBAC, NetworkPolicy or admission policy?' 1
ask 'Edge or on-premise boxes with little RAM?' -2
ask 'Team of 3 or fewer with no Kubernetes skills?' -1
[ $score -ge 3 ] && echo "score $score: choose Kubernetes" || echo "score $score: Swarm is fine for now"
sw() { docker exec node1 docker "$@"; }
for s in $(sw stack services shop --format '{{.Name}}'); do
  sw service inspect $s --format '{{.Spec.Name}} image={{.Spec.TaskTemplate.ContainerSpec.Image}} replicas={{.Spec.Mode.Replicated.Replicas}} ports={{range .Endpoint.Ports}}{{.PublishedPort}}->{{.TargetPort}} {{end}} secrets={{range .Spec.TaskTemplate.ContainerSpec.Secrets}}{{.SecretName}} {{end}} constraints={{.Spec.TaskTemplate.Placement.Constraints}}'
done | tee inventory.txt

What each line does

  • Weighted questions — Cloud, autoscaling and scale weigh most; edge hardware and a tiny team pull towards Swarm.
  • read a — Interactive; answer for your real project.
  • service inspect --format — Prints image, replicas, ports, secrets and constraints for each service.
  • inventory.txt — The migration checklist: every line must have a Kubernetes twin before cutover.
  • Constraints — Pinned services such as the database are the hard part of every migration.

Green output

$ bash decide.sh
Running on AWS, GCP or Azure with a managed option? [y/n] y
Need autoscaling for traffic peaks? [y/n] y
More than 15 services or more than 2 teams? [y/n] n
Stateful services that must move between nodes? [y/n] y
Need RBAC, NetworkPolicy or admission policy? [y/n] n
Edge or on-premise boxes with little RAM? [y/n] n
Team of 3 or fewer with no Kubernetes skills? [y/n] n
score 5: choose Kubernetes
shop_db image=postgres:17-alpine replicas=1 ports= secrets=db_password  constraints=[node.hostname == node3]
shop_orders image=127.0.0.1:5000/orders:1.1.0 replicas=6 ports=8080->80  secrets=db_password  constraints=[]

Expected result. A cloud-hosted shop with sale-day peaks scores clearly for Kubernetes, and the inventory shows two services, one secret and one pinned database to move.

Example 2: Convert the stack with Kompose and fix the gaps

Goal. Convert the stack with Kompose and fix the gaps.

convert.sh

mkdir -p migrate && cd migrate
kompose convert -f ../swarm/stack.yml -o raw/ 2>&1 | tail -4
ls raw/
grep -n "replicas\|image:" raw/orders-deployment.yaml
grep -c "readinessProbe" raw/orders-deployment.yaml || printf 'no readinessProbe\n'
grep -n "kind:" raw/db-deployment.yaml
mkdir -p fixed
cp ../k8s/postgres.yaml fixed/
sed 's#127.0.0.1:5000/orders#orders#' raw/orders-deployment.yaml > fixed/orders-deployment.yaml
kubectl -n shop patch --dry-run=client -o yaml --local -f fixed/orders-deployment.yaml --type=json -p '[
 {"op":"add","path":"/spec/template/spec/containers/0/readinessProbe","value":{"httpGet":{"path":"/version","port":80}}}]' > fixed/orders.yaml
kubectl apply -n shop -f fixed/postgres.yaml -f fixed/orders.yaml --dry-run=server

What each line does

  • kompose convert on a stack file — Reads deploy.replicas, ports, secrets and volumes from the same file Swarm used.
  • replicas carried over — Kompose keeps 6 replicas from the stack.
  • No readinessProbe — Swarm's healthcheck becomes a liveness probe at best; readiness must be added by hand.
  • db as a Deployment — Replaced with the StatefulSet from 67.3.
  • --dry-run=server — The API server validates the fixed files without changing the cluster.

Green output

$ bash convert.sh
INFO Kubernetes file "raw/orders-service.yaml" created
INFO Kubernetes file "raw/orders-deployment.yaml" created
INFO Kubernetes file "raw/db-deployment.yaml" created
INFO Kubernetes file "raw/db-password-secret.yaml" created
dbdata-persistentvolumeclaim.yaml  db-deployment.yaml  db-password-secret.yaml  orders-deployment.yaml  orders-service.yaml
  replicas: 6
      image: 127.0.0.1:5000/orders:1.1.0
no readinessProbe
kind: Deployment
statefulset.apps/db configured (server dry run)
deployment.apps/orders configured (server dry run)

Expected result. Kompose did most of the typing again, and the same three fixes as in 66.5 made the output production-shaped. Delete raw/db-password-secret.yaml, because the Secret must come from the command line or a secrets manager, never from a file in Git.

Example 3: Parallel run, traffic shift, data copy and cutover

Goal. Parallel run, traffic shift, data copy and cutover.

shift.sh

printf '%s\n' \
 'upstream shop {' \
 '  server host.docker.internal:8080 weight=9;' \
 '  server host.docker.internal:30080 weight=1;' \
 '}' \
 'server { listen 80; location / { proxy_pass http://shop; } }' > lb.conf
docker run -d --name lb -p 9000:80 --add-host host.docker.internal:host-gateway \
  -v "$PWD/lb.conf:/etc/nginx/conf.d/default.conf:ro" nginx:1.27-alpine
docker exec node1 docker service update --publish-add 8080:80 shop_orders >/dev/null 2>&1 || true
for i in $(seq 1 100); do curl -s localhost:9000/version; done | sort | uniq -c
# data copy: Swarm Postgres to Kubernetes Postgres
DBT=$(docker exec node3 docker ps -q --filter name=shop_db)
docker exec node3 docker exec $DBT pg_dump -U postgres -Fc postgres > shop.dump
kubectl -n shop exec -i db-0 -- pg_restore -U postgres -d postgres --clean --if-exists < shop.dump
# cutover: all traffic to Kubernetes
sed -i 's/weight=9;/weight=1 backup;/; s/30080 weight=1/30080 weight=10/' lb.conf
docker exec lb nginx -s reload
for i in $(seq 1 100); do curl -s localhost:9000/version; done | sort | uniq -c

What each line does

  • nginx upstream weights — Stands in for Route 53 weighted records or ALB weighted target groups in production.
  • weight=9 and weight=1 — 90 percent to Swarm, 10 percent to Kubernetes, like a canary.
  • pg_dump -Fc — Custom-format dump that pg_restore can load with --clean.
  • backup — Swarm stays as a hot fallback after cutover, which is your rollback plan.
  • nginx -s reload — Changes weights without dropping connections.

Green output

$ bash shift.sh
4b7c...
     91 1.1.0  (swarm)
      9 1.1.0  (k8s)
pg_restore: restored 12 tables
    100 1.1.0  (k8s)

Expected result. Traffic moved from 90/10 to 100 percent Kubernetes with Swarm kept as a backup. In production you would stop writes or use logical replication before the final data copy, then remove the Swarm after a quiet week.

Every command, step by step (kind lab with kubectl)

  1. Decide

    bash decide.sh
    

    What it does: Score.

    score 5: choose Kubernetes
    
  2. Inventory

    docker service inspect shop_orders --format ...
    

    What it does: List.

    shop_orders image=... replicas=6
    
  3. Convert

    kompose convert -f swarm/stack.yml -o raw/
    

    What it does: Raw YAML.

    INFO Kubernetes file created
    
  4. Validate

    kubectl apply -f fixed/ --dry-run=server
    

    What it does: Checked.

    configured (server dry run)
    
  5. Apply

    kubectl apply -n shop -f fixed/
    

    What it does: Live.

    deployment.apps/orders configured
    
  6. Proxy

    docker run -d --name lb -p 9000:80 ... nginx:1.27-alpine
    

    What it does: Split.

    4b7c...
    
  7. Test

    curl -s localhost:9000/version
    

    What it does: Mixed.

    1.1.0
    
  8. Dump

    pg_dump -U postgres -Fc postgres > shop.dump
    

    What it does: Backup.

    (no output)
    
  9. Restore

    kubectl -n shop exec -i db-0 -- pg_restore -U postgres -d postgres --clean
    

    What it does: Data.

    restored
    
  10. Cutover

    docker exec lb nginx -s reload
    

    What it does: All to K8s.

    (no output)
    

When it is red 1

WARN Unsupported root level secrets key

Cause. Older Kompose release.

Fix, step by step

  1. Use Kompose 1.38.0 or create Secrets by hand.

When it is red 2

pg_restore: error: input file appears to be a text format dump

Cause. Dump made without -Fc.

Fix, step by step

  1. Use pg_dump -Fc or load with psql.

When it is red 3

502 Bad Gateway from lb

Cause. host.docker.internal not resolved.

Fix, step by step

  1. Add --add-host host.docker.internal:host-gateway.

When it is red 4

Kompose output uses 127.0.0.1:5000 image

Cause. Swarm-local registry name.

Fix, step by step

  1. Push to ECR and update the image.

When it is red 5

Duplicate orders after cutover

Cause. Writes hit both databases.

Fix, step by step

  1. Freeze writes or use logical replication before the final copy.

Practice tasks

Easy Task 1: Score

Why: Decide.

What to do: Run the checklist.

How:
  1. Your project.
Solution
bash decide.sh

Easy Task 2: Inventory

Why: List.

What to do: Inventory the stack.

How:
  1. inventory.txt.
Solution
cat inventory.txt

Easy Task 3: Convert

Why: Kompose.

What to do: Convert stack.yml.

How:
  1. raw/.
Solution
ls raw

Medium Task 4: Fix

Why: Gaps.

What to do: Add readiness, StatefulSet, remove Secret file.

How:
  1. fixed/.
Solution
ls fixed

Medium Task 5: Validate

Why: Dry run.

What to do: Server dry run.

How:
  1. No errors.
Solution
kubectl apply -f fixed --dry-run=server

Medium Task 6: Split

Why: 90/10.

What to do: Run the weighted proxy.

How:
  1. Count.
Solution
curl -s localhost:9000/version

Hard Task 7: Data

Why: Copy.

What to do: pg_dump and pg_restore.

How:
  1. Tables.
Solution
kubectl -n shop exec db-0 -- psql -U postgres -c '\dt'

Hard Task 8: Startup

Why: Main task.

What to do: Full migration and DECISION.md.

How:
  1. Cutover.
Solution
cat DECISION.md

Homework: try at home

For a Pune logistics startup like Delhivery's early stack, score the move with the checklist, inventory a three-service Swarm stack, convert it with Kompose, fix it, run both behind weighted nginx, copy the database and cut over, then write DECISION.md.

Recap

  • Pick Swarm for small, simple, edge or on-premise setups; Kubernetes for cloud, scale and many teams.
  • Inventory every service, secret, volume and constraint before migrating.
  • Kompose converts the stack file; fix probes, storage and secrets by hand.
  • Run both platforms in parallel and shift traffic with weights.
  • Keep Swarm as a fallback until the new platform proves itself.

FAQ

Q. Who still runs Swarm in production?

Mostly smaller companies, on-premise and edge setups, and products that ship a Swarm stack to customers. Mirantis continues to support Swarm for enterprise customers.

Q. Is Nomad another option?

Yes. HashiCorp Nomad schedules containers and plain binaries and is used by companies like Cloudflare. It sits between Swarm and Kubernetes in complexity, but its licence changed in 2023 to the Business Source Licence.

Q. Should we migrate straight to EKS?

Usually yes on AWS, with managed node groups or Karpenter, so the team does not run a control plane. On-premise, K3s or a vendor distribution such as OpenShift is common.

Q. How long does a migration take?

For a dozen services, a few weeks: one for inventory and conversion, one or two for parallel running, and one for data cutover and cleanup.

Interview questions

  1. How would you migrate from Swarm to Kubernetes?

    Model answer: Inventory, convert with Kompose and fix, run in parallel, shift traffic with weights, migrate data, cut over, keep a rollback path.

  2. What does Kompose not handle well?

    Model answer: Readiness probes, StatefulSets, secrets, resources and security context.

  3. When would you advise staying on Swarm?

    Model answer: Few services, small team, edge or on-premise hardware, no autoscaling need.

  4. What is a decision record?

    Model answer: A short document with context, options, decision and consequences, kept with the code.

Quick quiz

Q1. What does the inventory list?

Services, ports, secrets, configs, volumes, constraints.

Q2. Which tool converts the stack file?

Kompose.

Q3. How is traffic split in production?

Weighted DNS or ALB target groups.

Q4. How is Postgres data copied?

pg_dump -Fc and pg_restore.

Q5. What is the rollback plan?

Keep Swarm as backup.

Samajla ka?

Lab

  1. Install Docker Engine 28 or newer, kind and kubectl.
  2. Use the orders image from Chapter 66, tagged 1.0.0 and 1.1.0.
  3. Keep Swarm files in swarm/ and Kubernetes files in k8s/ inside one folder.
  4. Create secrets from the command line with placeholder values only.
  5. Clean up with docker swarm leave --force and kind delete cluster --name ch67.

Practice task

Run the orders API with a Postgres database on a three-node Swarm and on a three-node kind cluster, perform the same five operations on both (deploy, scale to 6, rolling update, kill a node, roll back), and write a one-page comparison with real timings.

Ravindra Bagale's Tip

Interview madhe 'Swarm aani Kubernetes madhe farak kay?' ha prashna khup common aahe. Fakt theory sangu naka; donhi var same app chalvun je baghitla te sanga.

In short

  • Swarm is built into Docker and is simple; Kubernetes is bigger but is the industry standard.
  • The same image runs unchanged on both; only the deployment files differ.
  • Both do scaling, rolling updates, secrets and self-healing, with different depth.
  • Kubernetes wins on ecosystem, autoscaling, storage and managed services like EKS and GKE.
  • Swarm still fits small teams and edge sites that want one binary and little to operate.