DevLogTechnical writing
Tutorials

Write your first Dockerfile: containerize a tiny app

September 20, 2026

A Dockerfile is a plain-text recipe that tells Docker how to assemble an image — the self-contained snapshot your container will run from. Every line is an instruction, read top to bottom, and the result is reproducible: anyone who builds the image gets the same environment. Containerizing an app also means the app no longer depends on whatever happens to be installed on the host machine — your laptop, your colleague's laptop, and the server all get exactly the same runtime.

This guide takes a tiny Python app and turns it into an image, building and running it along the way.

The project

You have a small web app. The pieces that matter are a dependency manifest and the source:

myapp/
├── app.py
└── requirements.txt

The manifest lists what runtime packages the app needs. The source is a short file, for example a small Flask app in app.py. Alongside them you'll create one more file, named exactly Dockerfile — capital D, no extension (watch out: some editors quietly append .txt, which breaks the build).

Write the Dockerfile

FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
RUN useradd --create-home appuser
USER appuser
EXPOSE 5000
CMD ["python", "app.py"]

Walk through the instructions:

The ordering does a real job: Docker builds in layers and caches each one. Because you copy requirements.txt and install dependencies before copying the source, editing app.py rebuilds only the last layer — Docker reuses the cached dependency install. If you copied source first, any code change would reinstall everything on every build.

Build the image

From inside myapp/, run:

docker build -t myapp:v1 .

The . is the build context — it tells Docker the current directory holds the Dockerfile and the files to copy. It is required: docker build -t myapp without the dot fails.

Docker sends the whole directory to the daemon as the build context, so trim what shouldn't travel. Add a .dockerignore file in the project root:

.git
.venv
__pycache__

This keeps source-control history, virtual environments, and Python caches out of the image.

Run the container

docker run -d -p 5000:5000 --name myapp myapp:v1

Check it and see it running:

docker ps
curl http://localhost:5000

Clean up when you're done with docker stop myapp and docker rm myapp.

What's next

You have a working image; the natural next step is sharing it. Push the tagged image to a registry and run the same docker run on a server — that's the whole promise of containers, an environment that travels. After that, look at docker compose to define the app plus its database and networking in one file.

← More Tutorials