Maintenance

Site is under maintenance — quizzes are still available.

Go to quizzes
Python

Lock Down Python Docker Containers

Practical steps to secure your Python Docker images and containers: minimal base images, non-root users, pinned dependencies, scanning, resource limits, and read-only filesystems.

July 2026 8 min read 2 views 0 hearts

Secure Python Docker Containers from Exploits

You've probably heard it a thousand times: "It works on my machine." Docker was supposed to fix that, but if you're not careful, your containerized Python app might become a playground for attackers. Let's talk about real, practical ways to lock things down without going crazy.

Start with a Minimal Base Image

Most people grab python:3.11 and call it a day. That image is over 900MB. That's not just disk space—that's a massive attack surface. Every included package, library, and tool is a potential entry point.

Use python:3.11-slim or even python:3.11-alpine instead. The slim version is about 120MB. Alpine is around 50MB. Fewer packages mean fewer vulnerabilities.

FROM python:3.11-slim-bookworm

But here's the thing: alpine uses musl libc instead of glibc. Some Python packages don't play nice with it. Test first.

Never Run as Root

Docker containers run as root by default. If an attacker gets shell access to your container, they have root. That's a gift basket for bad actors.

Create a non-root user in your Dockerfile:

RUN addgroup --system appgroup && adduser --system --ingroup appgroup appuser
USER appuser

Put this after you install dependencies, but before the CMD. That way, you don't have permission issues during the build.

Pin Dependencies, Seriously

"PythonSkillset had a nasty wake-up call last year when an automated rebuild pulled in a compromised version of a logging library. It took hours to trace."

Lock your dependencies with exact versions:

requests==2.31.0
flask==3.0.0

Don't use >= or ~=. Use pip freeze > requirements.txt after testing. Even better, use a lock file with pip-tools or poetry. Every time you rebuild, you get the same bits.

Scan Before You Deploy

There's no excuse for not scanning images anymore. Tools like Trivy, Snyk, or Grype catch known vulnerabilities in base images and dependencies.

trivy image your-python-app:latest

Make this part of your CI pipeline, not something you run once a month. When a high-severity CVE drops for numpy, you want to know immediately.

Remove Unused Tools

Your Python app probably doesn't need curl, wget, git, or a compiler. Every one of those is a weapon waiting to be used.

RUN apt-get update && apt-get install -y --no-install-recommends \
    build-essential \
    && pip install -r requirements.txt \
    && apt-get purge -y build-essential \
    && apt-get autoremove -y \
    && rm -rf /var/lib/apt/lists/*

Yes, it's longer. Yes, it's worth it. The smaller the container, the harder it is to break.

Set Resource Limits

Docker containers can consume all your host's memory and CPU if you don't constrain them. That's not just a performance issue—it's a denial-of-service vulnerability.

docker run --memory="512m" --cpus="1.0" your-python-app

Or set it in docker-compose:

services:
  app:
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 256M

Use Read-Only Filesystems

Most Python apps don't need to write to the filesystem at runtime. Tell Docker to make it read-only:

docker run --read-only your-python-app

If your app needs to write to a specific directory (like /tmp or /var/log), mount a tmpfs:

docker run --read-only --tmpfs /tmp:rw,noexec,nosuid,size=100m your-python-app

Now if an attacker tries to write malicious code to disk, they can't.

Handle Secrets Properly

Do not bake secrets into your image. No .env files, no hardcoded API keys, no passwords in Dockerfiles. Use Docker secrets or environment variables passed at runtime.

docker run -e DB_PASSWORD=$(cat /run/secrets/db_password) your-python-app

Or in compose:

secrets:
  db_password:
    file: ./db_password.txt

Don't Expose Unnecessary Ports

Only expose the port your app actually needs. If you're running a Flask app on port 5000, don't also expose debug ports or management interfaces.

EXPOSE 5000

And when you run it, map only what's necessary:

docker run -p 5000:5000 your-python-app

Watch Your Logging

Python's print() statements go to stdout by default, which Docker captures. That's fine. But if you're logging sensitive data—passwords, tokens, PII—you're putting it at risk.

Use structured logging with a library like structlog or the built-in logging module. Filter out sensitive fields before they hit the logs.

import logging

logger = logging.getLogger(__name__)
logger.info("User login successful", extra={"user_id": user.id, "ip": anonymize(ip)})

Test Your Dockerfile

A quick sanity check: run docker scout quickview on your image. Or use docker run --rm your-python-app sh -c "whoami". If it says root, fix that.

The Bottom Line

Securing Docker containers isn't about doing one big thing right. It's about doing a hundred small things right. Minimal base, non-root user, pinned dependencies, scanning, resource limits, read-only filesystem.

PythonSkillset learned some of these lessons the hard way. You don't have to. Start with the basics, automate the checks, and your containers will be harder to crack.

Got a specific security concern with your Python Docker setup? The community over at PythonSkillset.com has seen it all.

Comments

Questions, corrections, and tips stay visible for everyone reading this page.

0 in thread

Join the discussion

Shown next to your comment.

Up to 4,000 characters

No comments yet

Be the first to leave a note — it helps the next reader.