Skip to content
Commitpedia
Log in Join & edit

Build and run a small Python web app in a container

Write a tiny Flask app, give it a Dockerfile, build the image, run it with a published port, make a change and rebuild, then clean up. A first hands-on project with Docker from an empty folder.

Drafted with AI from official documentation. Scripts were run in a test environment and repository links verified. Awaiting a human check. Did it work for you? Last edited Outdated?

Edit history of Build and run a small Python web app in a container: 1 recorded edit
Edit history · 1 edit
Type
Article
Edits
1
Editors
1
First issued
15 Oct 2026
Last edit
15 Oct 2026
Reading
4 min

What you will build

A web page served by a small Python (Flask) application that runs inside a Docker container. You will write the app, describe its image in a Dockerfile, build the image, run it on port 8000, look at its logs, change the code and rebuild, and finally remove everything. By the end you will have used the handful of commands that cover most daily Docker work.

Before you start

  • Docker installed and working: docker run hello-world prints a greeting. On Linux see the entry on installing Docker Engine; on macOS and Windows use Docker Desktop.
  • A terminal and a text editor. No Python installation is needed on your machine; Python runs inside the container.
  • About 20 minutes and roughly 200 MB of disk for the base image.

Step 1: Create the project folder and the app

mkdir hello-container
cd hello-container

Create app.py:

import os
import socket

from flask import Flask

app = Flask(__name__)


@app.get("/")
def index():
    name = os.environ.get("GREETING_NAME", "world")
    return f"<h1>Hello, {name}!</h1><p>Served from container {socket.gethostname()}</p>"


@app.get("/health")
def health():
    return {"status": "ok"}


if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8000)

Two things matter for containers here. The app listens on 0.0.0.0, not 127.0.0.1: inside the container, localhost is the container itself, and Docker’s port forwarding only reaches a process bound to all interfaces. And configuration comes from an environment variable, which is how containers are usually configured.

Create requirements.txt with the one dependency:

flask

Step 2: Write the Dockerfile

Create a file named exactly Dockerfile (no extension):

FROM python:3.13-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY app.py .

RUN useradd --create-home appuser
USER appuser

EXPOSE 8000
CMD ["python", "app.py"]
  • FROM python:3.13-slim: start from the official Python image, the slim variant without build tools.
  • WORKDIR /app: all following paths are relative to /app inside the image.
  • Dependencies are installed before the source is copied, so editing app.py later does not re-run pip install.
  • USER appuser: the app runs as a normal user, not root.
  • CMD: the command the container runs when it starts.

Also create .dockerignore so stray files never end up in the image:

__pycache__
*.pyc
.git

Step 3: Build the image

docker build -t hello-container:1.0 .
docker image ls hello-container

The final . is the build context: the folder whose files COPY may use. The first build downloads the base image and installs Flask; expect a minute or so. -t names the image. The listing shows an image of a bit over 100 MB.

Step 4: Run the container

docker run -d --name hello -p 8000:8000 hello-container:1.0
docker ps

Open http://localhost:8000 in a browser, or from the terminal:

curl http://localhost:8000
curl http://localhost:8000/health

You should see the greeting with a hostname that looks like 3f1c9a2b7d4e. That is the container’s ID: each container gets its own hostname.

Step 5: Look at logs and get a shell inside

docker logs hello
docker exec -it hello sh
ls -la /app
whoami
exit

logs shows Flask’s request log. exec -it ... sh opens a shell in the running container; whoami prints appuser, confirming the USER instruction worked.

Step 6: Change the configuration without rebuilding

docker rm -f hello
docker run -d --name hello -p 8000:8000 -e GREETING_NAME=Docker hello-container:1.0
curl http://localhost:8000

The page now says Hello, Docker. Settings that differ between environments belong in environment variables, so the same image runs everywhere.

Step 7: Change the code and rebuild

Edit app.py and change the <h1> text, then build a new version and replace the running container:

docker build -t hello-container:1.1 .
docker rm -f hello
docker run -d --name hello -p 8000:8000 hello-container:1.1
curl http://localhost:8000

This build is much faster: every layer up to COPY app.py came from the cache. Both 1.0 and 1.1 exist locally now, so rolling back is just running the old tag again.

Check that it works

  • docker ps lists hello with status Up and ports 0.0.0.0:8000->8000/tcp.
  • curl http://localhost:8000/health returns {"status":"ok"}.
  • docker logs hello shows a line for each request you made.
  • docker exec hello whoami prints appuser.

Clean up

docker rm -f hello
docker rmi hello-container:1.0 hello-container:1.1
docker image ls

The project folder stays; only the container and your two images are removed. Careful: docker rmi deletes the images, so rebuild with docker build if you want them back. You can also remove python:3.13-slim with docker rmi python:3.13-slim if you do not plan to build again.

Next steps

  • Add a database and run both with Compose: see the entry on docker compose for an app with a PostgreSQL database.
  • Replace Flask’s development server with a production server such as gunicorn in the CMD.
  • Push the image to a registry so you can run it on another machine: see the entry on tagging, pushing and pulling images.
  • Read the Dockerfile entry for ENTRYPOINT, multi-stage builds and cache ordering.

Official documentation

Did this work for you?

No votes yet. Be the first.

If it did not work, the report form below tells the editors what went wrong.

See also

  • 0002 Python cheat sheet: strings, lists, dicts and f-strings The everyday methods for text, lists and dictionaries, plus f-string formatting and comprehensions, each with a one-line example you can paste into… Beginner · Cheat sheet · Any system Article Basics 15 Oct 2026
  • 0005 Dataclasses in depth @dataclass writes __init__, __repr__ and __eq__ for classes that mainly hold data. Learn defaults, default_factory, frozen and ordered classes, __post_init__ validation, slots… Advanced · Explains an idea · Any system Article Basics 15 Oct 2026
  • 0055 Docker basics: images, containers and the commands you need An image is a read-only package of an app and its dependencies, and a container is a running instance of it. Learn… Beginner · Explains an idea · Linux, macOS, Windows Article Docker basics 15 Oct 2026
  • 0056 Docker Compose: run a small web app with a database Docker Compose describes several containers, their ports, volumes and environment in one compose.yaml file, then starts them all with docker compose up. Intermediate · Step by step · Linux, macOS, Windows Article Docker basics · Docker Compose 15 Oct 2026
  • 0250 Containerize a Node.js app and run it with PostgreSQL using Docker Compose Package a small Node.js web app in a multi-stage Docker image, then run it next to PostgreSQL with Docker Compose, named volumes,… Intermediate · Guided project · Linux, macOS, Windows Article Docker basics · Docker Compose · PostgreSQL 15 Oct 2026
  • 0268 What is a container? A container is a lightweight box that packs a program with everything it needs so it runs the same on any computer. Beginner · Explains an idea · Any system Article Docker basics 15 Oct 2026

Repos in the wild

History

Every saved edit is kept. The plot on the sleeve draws one line per edit. Open any edit to see exactly what changed.

  1. Dominik +5,480 chars

Report a problem

Something wrong, unsafe or copied? Tell the editors.
What is wrong?