Speed up Docker builds by properly ordering your Dockerfile commands. Learn how to leverage Docker's layer caching to avoid rebuilding everything when you change your code.
The Problem
Every time you change a line of code, Docker rebuilds everything from scratch? You're not leveraging layer caching!
β‘ Proper layer caching can reduce build times from 5+ minutes to under 30 seconds!
β Bad: Inefficient Layer Order
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /app
# π« This copies everything first!
COPY . .
RUN dotnet restore
RUN dotnet build
RUN dotnet publish -o /app/publish
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyApp.dll"]
Problem: Any code change invalidates all layers, forcing complete rebuild including dotnet restore.
β Good: Optimized Layer Caching
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /app
# β
Copy project files first (changes rarely)
COPY *.csproj ./
RUN dotnet restore
# β
Copy source code after restore (changes often)
COPY . .
RUN dotnet build -c Release --no-restore
RUN dotnet publish -c Release --no-build -o /app/publish
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyApp.dll"]
π― Why This Works
- Layer 1-3: Rarely change β cached most of the time
- Layer 4: Only rebuilds when .csproj changes
- Layer 5-6: Code changes β only these layers rebuild
- Result: NuGet packages stay cached!
π₯ Advanced: Multi-Project Solutions
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /app
# Copy solution file first
COPY *.sln ./
# Copy all project files (maintaining folder structure)
COPY src/MyApp.Api/*.csproj ./src/MyApp.Api/
COPY src/MyApp.Core/*.csproj ./src/MyApp.Core/
COPY src/MyApp.Infrastructure/*.csproj ./src/MyApp.Infrastructure/
COPY tests/MyApp.Tests/*.csproj ./tests/MyApp.Tests/
# Restore all projects
RUN dotnet restore
# Copy all source code
COPY . .
# Build and publish
RUN dotnet build -c Release --no-restore
RUN dotnet publish src/MyApp.Api -c Release --no-build -o /app/publish
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyApp.Api.dll"]
π‘ Pro Tips for Maximum Speed
1. Use .dockerignore
# .dockerignore
**/bin/
**/obj/
**/.vs/
**/node_modules/
.git/
README.md
.dockerignore
Dockerfile
2. Separate NuGet Cache Layer
# For solutions with global.json or Directory.Packages.props
COPY global.json ./
COPY Directory.Packages.props ./
COPY *.csproj ./
RUN dotnet restore
3. Use --no-restore and --no-build Flags
Always use these flags to prevent unnecessary work:
--no-restorein build (already restored)--no-buildin publish (already built)
Build Time Comparison
β Without Layer Caching
- First build: 5-8 minutes
- Code change: 5-8 minutes
- Package update: 5-8 minutes
Every change = full rebuild!
β With Layer Caching
- First build: 5-8 minutes
- Code change: 20-60 seconds
- Package update: 2-3 minutes
Dramatic time savings!
π Quick Wins
- Copy .csproj files first β cache NuGet restore
- Copy source code last β only rebuild when code changes
- Use multi-stage builds β smaller production images
- Add .dockerignore β faster context copying
π¬ Comments & Reactions