A reasonable secure dockerfile for python applications

Posted on Fr 30 Mai 2025 in Blog

The first step for a (reasonable) secure container deployment is the container creation process. I have a simple Python sample application that I want to deploy.

Many examples on the internet use quite insecure Dockerfile examples where the applications run as root in the container. And it's pretty obvious that this is not a good idea. The applications in a container should follow the principle of least privilege.

One of the most important things when creating a container is to specify a .containerignore file with all the things that don't need to be in the container. The final image will be smaller and only contain the files that are needed to run the application in the final container.

For me it is important to have reproducible builds. Therefore, it should be specified exactly which Python version should be used. It is also good to use a small base image with only a few pre-installed programs. I use Debian most of the time, therefore I specify a slim version of Debian. I mostly use Debian, so I specify the slim version of Debian.

FROM python:3.13-slim-bookworm

The first step is to update the image. After the update, the cache will be removed.

RUN set -ex && DEBIAN_FRONTEND=noninteractive && apt-get update && \
apt-get upgrade -y && \
rm -rf /var/lib/apt/lists/*

The next step is to remove the shell from the root user and create an unprivileged user. The home directory of the user will be in /srv/app and that will be the place where all the files for the python app will be.

RUN chsh -s /usr/sbin/nologin root && addgroup --gid 1001 --system app && \
adduser --home /srv/app --shell /bin/false --disabled-password --uid 1001 --system --group app

After that an update of python-related programs like pip is performed.

RUN python -m pip install --no-cache-dir  wheel setuptools pip --upgrade

The next steps will all be executed as the app user that was created above. We will add the place for the installed packages to the PATH, copy the content of the app and install all dependencies.

USER app
ENV PATH=/srv/app/.local/bin/:$PATH
COPY . /srv/app/
RUN pip install --no-cache-dir pip-tools && pip-compile /srv/app/requirements.in && pip install --no-cache-dir -r /srv/app/requirements.txt

The final step is to declare how the script is executed.

EXPOSE 8000
ENTRYPOINT [ "python3" ]
CMD ["/srv/app/main.py"]

After creating the container, I use the rootless mode of podman to let the container run. The run command should drop all capabilities and use a read-only file system.

podman build -f Dockerfile -t fast-api-example
podman run -d --name fast-api-example -p 8000:8000 --read-only=True --cap-drop ALL --security-opt=no-new-privileges fast-api-example

The whole example can be found on GitHub.