Cybersecurity

Docker Security for Developers: 15 Practical Rules

Python Docker SQL Injection XSS CI/CD Git Linux AWS Databases SQL Rust Credentials Passwords
1,093 words Includes Code

Docker Security for Developers: 15 Practical Rules

Every Dockerfile default is a security decision. Here are 15 rules that turn insecure defaults into production-ready containers.

KEY TAKEAWAY
Docker containers run with permissive defaults. Run as non-root, use minimal base images, never embed secrets, scan for vulnerabilities, drop Linux capabilities, and sign your images. These 15 rules cover the build, image, runtime and deployment layers of container security.

Docker makes it easy to package and deploy applications. It also makes it easy to ship containers with root access, hardcoded secrets, unpatched vulnerabilities and excessive permissions. The good news: every one of these issues has a straightforward fix.

This article covers 15 practical security rules every developer should apply, with concrete Dockerfile examples and commands.

Rule 1: Run as Non-Root User

# GOOD: Create and use non-root user
FROM python:3.12-slim
RUN groupadd -r appuser && useradd -r -g appuser appuser
COPY --chown=appuser:appuser . /app
WORKDIR /app
USER appuser
CMD ["python", "main.py"]

# Verify: docker run --rm myapp whoami
# Output should be "appuser", NOT "root"

By default, Docker runs containers as root. If a container escape vulnerability is exploited, root inside the container means root on the host. Creating a dedicated user limits the blast radius.

Rule 2: Use Minimal Base Images

Image Security Level Trade-off
gcr.io/distroless/* Highest No shell, no package manager — debugging harder
alpine:3.19 High ~5 MB, has shell and apk for debugging
python:3.12-slim Medium ~50 MB, Debian minimal, easy debugging
python:3.12 Lower ~900 MB, full Debian — many unused packages

Every package in the base image is potential attack surface. Smaller images have fewer vulnerabilities and are faster to pull and deploy.

Rule 3: Never Store Secrets in Images

# BAD: Anyone who pulls the image can see these
ENV API_KEY=sk_live_REAL_KEY
ENV DATABASE_PASSWORD=p4ssw0rd
# GOOD: Inject at runtime, never in the image
docker run -e API_KEY=$API_KEY myapp

# BEST: Fetch from secret manager at startup
# Vault / AWS Secrets Manager / GCP Secret Manager

Rule 4: Use .dockerignore

# .dockerignore
.git
.env
.env.*
*.pem
*.key
*.cert
node_modules
__pycache__
.vscode
tests/
README.md

Even deleted files exist in image layers. If you COPY . /app without a .dockerignore, your .env file and private keys enter the image — even if you delete them in a subsequent RUN instruction.

Rule 5: Pin Image Versions

# BAD: Unpredictable
FROM python:latest
# GOOD: Pinned version
FROM python:3.12.7-slim

# BEST: Pinned by digest (maximum reproducibility)
FROM python:3.12.7-slim@sha256:abc123def456...

Rule 6: Use Read-Only Filesystem

docker run --read-only --tmpfs /tmp myapp

# Container filesystem is read-only
# Writes go to /tmp (in-memory only)
# Malware cannot persist files to disk

Rule 7: Scan Images for Vulnerabilities

Tool Type Command
Docker Scout Built-in docker scout cves myapp
Trivy Open source trivy image myapp:latest
Grype Open source grype myapp:latest
Snyk Freemium snyk container test myapp

Add scanning to your CI/CD pipeline. Fail the build on HIGH or CRITICAL vulnerabilities.

Rules 8-10: Build Hygiene

Rule Do This Not This
Rule 8: Fewer packages Install only what app needs curl, vim, git, wget in prod
Rule 9: COPY not ADD COPY app/ /app/ ADD http://... /app/
Rule 10: Resource limits --memory=512m --cpus=1 Unlimited RAM and CPU

Rules 11-13: Runtime Security

# Rule 11: Use trusted base images
# Prefer Docker Official Images, Verified Publishers, or distroless

# Rule 12: Drop all capabilities, add only what's needed
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE myapp

# Rule 13: Never use --privileged
# Use --device for specific hardware access instead
docker run --device=/dev/gpu myapp

Rules 14-15: Supply Chain Security

# Rule 14: Multi-stage builds (smaller = safer)
FROM python:3.12 AS builder
COPY . /build
RUN pip install --user -r requirements.txt

FROM python:3.12-slim
RUN groupadd -r app && useradd -r -g app app
COPY --from=builder /root/.local /home/app/.local
COPY --from=builder /build /app
USER app
CMD ["python", "main.py"]

# Rule 15: Sign and verify images
# Sign with cosign
cosign sign --key cosign.key myregistry/myapp:v1

# Verify before deploy
cosign verify --key cosign.pub myregistry/myapp:v1

The Complete Secure Dockerfile

Combining all 15 rules into one template:

# Rule 5: Pin version | Rule 14: Multi-stage
FROM python:3.12.7-slim AS builder
WORKDIR /build
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt
COPY . .

# Rule 2: Minimal base | Rule 1: Non-root
FROM python:3.12.7-slim
RUN groupadd -r app && useradd -r -g app -d /home/app app
COPY --from=builder --chown=app:app /root/.local /home/app/.local
COPY --from=builder --chown=app:app /build /home/app
WORKDIR /home/app

# Rule 1: Non-root | Rule 6: Read-only ready
USER app
ENV PATH=/home/app/.local/bin:$PATH

# Rule 3: No secrets | Rule 10: Resource limits at runtime
# docker run --read-only --memory=512m --cpus=1 myapp

CMD ["python", "main.py"]

Security Checklist

BUILD:
  [  ] 1.  Non-root user (USER directive)
  [  ] 2.  Minimal base image (alpine/slim/distroless)
  [  ] 3.  No secrets in image or ENV
  [  ] 4.  .dockerignore configured
  [  ] 5.  Pinned image versions (not :latest)

IMAGE:
  [  ] 6.  Read-only filesystem where possible
  [  ] 7.  Vulnerability scan passed
  [  ] 8.  No unnecessary packages installed
  [  ] 9.  COPY used instead of ADD
  [  ] 10. Resource limits set at runtime

RUNTIME:
  [  ] 11. Trusted base image source
  [  ] 12. Linux capabilities dropped (cap-drop=ALL)
  [  ] 13. --privileged never used
  [  ] 14. Multi-stage build (smaller image)
  [  ] 15. Image signed and verified

Try It Yourself

Related BestWordz Articles

Further Reading

Conclusion

Docker security is not a single setting — it is a series of decisions at build time, image creation, runtime and deployment. The 15 rules in this article cover the most impactful changes: running non-root, using minimal images, keeping secrets out of images, scanning for vulnerabilities, limiting capabilities and signing your supply chain.

Most of these rules add minutes to your Dockerfile and seconds to your CI/CD pipeline. The security improvement is disproportionate to the effort. Start with rules 1-5 (the highest impact, lowest effort), then progressively add the rest.

BOTTOM LINE
The default Docker container is not secure. Non-root user, minimal base, no secrets, vulnerability scanning, dropped capabilities and image signing — these six changes eliminate the most common container security failures.

Try the JSON Formatter

Put what you've learned into practice with this free BestWordz tool.

Open Tool →

💬 Discuss on BestWordz Community

Join the conversation about Python, Docker, SQL Injection on the BestWordz Community forum.

Visit Forum →