Speed up builds and reduce image size by managing build contexts effectively. Don’t send unnecessary files to the Docker daemon!
Understanding build context
The build context is what gets sent to Docker daemon:
services:
app:
build: . # Current directory is the context
# Everything in . gets sent to daemon!
Check your context size:
# See what's being sent
docker build --no-cache . 2>&1 | grep "Sending build context"
# Output: Sending build context to Docker daemon 458.2MB π±
Custom build contexts
Specify different contexts for different services:
services:
frontend:
build:
context: ./frontend # Only frontend/ directory
dockerfile: Dockerfile
backend:
build:
context: ./backend # Only backend/ directory
dockerfile: Dockerfile
shared-lib:
build:
context: . # Root for accessing multiple dirs
dockerfile: ./shared/Dockerfile
The power of .dockerignore
Create .dockerignore files to exclude unnecessary files:
# .dockerignore
# Version control
.git
.gitignore
# Dependencies
node_modules
vendor
__pycache__
*.pyc
# IDE files
.idea
.vscode
*.swp
*.swo
# OS files
.DS_Store
Thumbs.db
# Build artifacts
dist
build
*.o
*.exe
# Local env files
.env
.env.local
*.env
# Logs
*.log
logs/
# Tests
test/
tests/
coverage/
.coverage
# Documentation
docs/
*.md
!README.md # Exception: include README
# Development files
docker-compose.override.yml
Makefile
Multiple dockerignore patterns
Different contexts can have different .dockerignore files:
project/
βββ .dockerignore # Root ignore
βββ frontend/
β βββ .dockerignore # Frontend-specific ignore
β βββ Dockerfile
βββ backend/
β βββ .dockerignore # Backend-specific ignore
β βββ Dockerfile
βββ docker-compose.yml
Each service uses its context’s .dockerignore:
services:
frontend:
build: ./frontend # Uses ./frontend/.dockerignore
backend:
build: ./backend # Uses ./backend/.dockerignore
Advanced context with multiple sources
Use bind mounts for selective file inclusion:
services:
app:
build:
context: .
dockerfile: Dockerfile
additional_contexts:
- shared=../shared-libs
- configs=./configurations
In Dockerfile:
# Copy from additional contexts
COPY --from=shared . /app/shared
COPY --from=configs . /app/config
Git-based contexts
Build directly from Git repositories:
services:
app:
build: https://github.com/user/repo.git#branch
specific-commit:
build: https://github.com/user/repo.git#v1.0.0
subdirectory:
build: https://github.com/user/repo.git#main:subdirectory
Named contexts for reuse
Share contexts between services:
x-backend-build: &backend-build
context: ./backend
dockerfile: Dockerfile
args:
BUILD_ENV: production
services:
api:
build: *backend-build
worker:
build:
<<: *backend-build
target: worker # Different stage
scheduler:
build:
<<: *backend-build
target: scheduler
Pro tip
Use BuildKit’s cache mounts to speed up builds:
services:
app:
build:
context: .
dockerfile: Dockerfile
cache_from:
- type=local,src=/tmp/buildcache
cache_to:
- type=local,dest=/tmp/buildcache,mode=max
And in your Dockerfile:
# syntax=docker/dockerfile:1
FROM node:18
WORKDIR /app
# Cache package manager downloads
RUN --mount=type=cache,target=/root/.npm \
npm set cache /root/.npm
# Cache dependencies
COPY package*.json .
RUN --mount=type=cache,target=/root/.npm \
npm ci --only=production
# Copy only necessary files (respecting .dockerignore)
COPY . .
RUN npm run build
Smaller contexts = faster builds = happier developers!