$ [Source Code] --> [Builder Stage] --> [Runtime Stage] --> [Minimal Image] | | [Dependencies] [Binary Only] [Build Tools] [No Shell] [Test Frameworks] [Non-root User]
[Source Code] --> [Builder Stage] --> [Runtime Stage] --> [Minimal Image] | | [Dependencies] [Binary Only] [Build Tools] [No Shell] [Test Frameworks] [Non-root User]
[Source Code] --> [Builder Stage] --> [Runtime Stage] --> [Minimal Image] | | [Dependencies] [Binary Only] [Build Tools] [No Shell] [Test Frameworks] [Non-root User]
# Builder stage
FROM golang:1.21 AS builder
WORKDIR /app
go.mod go.sum ./
RUN --mount=type=cache,target=/go/pkg/mod \ go mod download
. .
RUN --mount=type=cache,target=/go/pkg/mod \ --mount=type=cache,target=/root/.cache/go-build \ CGO_ENABLED=0 go build -ldflags="-s -w" -trimpath -o /app/server # Runtime stage
FROM gcr.io/distroless/static-debian11
--from=builder /app/server /usr/local/bin/
USER nonroot:nonroot
ENTRYPOINT ["/usr/local/bin/server"]
# Builder stage
FROM golang:1.21 AS builder
WORKDIR /app
go.mod go.sum ./
RUN --mount=type=cache,target=/go/pkg/mod \ go mod download
. .
RUN --mount=type=cache,target=/go/pkg/mod \ --mount=type=cache,target=/root/.cache/go-build \ CGO_ENABLED=0 go build -ldflags="-s -w" -trimpath -o /app/server # Runtime stage
FROM gcr.io/distroless/static-debian11
--from=builder /app/server /usr/local/bin/
USER nonroot:nonroot
ENTRYPOINT ["/usr/local/bin/server"]
# Builder stage
FROM golang:1.21 AS builder
WORKDIR /app
go.mod go.sum ./
RUN --mount=type=cache,target=/go/pkg/mod \ go mod download
. .
RUN --mount=type=cache,target=/go/pkg/mod \ --mount=type=cache,target=/root/.cache/go-build \ CGO_ENABLED=0 go build -ldflags="-s -w" -trimpath -o /app/server # Runtime stage
FROM gcr.io/distroless/static-debian11
--from=builder /app/server /usr/local/bin/
USER nonroot:nonroot
ENTRYPOINT ["/usr/local/bin/server"]
FROM node:20-alpine AS builder
WORKDIR /app
package*.json pnpm-lock.yaml ./
RUN corepack -weight: 500;">enable pnpm && pnpm -weight: 500;">install --frozen-lockfile
. .
RUN pnpm run build FROM node:20-alpine
WORKDIR /app
--from=builder /app/dist ./dist
--from=builder /app/node_modules ./node_modules
USER node
CMD ["node", "dist/index.js"]
FROM node:20-alpine AS builder
WORKDIR /app
package*.json pnpm-lock.yaml ./
RUN corepack -weight: 500;">enable pnpm && pnpm -weight: 500;">install --frozen-lockfile
. .
RUN pnpm run build FROM node:20-alpine
WORKDIR /app
--from=builder /app/dist ./dist
--from=builder /app/node_modules ./node_modules
USER node
CMD ["node", "dist/index.js"]
FROM node:20-alpine AS builder
WORKDIR /app
package*.json pnpm-lock.yaml ./
RUN corepack -weight: 500;">enable pnpm && pnpm -weight: 500;">install --frozen-lockfile
. .
RUN pnpm run build FROM node:20-alpine
WORKDIR /app
--from=builder /app/dist ./dist
--from=builder /app/node_modules ./node_modules
USER node
CMD ["node", "dist/index.js"]
# Dependencies first (changes rarely)
package.json package-lock.json ./
RUN -weight: 500;">npm ci # Source code second (changes often)
. .
RUN -weight: 500;">npm run build
# Dependencies first (changes rarely)
package.json package-lock.json ./
RUN -weight: 500;">npm ci # Source code second (changes often)
. .
RUN -weight: 500;">npm run build
# Dependencies first (changes rarely)
package.json package-lock.json ./
RUN -weight: 500;">npm ci # Source code second (changes often)
. .
RUN -weight: 500;">npm run build
-weight: 500;">docker buildx build \ --cache-from type=registry,ref=registry.example.com/cache \ --cache-to type=registry,ref=registry.example.com/cache \ -t registry.example.com/app:v1.2.3 .
-weight: 500;">docker buildx build \ --cache-from type=registry,ref=registry.example.com/cache \ --cache-to type=registry,ref=registry.example.com/cache \ -t registry.example.com/app:v1.2.3 .
-weight: 500;">docker buildx build \ --cache-from type=registry,ref=registry.example.com/cache \ --cache-to type=registry,ref=registry.example.com/cache \ -t registry.example.com/app:v1.2.3 .
turbo run build --api="https://cache.example.com" --token="$CACHE_TOKEN"
turbo run build --api="https://cache.example.com" --token="$CACHE_TOKEN"
turbo run build --api="https://cache.example.com" --token="$CACHE_TOKEN"
jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Build container run: -weight: 500;">docker build -t app:${{ github.sha }} . unit-tests: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Run tests run: -weight: 500;">npm test security-scan: needs: build runs-on: ubuntu-latest steps: - name: Scan image run: trivy image app:${{ github.sha }} integration-tests: needs: build runs-on: ubuntu-latest services: postgres: image: postgres:15 steps: - name: Run integration tests run: ./run-integration-tests.sh
jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Build container run: -weight: 500;">docker build -t app:${{ github.sha }} . unit-tests: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Run tests run: -weight: 500;">npm test security-scan: needs: build runs-on: ubuntu-latest steps: - name: Scan image run: trivy image app:${{ github.sha }} integration-tests: needs: build runs-on: ubuntu-latest services: postgres: image: postgres:15 steps: - name: Run integration tests run: ./run-integration-tests.sh
jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Build container run: -weight: 500;">docker build -t app:${{ github.sha }} . unit-tests: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Run tests run: -weight: 500;">npm test security-scan: needs: build runs-on: ubuntu-latest steps: - name: Scan image run: trivy image app:${{ github.sha }} integration-tests: needs: build runs-on: ubuntu-latest services: postgres: image: postgres:15 steps: - name: Run integration tests run: ./run-integration-tests.sh
apiVersion: v1
kind: Pod
spec: containers: - name: kaniko image: gcr.io/kaniko-project/executor:latest args: - "--dockerfile=Dockerfile" - "--context=-weight: 500;">git://github.com/example/app" - "--destination=registry.example.com/app:v1.2.3" - "--cache=true"
apiVersion: v1
kind: Pod
spec: containers: - name: kaniko image: gcr.io/kaniko-project/executor:latest args: - "--dockerfile=Dockerfile" - "--context=-weight: 500;">git://github.com/example/app" - "--destination=registry.example.com/app:v1.2.3" - "--cache=true"
apiVersion: v1
kind: Pod
spec: containers: - name: kaniko image: gcr.io/kaniko-project/executor:latest args: - "--dockerfile=Dockerfile" - "--context=-weight: 500;">git://github.com/example/app" - "--destination=registry.example.com/app:v1.2.3" - "--cache=true"
-weight: 500;">docker buildx build \ --platform linux/amd64,linux/arm64 \ -t registry.example.com/app:v1.2.3 \ --push .
-weight: 500;">docker buildx build \ --platform linux/amd64,linux/arm64 \ -t registry.example.com/app:v1.2.3 \ --push .
-weight: 500;">docker buildx build \ --platform linux/amd64,linux/arm64 \ -t registry.example.com/app:v1.2.3 \ --push .
- name: Scan dependencies run: | -weight: 500;">npm audit --audit-level=high snyk test --severity-threshold=high
- name: Scan dependencies run: | -weight: 500;">npm audit --audit-level=high snyk test --severity-threshold=high
- name: Scan dependencies run: | -weight: 500;">npm audit --audit-level=high snyk test --severity-threshold=high
syft packages registry.example.com/app:v1.2.3 -o spdx-json > sbom.json
grype sbom.json
syft packages registry.example.com/app:v1.2.3 -o spdx-json > sbom.json
grype sbom.json
syft packages registry.example.com/app:v1.2.3 -o spdx-json > sbom.json
grype sbom.json
cosign sign --yes registry.example.com/app:v1.2.3
cosign sign --yes registry.example.com/app:v1.2.3
cosign sign --yes registry.example.com/app:v1.2.3 - Multi-stage Docker builds with BuildKit caching reduce image sizes by 80% and build times by 60%
- Remote build caching shares artifacts across developers and CI, eliminating redundant work
- Parallel pipeline execution runs independent stages simultaneously for faster feedback
- SBOM generation and container signing are now required for European regulated industries - Set up remote registry caching – Share build layers across your team and CI
- Implement Turborepo/Nx – Content-addressable caching for monorepos
- Optimize layer ordering – Dependencies first, code last
- Reduce build times by 60-80% – Measurable results - A machine that costs 4x more per hour but finishes in one-quarter the time costs the same
- Factor in developer wait time → faster machines win decisively
- Most teams underestimate the impact of machine sizing on total cost - SBOM = Software Bill of Materials - a machine-readable inventory of all software components in your artifact
- Required for: Government contracts and regulated industries
- Tools: Generate with Syft during builds; scan with Grype for vulnerabilities