LLMOpsThe LLMOps lifecycle · MLflow 3 · FastAPI · Docker · AWS/Azure
Dashboard
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
41 small wins to finish your pathNext lesson →

Packaging the app in a container

A container packages the app from Part 4, its Python version, its dependencies and its entry command, into one image that runs the same on your laptop, a teammate's machine or a cloud host.

Last updated: 15 Oct, 2026 · LLMOps

The Deployment choices, and why lesson named the container as the common middle ground for serving an app. A container is built from a Dockerfile, a short recipe that layers the image step by step. This lesson writes one for the FastAPI shop assistant and shows the two commands that build and run it. The commands are shown, not run here, because building an image needs Docker installed on the machine.

A Dockerfile for the FastAPI app

The recipe starts from a slim Python base, installs the dependencies from requirements.txt in their own layer so a code change does not reinstall them, copies the project, and ends with the command that serves the app. The final line is the same uvicorn invocation that runs the Part 4 entry point, here bound to the container's own port.

dockerfile
FROM python:3.11-slim
WORKDIR /app

# install dependencies first so this layer is cached across code changes
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# copy the app and serve it with uvicorn on the container's port
COPY . .
EXPOSE 8000
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]

Building and running the image

Two commands turn the recipe into a running app: build reads the Dockerfile and produces a named image, and run starts a container from it and maps the container's port to a port on the host. Both need Docker on the machine, so they are shown rather than run in this course.

bash
docker build -t shop-assistant .
docker run -p 8000:8000 shop-assistant

With the container running, the app answers on the mapped port exactly as it did when you started it by hand, the difference being that the image carries its own Python and dependencies and needs none installed on the host.

What the container gave you

  • One artifact, so the app, its Python version and its pinned dependencies ship together and run the same everywhere.
  • A cached dependency layer, because requirements are installed before the code is copied, so editing the app rebuilds fast.
  • A host-independent unit, which is what a cloud service or an orchestrator schedules, and what the next lessons deploy.
A container image drawn as four stacked layers: FROM python:3.11-slim as the base image, then COPY requirements.txt and RUN pip install, marked cached because dependencies rarely change, then COPY dot dot for your code which changes on every commit, then CMD uvicorn as the command the host runs, making one image of code, Python and pinned dependencies. Two panels beside it explain why this order: a code change rebuilds only the last two layers rather than the pip install; and a red warning never to COPY a .env, because an image is a second place a secret leaks and it travels.

A folder of files against a container image

A folder of filesA container image
CarriesOnly your codeCode, Python and dependencies
Runs the same elsewhereOnly if the host matchesYes, by design
Set up on a new hostInstall Python and pip installPull the image and run
Scheduled byNothing; you start itA cloud service or orchestrator

Where a container earns its keep

  • Shipping the app to any cloud, where the host runs the image without a matching Python environment.
  • A reproducible run, where a teammate or a CI job gets the exact same environment from the image.
  • The unit an orchestrator scales, where the next part runs many copies of the one image behind a load balancer.
Watch out. A container image is a second place a secret can leak. Do not copy a .env file or an API key into the image, because anyone who pulls it can read it. Pass secrets in at run time as environment variables, and keep a .dockerignore so local keys and caches never land in the build.
Try it yourself
  • Install Docker, then build and run the image and post a message to the app on the mapped port.
  • Add a .dockerignore that excludes .env and __pycache__ and confirm they stay out of the image.
  • Pass a setting as docker run -e and read it in the app, instead of baking it into the image.

Every expert started right here.