DEV Community

Mikhail Dorokhovich
Mikhail Dorokhovich

Posted on • Originally published at dorokhovich.com

Local Kubernetes Dev — Part 6: Containerizing your service — writing a Dockerfile

Two mistakes beginners make in Dockerfiles constantly — and both have real consequences: running as root (a security hole) and the wrong layer order (a tax on build speed).

Part six of the series is a production-ready Dockerfile for the FastAPI service myapp. Not "your first Dockerfile," but a breakdown of what separates a solid image from a bloated, insecure one. Inside: why COPY requirements.txt BEFORE COPY ./app — and pip install gets cached instead of reinstalling everything on every code change; a multi-stage build that carries only the finished venv into the final image, without compilers and pip caches; base image choice (slim vs alpine with its musl and compile-from-source vs distroless with no shell); and the security backbone of the chapter — an unprivileged user (adduser --uid 10001 + USER appuser) plus runAsNonRoot in the manifest.

Plus the important details that break prod silently: the exec form of CMD (otherwise SIGTERM never arrives and graceful shutdown breaks), fastapi run instead of bare uvicorn, PYTHONUNBUFFERED=1 (otherwise you see no logs in kubectl logs), HEALTHCHECK via Python (there's no curl in slim).

And the finale — the main trap: docker build does NOT make the image visible to k3d. Two ways to deliver it: k3d image import and the built-in registry. https://dorokhovich.com/blog/local-k8s-containerizing-your-service?utm_source=devto&utm_medium=syndication&utm_campaign=local-k8s-containerizing-your-service

Top comments (0)