Support

Free Guide

How Software Gets to Production: A Map for Developers

By Simon O'Connor · Updated 7 August 2026 · 11 min read

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

The production 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.

One request through production
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.

ThingWhat it isWhat it is not
DockerfileA build recipeA running application
ImageAn immutable application artefactA server or hosting account
ContainerA running instance of an imageA durable database by default
RegistryStorage for versioned imagesA 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.

A release pipeline
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.

OptionYou gainYou still own
Managed app platformFast deployment, HTTPS, logs, restartsApplication, data choices, configuration, verification
Virtual machineControl and a familiar serverPatching, routing, process supervision, backups
Managed containersImage-based releases without managing hostsService design, networking choices, observability
Managed KubernetesA powerful cluster control planeWorkload 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.

A small production architecture
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.


Next step

Use the Docker guide to package a small API, or follow Mastering APIs with Python when you want the complete applied path through Compose, AWS deployment, CI/CD, observability, and a production capstone.


Next Step:

This guide covers one production pattern. The book wires the patterns together into complete projects: API clients with retries and logging, OAuth logins, databases, test suites, and deployment to AWS. Three full chapters are free to read, no account needed.