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.ADDcan also fetch URLs and unpack archives; preferCOPYunless 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.