Skip to main content

Build Options

Learn how to optimize builds with caching, build arguments, and advanced Docker build features.

Build Arguments

Pass variables to your Dockerfile during build time using --build-arg.

Basic Build Args

Dockerfile:

ARG VERSION=dev
ARG BUILD_DATE

LABEL version="${VERSION}"
LABEL build-date="${BUILD_DATE}"

Build with arguments:

uc build --build-arg VERSION=1.2.3 --build-arg BUILD_DATE=2024-03-15

Or using uc deploy:

uc deploy --build-arg VERSION=1.2.3

Multiple Build Args

Pass multiple arguments:

uc build \
--build-arg NODE_ENV=production \
--build-arg API_URL=https://api.example.com \
--build-arg VERSION=1.2.3

Build Args in compose.yaml

Define build args in your compose file:

services:
web:
build:
context: .
args:
NODE_ENV: production
VERSION: "1.2.3"
image: myapp:latest

Override from command line:

uc build --build-arg NODE_ENV=development

Command line args take precedence over compose.yaml.

Environment Variable Build Args

Use environment variables:

export VERSION=1.2.3
uc build --build-arg VERSION

Or inline:

VERSION=1.2.3 uc build --build-arg VERSION

Common Build Arg Patterns

Version tagging:

ARG VERSION=latest
ENV APP_VERSION=${VERSION}
uc build --build-arg VERSION=$CI_COMMIT_SHA

Environment-specific builds:

ARG BUILD_ENV=development

RUN if [ "$BUILD_ENV" = "production" ]; then \
npm run build:prod; \
else \
npm run build:dev; \
fi
uc build --build-arg BUILD_ENV=production

Conditional dependencies:

ARG ENABLE_FEATURE_X=false

RUN if [ "$ENABLE_FEATURE_X" = "true" ]; then \
apt-get install -y feature-x-deps; \
fi

Build Caching

Docker caches build layers to speed up subsequent builds. Understanding caching is key to fast iteration.

How Caching Works

Docker caches each instruction in your Dockerfile:

FROM node:20-alpine          # Cached if base image unchanged
WORKDIR /app # Cached
COPY package*.json ./ # Cached if package files unchanged
RUN npm install # Cached if previous layer cached
COPY . . # Cached if source files unchanged
RUN npm run build # Cached if previous layer cached

Changes to any instruction invalidate cache for that layer and all subsequent layers.

Optimize for Caching

Good: Dependencies cached separately from source

FROM node:20-alpine
WORKDIR /app

# Install dependencies (cached unless package files change)
COPY package*.json ./
RUN npm install

# Copy source (cached unless source changes)
COPY . .
RUN npm run build

Bad: Everything rebuilds on any source change

FROM node:20-alpine
WORKDIR /app

# Copy everything (cache invalidated on any file change)
COPY . .
RUN npm install && npm run build

Disable Caching

Force a complete rebuild without using cache:

uc build --no-cache

When to use:

  • Debugging build issues
  • Cache is stale or corrupted
  • External dependencies changed
  • Want to ensure a clean build

Selective Cache Invalidation

Use .dockerignore to prevent cache invalidation from irrelevant files:

# .dockerignore
.git
node_modules
*.log
.env
README.md

Changes to these files won't invalidate Docker cache.

Pull Base Images

Control whether Docker pulls newer base images before building.

Always Pull

Pull latest base images during build:

uc build --pull

Dockerfile:

FROM node:20-alpine  # Pulls latest 20-alpine before building

When to use:

  • Using :latest or floating tags
  • Want security updates from base images
  • CI/CD builds to ensure fresh base images

Default Behavior

By default, Docker uses locally cached base images if available.

uc build  # Uses cached node:20-alpine

Build Context

The build context is the set of files available during the build.

Default Context

By default, context is the directory containing your Dockerfile:

services:
web:
build: . # Context is current directory

Custom Context

Specify a different build context:

services:
web:
build:
context: ./backend
dockerfile: Dockerfile

Large Build Context

Large contexts slow down builds. Use .dockerignore:

# .dockerignore
node_modules
.git
*.log
dist/
build/
.env*

Multiple Build Contexts

Use additional contexts (Docker BuildKit feature):

services:
web:
build:
context: .
additional_contexts:
shared: ../shared-library

In Dockerfile:

FROM node:20-alpine
COPY --from=shared /src /app/shared

Multi-stage Builds

Optimize image size with multi-stage builds.

Basic Multi-stage Build

# Build stage
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

# Production stage
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY package*.json ./
RUN npm install --production
CMD ["node", "dist/main.js"]

Final image only contains production assets.

Target Specific Stage

Build only up to a specific stage:

services:
web:
build:
context: .
target: builder # Stop at builder stage
uc build --target builder

Use case: Build development images with extra tools.

Development vs Production

# Base stage
FROM node:20 AS base
WORKDIR /app
COPY package*.json ./

# Development stage
FROM base AS development
RUN npm install
COPY . .
CMD ["npm", "run", "dev"]

# Build stage
FROM base AS builder
RUN npm install
COPY . .
RUN npm run build

# Production stage
FROM node:20-alpine AS production
WORKDIR /app
COPY --from=builder /app/dist ./dist
RUN npm install --production
CMD ["node", "dist/main.js"]

Build for development:

uc build --target development

Build for production:

uc build --target production

Platform-specific Builds

Build for specific CPU architectures.

Specify Platform

services:
web:
build:
context: .
platform: linux/amd64

Multi-architecture Builds

Build for multiple platforms:

docker buildx build --platform linux/amd64,linux/arm64 -t myapp:latest .

Note: Multi-arch builds require Docker Buildx.

Validation and Checks

Validate Build Configuration

Check build config without actually building:

uc build --check

This validates:

  • Dockerfile exists
  • Build context is accessible
  • compose.yaml syntax is correct
  • Build arguments are valid

Use case: Pre-commit hooks, CI checks

Build Progress

View Build Output

By default, you see full Docker build output:

uc build

Output shows:

Building service web...
[+] Building 12.5s (14/14) FINISHED
=> [internal] load build definition
=> [internal] load .dockerignore
=> [internal] load metadata for docker.io/library/node:20
=> [1/6] FROM docker.io/library/node:20
=> [2/6] WORKDIR /app
=> [3/6] COPY package*.json ./
=> [4/6] RUN npm install
=> [5/6] COPY . .
=> [6/6] RUN npm run build
=> exporting to image
=> => naming to myapp:latest

Service Selection

Build Specific Services

Build only named services:

# Build only frontend
uc build frontend

# Build frontend and API
uc build frontend api

Build with Dependencies

Build a service and its dependencies:

uc build api --deps

If API depends on shared libraries or other services, they're built too.

Best Practices

1. Optimize Layer Caching

Place frequently changing instructions last:

FROM python:3.11
WORKDIR /app

# Rarely changes - cached
COPY requirements.txt .
RUN pip install -r requirements.txt

# Changes often - separate layer
COPY . .

2. Use .dockerignore

Exclude unnecessary files:

.git
*.log
node_modules
__pycache__
.env*
.vscode

3. Multi-stage for Size

Keep final images small:

FROM golang:1.21 AS builder
COPY . .
RUN go build -o app

FROM alpine:latest
COPY --from=builder /app /app
CMD ["/app"]

4. Pin Base Image Versions

Use specific versions in production:

FROM node:20.11.0-alpine  # Specific version
# Not: FROM node:latest

5. Leverage Build Args for Flexibility

ARG NODE_ENV=production
ARG VERSION=latest

ENV NODE_ENV=${NODE_ENV}
LABEL version="${VERSION}"
uc build --build-arg NODE_ENV=production --build-arg VERSION=1.2.3

Troubleshooting

Build Failures

Check build output for errors:

uc build

Common issues:

  • Missing dependencies in Dockerfile
  • Incorrect build context
  • Network issues during package installation

Slow Builds

Speed up builds:

  1. Use .dockerignore to reduce context size
  2. Optimize layer caching (dependencies first)
  3. Use multi-stage builds to parallelize
  4. Consider smaller base images

Cache Issues

If cache seems wrong:

uc build --no-cache

Out of Disk Space

Clean up old images:

docker image prune -a

Next Steps