Dev.to · 3 min read

Dockerize Like a Pro: 5 Best Practices for Lean and Secure Container Images

Dockerize Like a Pro: 5 Best Practices for Lean and Secure Container Images

Introduction Docker has revolutionized how we build, ship, and run applications. By packaging code and dependencies into containers, we eliminate the dreaded "it works on my machine" bug. However, dropping a monolithic app into a default Dockerfile can result in bloated image sizes, slow CI/CD pipelines, and hidden security vulnerabilities. Let’s look at five best practices to write clean, production-ready Dockerfiles. 1. Embrace Multi-Stage Builds When building compiled languages or modern frontend frameworks (like React, Next.js, or Go), you need heavy build tools (compilers, npm packages, node_modules) during compilation, but only the final static artifacts or binaries in production. The Solution: Use multi-stage builds to separate your build environment from your runtime environment. Dockerfile Stage 1: Build the application FROM node:18-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build Stage 2: Run the application FROM node:18-alpine AS runner WORKDIR /app COPY --from=builder /app/dist ./dist COPY --from=builder /app/node_modules ./node_modules EXPOSE 3000 CMD ["node", "dist/index.js"] This keeps your final production image lean by leaving behind compilers and source code. 2. Choose the Right Base Image Avoid using ubuntu or debian as base images unless you specifically need full operating system utilities. Alpine Linux: Extremely lightweight (often under 5MB) and secure, making it a favorite for production containers. Distroless Images (by Google): Contain only your application and its runtime dependencies. They don't even include a shell or package manager, drastically reducing your attack surface. 3. Order Your Instructions for Optimal Caching Docker builds images layer by layer, caching each step. If a layer changes, every subsequent layer must be rebuilt. Best Practice: Copy your package manifests (package.json, requirements.txt) before copying your source code. That way, if you change a line of code, Docker won't re-download and reinstall all your dependencies. Dockerfile Do this: COPY package*.json ./ RUN npm ci COPY . . # Source code changes frequently, keep it at the bottom 4. Avoid Running as Root By default, Docker containers run commands as the root user inside the container. If an attacker manages to exploit a vulnerability in your app, they instantly gain root privileges inside that container. Best Practice: Create a non-privileged user and switch to it before starting your app: Dockerfile RUN addgroup -S appgroup && adduser -S appuser -G appgroup USER appuser **5. Use .dockerignore **Just like .gitignore, a .dockerignore file prevents unwanted files (like node_modules, .git, local environment variables, and log files) from being sent to the Docker daemon during the docker build command. This speeds up build times and prevents secret leaks. Conclusion Writing efficient Dockerfiles takes minor adjustments, but the payoff is massive: smaller images, faster deployments, and a significantly tighter security posture.

This is a summary aggregated from Dev.to. Read the complete article on the original site:

Read full article at Dev.to

More Cybersecurity News