You know how to use both. You brought Aurora Libros up with compose.yaml and per-environment overrides in module 4, and you deployed it on a cluster with a StatefulSet, Ingress, HPA and a rehearsed rollback in module 6. This lesson is not here to tell you which one wins, but to give you the criteria to decide which one your project needs, with the exact equivalences between both formats and an honest figure for the cost of maintaining a cluster.
Contents
- They are not the same category of tool
- The right question
- An in-depth comparison, dimension by dimension
- Conceptual equivalences
- The same
aurora-api, in both formats - Decision tree
- The middle road: Compose in production
- Swarm as a stepping stone, and the services that run Compose
- Kompose and the real limits of conversion
- The hidden cost of Kubernetes
- Aurora Libros at three sizes
- They are not the same category of tool
Comparing Compose with Kubernetes is like comparing a recipe with an industrial kitchen. Both are there so you can eat, but one is a document and the other is a system with staff.
| Docker Compose | Kubernetes | |
|---|---|---|
| What it is | A descriptor for a multi-container application, plus a CLI that applies it | A distributed orchestrator with its own control plane |
| Where it lives | On your machine, talking to a daemon | On a cluster of nodes, with etcd and controllers |
| What it does when something falls over | Restarts the container according to restart: |
Reschedules the workload onto another node |
| Execution model | A docker compose up that you run |
Permanent reconciliation loops |
| Desired state | Exists for as long as the command lasts | Persistent in etcd, watched at all times |
| Smallest unit | The container | The Pod (one or more containers) |
The structural difference is in the fourth row. Compose runs what you ask for and finishes; Kubernetes stores your intent and never stops comparing it with reality. That loop is what gives you a node failing at four in the morning and the Pods reappearing on another one without waking anybody. It is also what forces you to maintain a control plane.
- The right question
It is not "which one is better?". It is a battery of concrete questions:
- Can you afford for the platform to be down for fifteen minutes while you restart a machine?
- How many nodes are you really going to have within a year: one, three or thirty?
- Is there anybody on the team who can debug a
CrashLoopBackOffwithout searching the Internet? - Who is on call at three in the morning, and is being paid for it?
- Is your load constant, or does it have ×8 peaks that demand autoscaling?
- Does the budget cover the control plane plus the nodes with spare headroom?
If you answered "fifteen minutes is acceptable, one node, nobody, nobody, constant, no" — which is the situation of most projects — then Compose is the professional answer, and saying so out loud will save you a year of suffering. If you answered the opposite on three or more questions, Kubernetes starts to pay for its cost.
- An in-depth comparison, dimension by dimension
| Dimension | Docker Compose | Kubernetes |
|---|---|---|
| Mental model | One file, services, up/down |
Declarative API, objects, controllers, selectors |
| Learning curve | Hours | Weeks, and months to operate it well |
| Scope | One host (or Swarm, with deploy:) |
Tens or thousands of nodes |
| High availability | Whatever the host has: if it falls, everything falls | Reschedules onto another node automatically |
| Rescheduling on failure | Does not exist | The Deployment guarantees it |
| Manual scaling | --scale api=4 on the same host |
kubectl scale, across the whole cluster |
| Autoscaling | No | HPA, VPA and Cluster Autoscaler |
| Networking | Bridge with DNS by service name | CNI, Service, NetworkPolicy, optional mesh |
| Discovery | Docker's internal DNS | Cluster DNS + stable Service objects |
| Load balancing | DNS round-robin or your own proxy | Service (L4) + Ingress (L7) |
| Storage | Local host volumes | PV/PVC, StorageClass, dynamic provisioning |
| Configuration | environment, env_file |
ConfigMap, mountable or injectable |
| Secrets | A file on disk or secrets: (Swarm) |
Secret + integration with external managers |
| Updates | Recreates the container: there is an outage | Rolling update with maxSurge/maxUnavailable |
| Rollback | Go back to the previous tag by hand | kubectl rollout undo, with history |
| Probes | healthcheck (a single one) |
Liveness, readiness and startup, separately |
| Observability | Daemon logs, Prometheus if you set it up | Native metrics and events, an enormous ecosystem |
| Extensibility | None to speak of | CRDs, operators, webhooks |
| Multi-tenancy | No | Namespaces, RBAC, quotas |
| Infrastructure cost | That of one VM | Control plane + nodes + spare headroom |
| Operational cost | Close to zero | High and permanent |
| Who maintains it | Anybody on the team | Somebody who knows how, on call |
| Team maturity required | One developer | At least one person with real experience |
The last three rows decide more projects than all the ones above put together, and they are the ones that show up least often in comparisons on the Internet.
- Conceptual equivalences
Almost everything you wrote in the Aurora Libros compose.yaml has a translation. What changes is not the idea, but how many objects it takes to express it.
| Compose | Kubernetes | Note |
|---|---|---|
services: api: |
Deployment + Service |
Two objects where there was one |
image: |
spec.containers[].image |
Identical; the same OCI image |
ports: "8080:8080" |
Service (internal) or Ingress (external) |
Exposure is split into two layers |
environment: |
ConfigMap + envFrom |
Configuration moves out of the descriptor |
secrets: / .env |
Secret (+ external manager) |
Secret is base64, not encrypted by default |
volumes: (named) |
PersistentVolumeClaim |
With a StorageClass and an access mode |
volumes: (bind mount) |
hostPath (avoid it) or a mounted ConfigMap |
Rarely what you want |
deploy.replicas |
spec.replicas |
Compose only honors it in Swarm |
deploy.resources.limits |
resources.requests / limits |
K8s distinguishes what it asks for from what it caps |
healthcheck: |
livenessProbe + readinessProbe + startupProbe |
From one probe to three semantics |
depends_on: condition: |
initContainers + readinessProbe |
There is no global ordering; there are retries |
restart: unless-stopped |
restartPolicy: Always (implicit) |
The Deployment already guarantees it |
networks: |
NetworkPolicy |
In Compose it isolates; in K8s you have to declare it |
profiles: |
Kustomize overlays | Both enable subsets |
compose.prod.yaml (override) |
overlays/prod with patches |
Same idea, different mechanics |
docker compose up -d |
kubectl apply -k overlays/prod |
One applies and exits; the other leaves controllers behind |
Two rows deserve a warning. The depends_on one: in Kubernetes global ordered startup does not exist; the API Pod will start even if PostgreSQL is not there, it will fail, and CrashLoopBackOff will retry it until the database answers. It is ugly to watch and it is the right behavior: it forces the application to tolerate its dependencies being absent, which is exactly what you learned in 06-01. And the Secret one: it is base64-encoded, not encrypted; without encryption at rest for etcd and RBAC set up properly, it is not a secret, it is text that is inconvenient to read.
- The same
aurora-api, in both formats
aurora-api, in both formatsCompose, as it ended up after module 4:
# compose.yaml (fragment)
services:
api:
image: ghcr.io/auroralibros/aurora-api:2.0.0
restart: unless-stopped
environment:
DB_HOST: aurora-db
DB_NAME: aurora_books
DB_USER: aurora
REDIS_URL: redis://aurora-cache:6379
LOG_LEVEL: info
secrets: [db_password]
ports: ["8080:8080"]
depends_on:
aurora-db: { condition: service_healthy }
aurora-cache: { condition: service_started }
healthcheck:
test: ["CMD", "node", "-e", "fetch('http://localhost:8080/health/live')"]
interval: 10s
timeout: 3s
retries: 3
start_period: 20s
deploy:
replicas: 3
resources:
limits: { cpus: "1.0", memory: 512M }
read_only: true
cap_drop: [ALL]
security_opt: ["no-new-privileges:true"]Twenty-eight lines. Now the same thing in Kubernetes, without cutting anything essential:
# k8s/base/api.yaml
apiVersion: v1
kind: ConfigMap
metadata: { name: aurora-config, namespace: aurora }
data:
DB_HOST: aurora-db
DB_NAME: aurora_books
DB_USER: aurora
REDIS_URL: redis://aurora-cache:6379
LOG_LEVEL: info
---
apiVersion: apps/v1
kind: Deployment
metadata: { name: aurora-api, namespace: aurora }
spec:
replicas: 3
selector:
matchLabels: { app: aurora-api }
template:
metadata:
labels: { app: aurora-api }
spec:
securityContext:
runAsNonRoot: true
runAsUser: 10001
containers:
- name: api
image: ghcr.io/auroralibros/aurora-api:2.0.0
ports: [{ containerPort: 8080, name: http }]
envFrom:
- configMapRef: { name: aurora-config }
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef: { name: aurora-secrets, key: db-password }
resources:
requests: { cpu: "250m", memory: "256Mi" }
limits: { cpu: "1000m", memory: "512Mi" }
startupProbe:
httpGet: { path: /health/live, port: http }
failureThreshold: 30
periodSeconds: 2
livenessProbe:
httpGet: { path: /health/live, port: http }
periodSeconds: 10
readinessProbe:
httpGet: { path: /health/ready, port: http }
periodSeconds: 5
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities: { drop: [ALL] }
---
apiVersion: v1
kind: Service
metadata: { name: aurora-api, namespace: aurora }
spec:
selector: { app: aurora-api }
ports: [{ port: 80, targetPort: http }]| Compose | Kubernetes | |
|---|---|---|
| Lines for the same service | 28 | 63 |
| Objects declared | 1 | 3 (ConfigMap, Deployment, Service) |
| Files in a complete project | 1 + overrides | Dozens, plus the overlays |
| What you gain | — | Rescheduling, separate probes, RBAC, HPA |
The ratio of more than twice the YAML is not a cosmetic detail: it multiplies per service, and with four services and three environments you end up with a directory nobody reads in full any more. In exchange, every extra line buys something real. The question is whether you need what it buys.
- Decision tree
graph TD
A["How much downtime do you tolerate?"] -->|"Minutes, no drama"| B["More than one node?"]
A -->|"Seconds or none"| E["Is there anybody who knows K8s?"]
B -->|No| C["Unpredictable load peaks?"]
B -->|Yes| E
C -->|No| D["**Compose on one host**<br/>set up properly"]
C -->|Yes| F["Does it fit in a managed<br/>container service?"]
F -->|Yes| G["**Cloud Run / Container Apps**<br/>autoscales with no cluster"]
F -->|No| E
E -->|"No, and we cannot hire"| H["**Managed or Compose**<br/>never self-managed K8s"]
E -->|Yes| I["Budget for a control<br/>plane + headroom?"]
I -->|No| H
I -->|Yes| J["**Managed Kubernetes**<br/>module 6 as it stands"]
Five criteria, and not one of them is "what everybody else is doing". Notice that the path towards Kubernetes demands two yeses in a row — knowledge and budget — and that node H exists because the worst possible decision is a self-managed cluster nobody knows how to repair.
- The middle road: Compose in production
There is a very widespread and false idea: that Compose "is not for production". Compose on a properly set up host serves millions of requests a day at real companies. What it takes is setting it up with judgment:
# compose.prod.yaml — what turns a toy into production
services:
api:
image: ghcr.io/auroralibros/aurora-api@sha256:9f2c... # digest, not a tag
restart: unless-stopped # survives a reboot
logging:
driver: json-file
options: { max-size: "10m", max-file: "3" } # the disk does not fill up
deploy:
resources:
limits: { cpus: "1.0", memory: 512M } # nobody eats the host
healthcheck: { test: ["CMD", "node", "healthcheck.js"], interval: 10s }| Production requirement | How Compose meets it |
|---|---|
| Starting after a host reboot | restart: unless-stopped + Docker under systemd |
| Not losing data | aurora-data volume + an external, tested backup |
| Not filling the disk | Log rotation + scheduled docker system prune |
| Reproducible image | Reference by digest, never :latest |
| Updating without a long outage | docker compose up -d --pull always (seconds) |
| Rolling back | The previous digest is in Git; up -d again |
| Monitoring | cAdvisor + Prometheus + alerts (05-06) |
| Backups | Scheduled pg_dump to remote storage |
| TLS certificate | Caddy or Traefik in front, with automatic renewal |
The two gaps it cannot close: the host is a single point of failure (if the machine dies, the service dies until you bring up another one) and there is no autoscaling. If the business tolerates those two gaps — and a great many businesses do — you have solved the platform with one file, with no control plane and no on-call rota.
An honest sizing rule: a 4 vCPU, 8 GB VM with a well-tuned Aurora Libros stack comfortably serves several hundred requests per second. Before assuming you need a cluster, measure with the load test from 06-06.
- Swarm as a stepping stone, and the services that run Compose
If the only problem is the single point of failure, there is an intermediate step before the big leap.
| Option | What it solves | What it costs | Status in 2026 |
|---|---|---|---|
| Docker Swarm | Several nodes, rescheduling, rolling updates | Very little learning: deploy: in the same file |
Maintained, with barely any evolution |
| Compose + a standby host | Recovering quickly after an outage | A well-rehearsed manual procedure | Trivial |
| ECS with Compose | Runs a compose.yaml on AWS |
Tying yourself to the provider | In use |
| Cloud Run / Container Apps | Autoscaling with no cluster, even down to zero | Less control over networking and state | Very mature |
| Managed Kubernetes | Everything from module 6 | Cost and knowledge | The standard |
Swarm (06-03) deserves a paragraph of honesty: it is simple, it works, and taking your compose.yaml to three nodes costs you adding deploy: and running docker stack deploy. But its ecosystem is frozen, hiring somebody who knows it gets harder every year and third-party documentation is scarce. As a temporary stepping stone it is reasonable; as a five-year bet, think twice.
Cloud Run-style services are the new development that changes the decision tree: they autoscale from zero to hundreds of instances without any cluster existing to maintain. For ghcr.io/auroralibros/aurora-api:2.0.0 — stateless, with probes, twelve-factor and graceful shutdown — they fit without touching a thing. State moves to a managed database, and availability stops being your problem.
- Kompose and the real limits of conversion
kompose translates a compose.yaml into Kubernetes manifests. It is useful as a starting point and dangerous as a finished result.
kompose convert -f compose.yaml -o k8s/
# INFO Kubernetes file "aurora-api-service.yaml" created
# INFO Kubernetes file "aurora-api-deployment.yaml" created
# WARN Volume mount on the host "./data" isn't supported - ignoring
# WARN Service "aurora-db" won't be created because 'ports' is not specified| What it converts well | What it converts badly or ignores |
|---|---|
image, command, ports |
depends_on (it loses it: there is no ordering) |
environment → individual variables |
healthcheck → it does not generate the three probes |
deploy.replicas |
Bind mounts → a warning, and then it is on you |
restart → restartPolicy |
secrets → it leaves them as files or loses them |
networks (partially) |
profiles, extends, develop.watch |
No Ingress, HPA, PDB or NetworkPolicy at all |
The practical conclusion: kompose saves you the first 60 % of the typing and saves you none of the thinking. Everything that makes a deployment production-ready — separate probes, measured requests and limits, a PDB, a network policy, an Ingress with TLS — is still your work. Treat it as a draft, review it line by line, and never apply it directly against a real cluster.
- The hidden cost of Kubernetes
What does not appear in the slide decks:
| Cost | What it means in practice |
|---|---|
| The cluster | Control plane (managed or not) + nodes + headroom for rescheduling |
| Idle resources | You need spare capacity so the Pods from a failed node fit somewhere |
| Upgrades | New versions every few months, with limited support and APIs that get removed |
| The YAML debt | Dozens of files per environment; overlays cover it up but do not remove it |
| New failure modes | CrashLoopBackOff, ImagePullBackOff, Pending for lack of resources, Evicted under memory pressure, a PVC that will not bind, cluster DNS failing |
| Harder debugging | The problem can be in the app, the Pod, the node, the CNI, the CSI or the Ingress |
| Tooling around it | Helm or Kustomize, a GitOps setup, a secrets manager, a metrics system |
| Continuous training | The ecosystem changes faster than the knowledge consolidates |
| The on-call rota | Somebody has to answer at three in the morning |
The last row is the one that decides. Phrase it like this in the meeting: "if the cluster breaks on a Saturday at three in the morning, who fixes it, how fast, and paid how much?". If there is no answer with a person's name in it, Kubernetes is not an option yet, however good it looks on the architecture diagram.
And it is worth saying the other way round too: when the answer does exist, Kubernetes gives back far more than it costs. Real autoscaling, deployments with no outage, automatic rescheduling, per-team quotas and an ecosystem that solves problems you did not know you had. The mistake is not choosing Kubernetes; it is choosing it too early.
- Aurora Libros at three sizes
| Small shop | Growing | With campaign peaks | |
|---|---|---|---|
| Traffic | 5-20 req/s | 100-300 req/s | 40 req/s with ×8-15 peaks |
| Team | 2 developers | 6 people, 1 with systems skills | 4 developers |
| On-call | No | Business hours | No |
| Tolerable outage | 30 min | 5 min | Zero during a campaign |
| Recommendation | Compose on one host | Managed Kubernetes | Cloud Run or similar |
| Why | One file, zero cluster, minimum cost | The traffic and the team already justify it | Autoscales with no cluster to maintain |
| Database | On the same host, with tested backups | Managed, with a replica | Managed, non-negotiable |
| Risk accepted | The host is a single point of failure | Permanent operational cost | Dependence on the provider |
| Next step | Monitoring and tested backups | PDB, HPA and an error budget | Measure cost per request at peak |
Look at the third column: a lot of traffic at peak does not imply Kubernetes. It implies autoscaling, and there is more than one way to get it. And in the second one, what tips the balance is not just the traffic: it is that there is a person with a systems profile. Without that person, the recommendation would be different even if the traffic were the same.
Common Mistakes and Tips
- Choosing Kubernetes for your CV. It is a real, human reason, and it is the worst of them all for the project. If you want to learn it, set up a lab cluster with kind, not the company's production platform.
- Believing that "Compose is not for production". With digests, limits, log rotation, tested backups and monitoring, Compose sustains real businesses. What it does not give you is tolerance to the host falling over, or autoscaling.
- Applying the output of
komposewithout reviewing it. It losesdepends_on, it does not generate the three probes or an Ingress, HPA or PDB, and it ignores bind mounts. - Migrating to Kubernetes without having measured. Before justifying a cluster on performance grounds, run the load test from 06-06 against the current host. The real limit is usually in the database, and a cluster does not move it.
- Translating
depends_onexpecting ordered startup. It does not exist. The application has to retry; if it does not, fix that before migrating. - Treating
Secretobjects as encrypted. They are base64. Without encryption at rest for etcd and strict RBAC, they protect nothing. - Tip: write the decision and its reasons in a short document inside the repository, with a date. A year from now you will want to know why it was chosen, and whether the premises still hold.
- Tip: keep the
compose.yamlalive even if you deploy on Kubernetes. It is the best way to bring the whole stack up locally to develop and debug. - Tip: when in doubt, start with Compose. Migrating from Compose to Kubernetes with a well-built image is days of work; dismantling a cluster nobody knows how to operate takes months.
Exercises
Exercise 1 — Translate a complete service. Take the aurora-cache service (redis:7-alpine, not exposed externally, with a volume for optional persistence, a 256 MB memory limit and a healthcheck using redis-cli ping) exactly as it stands in the compose.yaml. Write it in Kubernetes: Deployment, an internal Service and a PersistentVolumeClaim. State which element of the original has no direct equivalent and how you solve it.
Exercise 2 — Apply the decision tree. For each scenario, walk the tree from §6, state the final node and justify the decision in three sentences. (a) An internal invoicing API used by 30 employees during office hours, with one developer who also acts as the administrator. (b) A ticket-selling platform that sells out a concert in 90 seconds, with a five-person platform team and an on-call rota. (c) A corporate blog with 400 visits a day that an external supplier wants to deploy on a managed cluster "because it is the standard".
Exercise 3 — The real cost of the decision. Aurora Libros is growing and management asks whether "we should move to Kubernetes". Prepare a one-page comparison with: estimated monthly infrastructure cost in both scenarios (use approximate provider figures and state your assumptions), learning and setup time, what measurable improvement the cluster brings, what new risk it introduces, and your recommendation together with the condition that would have to be met to change it.
Solutions
Solution 1.
apiVersion: v1
kind: PersistentVolumeClaim
metadata: { name: aurora-cache-data, namespace: aurora }
spec:
accessModes: [ReadWriteOnce]
resources: { requests: { storage: 1Gi } }
---
apiVersion: apps/v1
kind: Deployment
metadata: { name: aurora-cache, namespace: aurora }
spec:
replicas: 1
strategy: { type: Recreate } # a single ReadWriteOnce PVC
selector: { matchLabels: { app: aurora-cache } }
template:
metadata: { labels: { app: aurora-cache } }
spec:
containers:
- name: redis
image: redis:7-alpine
args: ["--maxmemory", "200mb", "--maxmemory-policy", "allkeys-lru"]
ports: [{ containerPort: 6379, name: redis }]
resources:
requests: { cpu: "50m", memory: "128Mi" }
limits: { cpu: "500m", memory: "256Mi" }
livenessProbe:
exec: { command: ["redis-cli", "ping"] }
periodSeconds: 10
readinessProbe:
exec: { command: ["redis-cli", "ping"] }
periodSeconds: 5
volumeMounts: [{ name: data, mountPath: /data }]
volumes:
- name: data
persistentVolumeClaim: { claimName: aurora-cache-data }
---
apiVersion: v1
kind: Service
metadata: { name: aurora-cache, namespace: aurora }
spec:
selector: { app: aurora-cache }
ports: [{ port: 6379, targetPort: redis }]What has no direct equivalent is Compose's single healthcheck: here it has been split into liveness and readiness, both using redis-cli ping but with different periods, because in Kubernetes "it is alive" and "it can take traffic" are separate questions. Three more decisions the brief did not give you and that you have to reason about: strategy: Recreate instead of rolling, because a ReadWriteOnce PVC cannot be mounted on two Pods at once and the rolling update would get stuck; Redis's --maxmemory set below the container's limits.memory, so Redis evicts keys before the kernel kills the process with an OOM; and the absence of externally exposed ports, since a Service with no type is ClusterIP and can only be reached from inside the cluster, which is exactly what was asked for.
Solution 2.
(a) Internal invoicing API. Tolerable outage: hours, because outside office hours nobody is using it. A single node is enough and there are no peaks. Final node: Compose on a properly set up host. The single administrator cannot sustain a cluster, and a deployment with restart: unless-stopped, tested backups and basic monitoring covers the requirement with room to spare. Adding Kubernetes here would multiply the operational cost without improving anything you could notice.
(b) Ticket sales. The tolerable outage is zero during the 90 seconds that matter, there is a platform team and there is an on-call rota. The peak is brutal but predictable to the second, which lets you pre-scale ahead of the event instead of waiting for the HPA to react. Final node: managed Kubernetes. This is the textbook case: real high availability, autoscaling, deployments without outages and a team that can operate it. Here the cluster gives back what it costs.
(c) Corporate blog. 400 visits a day is about 0.005 requests per second on average. The tolerable outage is hours. Final node: Compose on one host, or plain static hosting if the content allows it. A managed cluster for this costs more per month than the blog itself generates in value, and it adds a dependency nobody on the team will know how to repair. Being "the standard" is not a requirement: ask the supplier to justify the decision using the same criteria as the tree and the conversation ends on its own.
Solution 3. Structure of the comparison (the amounts are illustrative and must be replaced with those of the real provider and region):
| Item | Compose on one host | Managed Kubernetes |
|---|---|---|
| Compute | 1 VM of 4 vCPU / 8 GB | 3 nodes of 2 vCPU / 4 GB + headroom |
| Control plane | 0 | The provider's fixed monthly cost |
| Database | On the host (or managed) | Managed, effectively mandatory |
| Load balancer and TLS | Traefik on the same host | The provider's load balancer, billed separately |
| Infrastructure | Baseline | Between 2.5 and 4 times the baseline |
| Setup | Already done | 2-4 weeks of real work |
| Training | 0 | 1-3 months until you operate it fluently |
| Maintenance | Host patches | Patches + cluster upgrades |
Measurable improvements from the cluster: automatic recovery when a node fails (from "minutes with human intervention" to "seconds without it"), autoscaling from 3 to 12 replicas under peaks, and outage-free deployments with a one-command rollback. New risks: dependence on knowledge that does not exist on the team today, more configuration surface that can fail, and a fixed cost that does not go down when traffic does.
Recommendation: stay on Compose and revisit the decision with a concrete trigger, not with a date. For example: "we move to managed Kubernetes when two of these three conditions are met: sustained host CPU above 60 %, the business setting an availability target above 99.9 %, or somebody with real experience operating clusters joining the team". Writing down the trigger turns a discussion of opinions into a verifiable condition, and that is the real deliverable of the exercise.
Conclusion
You can now defend the decision with criteria instead of with fashion. You are clear about the first and most forgotten point: they are not the same category of tool. Compose is a descriptor you run and it finishes; Kubernetes stores your intent in etcd and never stops reconciling it with reality. Every other difference follows from that one, including the good ones — automatic rescheduling, autoscaling, outage-free deployments — and the expensive ones.
You have the dimension-by-dimension comparison, with the three rows that decide more projects than any other: infrastructure cost, operational cost and team maturity. You have the complete equivalences table, with its two important warnings: depends_on has no translation because in Kubernetes ordered startup does not exist, and a Secret is base64, not encryption. And you have seen the same aurora-api in both formats: 28 lines against 63, one object against three, with the right question hanging over it — do you need what those extra lines buy?
The decision tree gives you five concrete criteria and a detail worth remembering: reaching Kubernetes demands two yeses in a row, knowledge and budget, and the worst option of all is a self-managed cluster nobody knows how to repair. You know that Compose in production is a perfectly professional decision when the host is set up properly — digests, limits, log rotation, tested backups, automatic TLS — with its two declared gaps: single point of failure and no autoscaling. You know the intermediate steps: Swarm, honest but frozen; the services that run your image without any cluster existing; and kompose, which saves you 60 % of the typing and none of the thinking.
And you take away the question that puts everything else in order: if the cluster breaks on a Saturday at three in the morning, who fixes it, how fast, and paid how much?. With the three versions of Aurora Libros — the small shop on Compose, the growing one on managed Kubernetes, the campaign one autoscaling without a cluster — you have three reference answers and the habit of writing the decision down, with its date and its review trigger, inside the repository.
In the next lesson we come down from the infrastructure to the desktop: Docker Desktop. What exactly that Linux VM you have been using without seeing it is, why it explains bind mount performance, what features it adds, what licensing terms it has — and why it is worth checking them before installing it at your company — and what alternatives exist on each operating system.
Docker: From Beginner to Advanced
Module 1: Introduction to Docker
- What Is Docker?
- Installing Docker
- Docker Architecture
- Basic Docker Commands
- Understanding Docker Images
- Creating Your First Docker Container
- The Course Project: The Aurora Libros Platform
Module 2: Working with Docker Images
- Docker Hub and Repositories
- Building Docker Images
- Dockerfile Basics
- Advanced Dockerfile Instructions
- Managing Docker Images
- Tagging and Publishing Images
Module 3: Docker Containers
- Running Containers
- Container Lifecycle
- Managing Containers
- Inspecting and Debugging Containers
- Docker Networking
- Data Persistence with Volumes
- Resource Limits and Restart Policies
Module 4: Docker Compose
- Introduction to Docker Compose
- Defining Services in Docker Compose
- Docker Compose Commands
- Multi-Container Applications
- Environment Variables in Docker Compose
- Profiles, Overrides and Multiple Environments
- Local Development with Docker Compose
Module 5: Advanced Docker Concepts
- Docker Networking Deep Dive
- Docker Storage Options
- Docker Security Best Practices
- Optimizing Docker Images
- Advanced Builds with BuildKit and Buildx
- Logging and Monitoring in Docker
- The Runtime Inside: Namespaces, Cgroups and Layers
Module 6: Docker in Production
- Preparing an Image for Production
- CI/CD with Docker
- Orchestrating Containers with Docker Swarm
- Introduction to Kubernetes
- Deploying Docker Containers in Kubernetes
- Scaling and Load Balancing
- Deployment Strategies and Rollback
