Ship the same environment everywhere.
Docker packages your Python app, its libraries and its system dependencies into an image that runs identically on your laptop, a teammate's machine and a server. CI/CD, usually GitHub Actions, runs your tests and evaluation set on every push and deploys when they pass. For AI projects with heavy dependencies and model files, both are the difference between 'works on my machine' and shipped.
- Docker
- A reproducible, portable environment in one image
- CI/CD
- Tests, evals and deploys on every push
- Python image
- python:3.x-slim as the base, almost always
- Free
- Docker Engine and GitHub Actions for public repos
- 5production AI apps
- 14engineers led
- 200+students mentored
Questions students ask
The Docker and CI questions searched most, answered from containerising AI services.
What is Docker and why do we use it?
Docker is a tool for running software in containers: isolated processes that carry their own file system, libraries and configuration, built from an image you define in a Dockerfile. We use it because environments are where projects break. An AI service depends on a specific Python version, dozens of packages, system libraries for images and audio, model files and environment variables; reproducing that by hand on every machine fails constantly. With Docker you describe it once, build an image, and run that image anywhere Docker runs, including every cloud platform. It also keeps projects from interfering with each other on one machine.
What is Docker in simple terms?
A container is a box with your program and everything it needs inside, and Docker is the tool that builds and runs the boxes. A virtual machine is a whole computer in a box, heavy and slow to start; a container shares the host's operating system kernel and only packs what differs, so it starts in a second and uses little memory. The Dockerfile is the recipe for the box; the image is the sealed box; the container is a running copy of it. Because the box is the same everywhere, 'it works on my machine' becomes 'it works in the box', which is all a server needs.
Is Docker for free?
The core is free and open source: Docker Engine, the command-line tools and the image format cost nothing on Linux. Docker Desktop, the Windows and Mac application that bundles the engine with a graphical interface, is free for personal use, education, open-source work and small businesses, with a paid subscription required for larger companies; check the current terms. Docker Hub hosts public images for free with rate limits on pulls. For a student everything you need is free, and the platforms you deploy to (Render, Cloud Run, Spaces) build and run your Dockerfile as part of their free tiers.
Why is Docker useful?
- Reproducibility: the same image runs on your laptop, in CI and in production.
- Isolation: each project's Python version and packages live apart; nothing conflicts.
- Deployability: every cloud platform accepts a Dockerfile; no server setup by hand.
- Onboarding: a new teammate runs one command instead of following a ten-page setup guide.
- Services: run Postgres, Redis, a vector database or an inference server locally with one compose file.
- Pinning: model files, system libraries and versions are fixed in the image, so a deploy months later behaves the same.
How to Docker Python application
Write a Dockerfile: start from a slim official Python image, set a working directory, copy the requirements file and install dependencies (this layer caches so rebuilds are fast), copy the rest of the code, expose the port and set the command that starts the app, such as Uvicorn for FastAPI. Add a .dockerignore that excludes the virtual environment, caches, data and secrets. Build the image with a tag, run it with the port mapped and environment variables passed in, and open the app in a browser. Use a compose file when the app needs a database or a vector store beside it. Keep secrets out of the image; inject them at run time.
What Python Docker image to use
For almost every service, the official image tagged with your Python version and the slim suffix: it is Debian-based, small, has the tools most packages need and receives security updates. The full image (no suffix) is much larger and only worth it when you compile native extensions in the container. Alpine-based images are smaller still but use a different C library, which makes many scientific and ML packages slow to install or break them; avoid Alpine for AI work. Pin the version tag (major and minor) rather than latest, so a base-image update cannot silently change your environment.
What is Python slim Docker?
The slim variant of the official Python image is a trimmed Debian image with Python installed and the development headers, compilers and documentation removed, roughly a fifth the size of the full image. It installs most pure-Python and pre-built binary packages without trouble; for the occasional package that needs to compile, you install the build tools in a build stage and copy only the result into the final slim image (a multi-stage build). It is the default base for the FastAPI services I deploy, because smaller images build, push and start faster, which matters on free tiers with slow cold starts.
Why Python Docker image is so big
Usually three causes. The base: the full Python image is several hundred megabytes before you add anything; switch to slim. The packages: PyTorch with CUDA support alone is gigabytes; install the CPU-only build if the container will not use a GPU, and avoid installing frameworks you do not use. The context: copying the whole project, including a virtual environment, datasets, model checkpoints and caches, bloats the image; a .dockerignore fixes it. Beyond that, multi-stage builds drop compilers, and pip's no-cache flag avoids storing downloaded wheels in the image. A lean FastAPI service with a model API client fits in a few hundred megabytes.
How to check Docker Python version
Run Python's version flag inside the container: start the image with the version command in place of the default, or open a shell in a running container and ask Python directly. In a Dockerfile, the base image tag states the version, which is the authoritative source. For a running service, add a health endpoint that returns the Python version and key package versions; it is a two-line addition that saves guessing during incidents. And pin the base image tag so the answer never changes without a deliberate edit.
Does GitHub have CI/CD?
Yes: GitHub Actions. A workflow file in the repository describes jobs that run on GitHub's machines when something happens, typically a push or a pull request: install dependencies, run tests, run the evaluation set, build the Docker image, and deploy or trigger a deploy on your platform. It is free for public repositories and includes a monthly allowance of minutes for private ones. For AI projects I run three jobs: unit tests for the deterministic code, the evaluation harness against a small labelled set so a prompt or retrieval change cannot ship a regression, and a build of the container. The brand-engine and this site's publishing workflows run on it.
From laptop to pipeline
Containerise and automate a Python AI service in an afternoon.
- 01
Dockerfile and ignore file
Slim base, pinned version, cached dependency layer, secrets injected at run time.
- 02
Compose for services
Postgres with pgvector and Redis beside the app for local development.
- 03
Actions workflow
Tests and the evaluation set on every push; build the image on main.
- 04
Deploy from the image
Render, Cloud Run or a Space builds the Dockerfile; environment variables set in the platform.
In the room
Where these systems get explained to students.


Read next
The service inside the container and where it runs.
A project that only works on one laptop?
I containerise AI services and set up CI with evals so every push is safe to deploy.

