Dockerfile basics

Docker · Essentials · 12 min

A Dockerfile is the recipe for your image; every containerized project has one.

A Dockerfile is a text file of instructions that build an image, executed top to bottom:

  • FROM python:3.13-slim: the base image to start from. Always first.
  • WORKDIR /app: set (and create) the working directory for later instructions and for the container.
  • COPY requirements.txt .: copy files from the build context (your project folder) into the image. ADD can also fetch URLs and unpack archives; prefer COPY unless you need that.
  • RUN pip install -r requirements.txt: run a command at build time and save the result in the image.
  • ENV PORT=8000: an environment variable available at build time and at run time.
  • EXPOSE 8000: documents the port the app listens on. It doesn't publish anything by itself.
  • USER app: run later steps and the container as this user.
  • CMD ["python", "app.py"]: the default command when a container starts. There can be one CMD; a later one wins.

RUN happens once, during the build; CMD happens every time a container starts. Mixing them up is the classic beginner bug.

Use the exec form with a JSON array (CMD ["python", "app.py"]) rather than the shell form (CMD python app.py). The shell form runs your app under /bin/sh -c, so the shell is PID 1 and docker stop's SIGTERM never reaches your app.

ENTRYPOINT sets a fixed executable, and CMD then supplies default arguments: with ENTRYPOINT ["gunicorn"] and CMD ["app:app"], docker run img other:app swaps just the argument. Use it when the image is essentially one command.

Example

# Dockerfile
FROM python:3.13-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
ENV PORT=8000
EXPOSE 8000
RUN useradd --create-home app
USER app
CMD ["python", "app.py"]

Remember

  • RUN executes at build time; CMD is the default at container start
  • Use exec form ["cmd", "arg"] so your app is PID 1 and gets signals
  • EXPOSE only documents a port; publishing happens at docker run
  • ENTRYPOINT fixes the executable; CMD gives default arguments

Watch out for

  • Writing RUN python app.py to 'start' the app — that runs during the build and blocks or bakes nothing useful.

Check yourself

  1. When does a RUN instruction execute?
  2. Why prefer CMD ["node", "server.js"] over CMD node server.js?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.docker.com/reference/dockerfile/