Container image size directly impacts deployment speed, storage costs, and security surface area. Multi-stage builds are the single most impactful technique for optimizing Docker images. Here’s how we use them across different technology stacks.
Why Multi-Stage Builds Matter
A naive Dockerfile that copies source code and runs the build inside a single stage produces images measured in gigabytes. Node.js images include node_modules (hundreds of MB). Python images include virtual environments and build tools. Laravel images include Composer dependencies and Node.js build tools.
Multi-stage builds solve this by separating the build environment from the runtime environment. The build stage has all tools needed to compile and bundle. The runtime stage has only what’s needed to run. The result: images 5-10x smaller.
Node.js / Next.js Pattern
Stage 1 (deps): Install production dependencies only. Stage 2 (builder): Copy source, install all dependencies, run the build. Stage 3 (runner): Copy built assets and production dependencies to a slim image. For Next.js, we use standalone output mode which creates a minimal server bundle.
The final image is typically 100-150MB instead of 1GB+. It contains only the Node.js runtime, production dependencies, and built assets.
Python / FastAPI Pattern
Stage 1 (builder): Install build tools and compile C extensions. Stage 2 (runtime): Copy compiled packages to a slim Python image. Use Alpine for even smaller images, but test thoroughly — some Python packages have Alpine compatibility issues.
Laravel Pattern
Stage 1 (frontend): Install Node.js, run npm build. Stage 2 (backend): Install Composer dependencies, optimize autoloader. Stage 3 (production): Copy PHP, vendor directory, and built assets to a PHP-FPM Alpine image. Configure OPCache for production performance.
Security Benefits
Smaller images have fewer packages, fewer vulnerabilities, and a smaller attack surface. Build tools, compilers, and development dependencies never make it to the runtime image. This significantly reduces the number of CVEs reported by container scanning tools.
Layer Caching Strategy
Order Dockerfile instructions from least to most frequently changed: base image, system packages, language dependencies, application code. This maximizes layer cache hits during builds, reducing build times from minutes to seconds when only application code changes.
Measuring the Impact
We track image size, build time, and deployment time for every service. Typical improvements: image size reduced by 70-90%, build time reduced by 40-60% (with cache), and deployment time reduced by 50-70%. The smaller images also reduce registry storage costs and network transfer time.
Need help with your project?
Our team specializes in building production-grade software. Explore our services:
Get engineering insights in your inbox
Production-tested approaches to AI, Laravel, React and more. No spam, unsubscribe anytime.


