Free Guide
How Software Gets to Production: A Map for Developers
Docker, Kubernetes, AWS, CI/CD, DNS, and servers are not six competing answers. They belong to different stages of one journey, from code sitting on your laptop to software answering real requests on the internet.
Beginners are often introduced to these names one at a time. The result is vocabulary without a map: Docker sounds like a place to host an app, Kubernetes sounds like the advanced version of Docker, and "the cloud" sounds like the destination for everything. The individual definitions can be correct while the overall picture stays blurry.
This guide builds the picture first. We will follow a Python API from source code to a running process, give it a public address, package it into a container, release it through a pipeline, and finally locate Kubernetes. You will finish knowing which question each layer answers and, just as importantly, which layers a small application can skip.
The whole map
Python source code
│ starts as
▼
running process ── listens on a port
│ reached through
▼
DNS + HTTPS + routing
│ packaged as
▼
container image ── stored in a registry
│ released by
▼
deployment platform + CI/CD
│ kept dependable by
▼
logs + metrics + health checks + backups
│ coordinated at larger scale by
▼
an orchestrator, sometimes Kubernetes
Read the map as a sequence of questions. What is running? How does traffic reach it? What environment does it need? Where is the release stored? Who starts it and replaces it after a crash? How do you know it is healthy? If the system grows, who coordinates all its workloads?
No tool answers every question. That is the first idea to keep. Docker packages an environment but does not buy you a public domain. AWS offers many hosting services but does not decide which one your application needs. Kubernetes coordinates workloads but does not make an unnecessary distributed system simpler.
Code becomes a process
A Python file is text on disk. When you run it, the operating system starts Python and creates a process. That process gets memory and CPU time, can read configuration, writes output, and eventually exits or crashes.
For a web API, the process runs a server such as Gunicorn or Uvicorn. It listens on a port, perhaps 8000. A port is a numbered entry point on a machine. When you visit http://localhost:8000, your browser is connecting to port 8000 on your own computer, where the process is waiting for requests.
Moving that same process to another machine does not automatically make it production-ready. Something must start it after a reboot, restart it after a crash, supply its environment variables, capture its logs, and prevent an unfinished start-up from receiving user traffic. A managed application platform can take on most of those jobs. On a virtual machine, you configure more of them yourself.
The foundation never disappears
Containers and cloud platforms add layers around the process. At the centre, your Python application is still an operating-system process listening for work.
How a request finds the process
Suppose a client calls https://api.example.com/articles. DNS first translates the domain into an address. HTTPS establishes an encrypted connection and lets the client verify the server's identity. A reverse proxy or load balancer accepts the public request, then forwards it to an application process on an internal port.
client
│ GET https://api.example.com/articles
▼
DNS
│ api.example.com → destination address
▼
HTTPS endpoint / load balancer
│ certificate, routing, health check
▼
Python process on an internal port
│
├── PostgreSQL
├── Redis
└── upstream APIs
│
▼
JSON response
This request path also gives you a debugging order. If the process answers locally but the domain times out, changing the route handler is unlikely to help. Check DNS, firewall rules, the public listener, TLS, routing, port mapping, and health status. "The API is down" describes the user's experience, not the layer that failed.
What Docker changes
Without a container, your application depends on the machine to have the right Python version, operating-system libraries, and installed packages. Docker lets you describe that environment in a Dockerfile and build it into an image. The image is an immutable package containing the application and the environment it expects.
When that image starts, it becomes a container. The application process runs inside the container's isolated filesystem and network view. One image can start many containers, all based on the same packaged release.
| Thing | What it is | What it is not |
|---|---|---|
| Dockerfile | A build recipe | A running application |
| Image | An immutable application artefact | A server or hosting account |
| Container | A running instance of an image | A durable database by default |
| Registry | Storage for versioned images | A platform that operates them |
Docker solves repeatability. The image that passed your tests can be the image the production platform pulls and starts. It does not automatically provide DNS, HTTPS, backups, monitoring, scaling, or a safe release process. Those responsibilities belong to the surrounding platform and operations layer.
If you want to build the package rather than just place it on the map, the Dockerizing a Python API guide walks through a Dockerfile line by line.
Registry, deployment, and CI/CD
A registry is the bridge between building and running. Your pipeline builds an image, gives it an identifiable tag or digest, and pushes it to a registry such as Docker Hub, GitHub Container Registry, or Amazon ECR. The deployment platform then pulls that exact image and starts containers from it.
commit
↓
test
↓
build image
↓
scan and push to registry
↓
deploy new release
↓
wait for health checks
↓
verify or roll back
That sequence is CI/CD without the mystique. Continuous integration checks each change and produces a trustworthy artefact. Continuous delivery or deployment moves that artefact toward production through a repeatable release process. The important properties are not the logo on the pipeline. They are that the same steps run every time, a release can be identified, failures stop the rollout, and the previous release remains available for rollback.
Deployment is only the moment a release changes. Operations is everything required to keep the service dependable afterward: logs that explain events, metrics that expose trends, traces that connect distributed work, alerts that reach a person, backups that have been restored in a test, security updates, capacity planning, and incident response.
Where "the cloud" fits
The cloud is a catalogue of rented infrastructure and managed services. You can rent a virtual machine and operate almost everything yourself. You can choose a managed application platform that accepts source code or a container and handles much of the routing and process supervision. You can use a managed container service, a serverless container platform, or a Kubernetes service. You can also keep the application simple while putting the database on a managed service with automated backups and failover.
| Option | You gain | You still own |
|---|---|---|
| Managed app platform | Fast deployment, HTTPS, logs, restarts | Application, data choices, configuration, verification |
| Virtual machine | Control and a familiar server | Patching, routing, process supervision, backups |
| Managed containers | Image-based releases without managing hosts | Service design, networking choices, observability |
| Managed Kubernetes | A powerful cluster control plane | Workload design and substantial platform complexity |
Managed does not mean responsibility disappears. It means the boundary moves. A provider may patch the database engine, while you still decide retention, access controls, schema migrations, and whether restore procedures work.
Where Kubernetes fits
Kubernetes is an orchestrator. You declare desired state across a cluster: which images should run, how many instances should exist, which services should be reachable, and how updates should roll out. Kubernetes controllers watch the real state and continually act to make it match.
That is valuable for organisations operating many workloads across machines, especially when they need consistent scheduling, service discovery, rollout policies, resource controls, and a shared platform for multiple teams. It is not the inevitable next step after learning Docker.
A managed application platform may already restart failures, scale containers, route traffic, manage certificates, and roll out releases. If it meets your requirements, adding Kubernetes gives you another system to configure, secure, upgrade, monitor, and debug without establishing a new capability the product needs.
Ask for the missing capability
Before choosing Kubernetes, complete this sentence: "Our current platform cannot reliably ________, and Kubernetes would provide it by ________." If the blanks stay vague, the decision is not ready.
A small production architecture
Imagine a Python API for a paid reading app. It serves a few thousand users, stores accounts and entitlements, synchronises reading positions, and provides downloadable book files. It needs secure releases and dependable data, but it does not need a platform team.
mobile and web clients
│ HTTPS
▼
managed container service ── Python API image
│
├── managed PostgreSQL
├── object storage
└── logs, metrics, health checks
Git commit ── tests ── image registry ── deploy ── verify
Docker packages the API. The managed platform runs it and handles public traffic. PostgreSQL owns accounts, purchases, and progress. Object storage holds large files. A pipeline releases an immutable image after tests pass. Monitoring watches errors and latency. Kubernetes adds no necessary capability, so it is absent.
This is not a toy architecture. It is a deliberately small production architecture. Professionalism comes from meeting the requirements and operating the result, not from maximising the number of infrastructure layers.
The decision rule
Start with requirements: traffic, availability, data durability, release frequency, security constraints, budget, and the team's ability to operate the system. Choose the smallest platform that meets them. Add a layer only when you can name the failure, limit, or repeated operational burden it removes.
The map stays useful even as products change. Code still becomes a process. Traffic still needs a route. Data still needs durable storage. Releases still need identity and rollback. Failures still need to become visible. Tools are replaceable answers to those stable questions.