Blog
Mert Özen's blog posts on software development, backend, frontend, and DevOps.
Container Security: Non-root, Read-only Filesystems, Capabilities, Minimal Images and Scanning
Running a container securely means narrowing its default privileges down to what the application really needs: a non-root user, a read-only file system with tmpfs, --cap-drop ALL, no-new-privileges, and memory and process limits. With real output we run Bookstore's API and web containers with these flags, pass the password as a file instead of an environment variable, scan the image with Trivy and see why docker.sock means root on the host.
Debugging Containers: logs, exec, inspect, stats, events, docker debug and Exit Code 137
Debugging a container starts with three questions: does it never start, does it start and die, or does it run but cannot be reached? With real output we work through exec format error and a CRLF script, a restart loop, looking into a shell-less chiseled image with a helper container sharing its PID and network namespaces and with docker debug, and a Bookstore.Api that dies with 137 every 30 seconds because of its health check process.
Reading Old Docker Setups and Moving Them to 2026: Legacy Builder to BuildKit, Port 80 to 8080, Debian to Ubuntu, Graph Drivers to containerd
Moving an old Dockerfile to 2026 means reading several separate changes together: BuildKit instead of the legacy builder, labels and exec form instead of MAINTAINER and shell form, the move from port 80 to 8080 and from root to the app user in .NET 8, from Debian to Ubuntu in .NET 10, and Engine 29's containerd image store and API version. With real output we modernize Bookstore's hypothetical 2021 Dockerfile line by line and try the .NET 11 SDK's local runtime selection and SOURCE_DATE_EPOCH.