optimize-docker-build-cache

Optimize Docker build times using layer caching, multi-stage builds, BuildKit features, and dependency-first copy patterns. Applicable to R, Node.js, and Python projects. Use when Docker builds are slow due to repeated package installations, when rebuilds reinstall all dependencies on every code change, when image sizes are unnecessarily large, or when CI/CD pipeline builds are a bottleneck.

9 stars

Best use case

optimize-docker-build-cache is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Optimize Docker build times using layer caching, multi-stage builds, BuildKit features, and dependency-first copy patterns. Applicable to R, Node.js, and Python projects. Use when Docker builds are slow due to repeated package installations, when rebuilds reinstall all dependencies on every code change, when image sizes are unnecessarily large, or when CI/CD pipeline builds are a bottleneck.

Teams using optimize-docker-build-cache should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/optimize-docker-build-cache/SKILL.md --create-dirs "https://raw.githubusercontent.com/pjt222/agent-almanac/main/i18n/caveman-lite/skills/optimize-docker-build-cache/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/optimize-docker-build-cache/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How optimize-docker-build-cache Compares

Feature / Agentoptimize-docker-build-cacheStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Optimize Docker build times using layer caching, multi-stage builds, BuildKit features, and dependency-first copy patterns. Applicable to R, Node.js, and Python projects. Use when Docker builds are slow due to repeated package installations, when rebuilds reinstall all dependencies on every code change, when image sizes are unnecessarily large, or when CI/CD pipeline builds are a bottleneck.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Optimize Docker Build Cache

Reduce Docker build times through effective layer caching and build optimization.

## When to Use

- Docker builds are slow due to repeated package installations
- Rebuilds reinstall all dependencies on every code change
- Image sizes are unnecessarily large
- CI/CD pipeline builds are a bottleneck

## Inputs

- **Required**: Existing Dockerfile to optimize
- **Optional**: Target build time improvement
- **Optional**: Target image size reduction

## Procedure

### Step 1: Order Layers by Change Frequency

Place least-changing layers first:

```dockerfile
# 1. Base image (rarely changes)
FROM rocker/r-ver:4.5.0

# 2. System dependencies (change occasionally)
RUN apt-get update && apt-get install -y \
    libcurl4-openssl-dev \
    libssl-dev \
    && rm -rf /var/lib/apt/lists/*

# 3. Dependency files only (change when deps change)
COPY renv.lock renv.lock
COPY renv/activate.R renv/activate.R
RUN R -e "renv::restore()"

# 4. Source code (changes frequently)
COPY . .
```

**Key principle**: Docker caches each layer. When a layer changes, all subsequent layers are rebuilt. Dependency installation should come before source code copy.

**Got:** The Dockerfile layers are ordered from least-changing (base image, system deps) to most-changing (source code), with dependency lockfiles copied before the full source.

**If fail:** If builds still reinstall dependencies on every code change, verify that `COPY . .` comes after the dependency installation `RUN` command, not before.

### Step 2: Separate Dependency Installation from Code

**Bad** (rebuilds packages on every code change):

```dockerfile
COPY . .
RUN R -e "renv::restore()"
```

**Good** (only rebuilds packages when lockfile changes):

```dockerfile
COPY renv.lock renv.lock
RUN R -e "renv::restore()"
COPY . .
```

Same pattern for Node.js:

```dockerfile
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
```

**Got:** Dependency lockfile (`renv.lock`, `package-lock.json`, `requirements.txt`) is copied and installed in a separate layer before the full source code `COPY . .`.

**If fail:** If the lockfile copy fails, ensure the file exists in the build context and is not excluded by `.dockerignore`.

### Step 3: Use Multi-Stage Builds

Separate build dependencies from runtime:

```dockerfile
# Build stage - includes dev tools
FROM rocker/r-ver:4.5.0 AS builder
RUN apt-get update && apt-get install -y \
    libcurl4-openssl-dev libssl-dev build-essential
COPY renv.lock .
RUN R -e "install.packages('renv'); renv::restore()"

# Runtime stage - minimal image
FROM rocker/r-ver:4.5.0
RUN apt-get update && apt-get install -y \
    libcurl4 libssl3 \
    && rm -rf /var/lib/apt/lists/*
COPY --from=builder /usr/local/lib/R/site-library /usr/local/lib/R/site-library
COPY . /app
WORKDIR /app
CMD ["Rscript", "main.R"]
```

**Got:** The Dockerfile has a builder stage with dev tools and a runtime stage with only production dependencies. The final image is significantly smaller than a single-stage build.

**If fail:** If `COPY --from=builder` fails to find libraries, verify the install path matches between stages. Use `docker build --target builder .` to debug the build stage independently.

### Step 4: Combine RUN Commands

Each `RUN` creates a layer. Combine related commands:

**Bad** (3 layers, apt cache persists):

```dockerfile
RUN apt-get update
RUN apt-get install -y curl git
RUN rm -rf /var/lib/apt/lists/*
```

**Good** (1 layer, clean cache):

```dockerfile
RUN apt-get update && apt-get install -y \
    curl \
    git \
    && rm -rf /var/lib/apt/lists/*
```

**Got:** Related `apt-get` or package install commands are combined into single `RUN` instructions, each ending with cache cleanup (`rm -rf /var/lib/apt/lists/*`).

**If fail:** If a combined `RUN` command fails midway, temporarily split it to identify the failing command, then recombine after fixing.

### Step 5: Use .dockerignore

Prevent unnecessary files from entering the build context:

```
.git
.Rproj.user
.Rhistory
.RData
renv/library
renv/cache
node_modules
docs/
*.tar.gz
.env
```

**Got:** A `.dockerignore` file exists in the project root excluding `.git`, `node_modules`, `renv/library`, build artifacts, and environment files. Build context size is noticeably smaller.

**If fail:** If needed files are missing in the container, check `.dockerignore` for overly broad patterns. Use `docker build` verbose output to verify which files are sent to the daemon.

### Step 6: Enable BuildKit

```bash
DOCKER_BUILDKIT=1 docker build -t myimage .
```

Or in `docker-compose.yml`:

```yaml
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
```

With `COMPOSE_DOCKER_CLI_BUILD=1` and `DOCKER_BUILDKIT=1` environment variables.

BuildKit enables:
- Parallel stage builds
- Better cache management
- `--mount=type=cache` for persistent package caches

**Got:** Builds run with BuildKit enabled (indicated by `#1 [internal] load build definition` style output). Multi-stage builds execute stages in parallel where possible.

**If fail:** If BuildKit is not active, verify the environment variables are exported before the build command. On older Docker versions, upgrade Docker Engine to 18.09+ for BuildKit support.

### Step 7: Use Cache Mounts for Package Managers

```dockerfile
# R packages with persistent cache
RUN --mount=type=cache,target=/usr/local/lib/R/site-library \
    R -e "install.packages('dplyr')"

# npm with persistent cache
RUN --mount=type=cache,target=/root/.npm \
    npm ci
```

**Got:** Subsequent builds reuse cached packages from the mount, dramatically reducing install times even when the layer is invalidated. Cache persists across builds.

**If fail:** If `--mount=type=cache` is not recognized, ensure BuildKit is enabled (`DOCKER_BUILDKIT=1`). The syntax requires BuildKit and is not supported by the legacy builder.

## Validation

- [ ] Rebuilds after code-only changes are significantly faster
- [ ] Dependency installation layer is cached when lockfile has not changed
- [ ] `.dockerignore` excludes unnecessary files
- [ ] Image size is reduced compared to unoptimized build
- [ ] Multi-stage build (if used) separates build and runtime dependencies

## Pitfalls

- **Copying all files before installing deps**: Invalidates the dependency cache on every code change
- **Forgetting `.dockerignore`**: Large build contexts slow down every build
- **Too many layers**: Each `RUN`, `COPY`, `ADD` creates a layer. Combine where logical.
- **Not cleaning apt cache**: Always end apt-get installs with `&& rm -rf /var/lib/apt/lists/*`
- **Platform-specific caches**: Cache layers are platform-specific. CI runners may not benefit from local caches.

## Related Skills

- `create-r-dockerfile` - initial Dockerfile creation
- `setup-docker-compose` - compose build configuration
- `containerize-mcp-server` - apply optimizations to MCP server builds

Related Skills

setup-docker-compose

9
from pjt222/agent-almanac

Configure Docker Compose for multi-container R development environments. Covers service definitions, volume mounts, networking, environment variables, and dev vs production configurations. Use to run R alongside other services (databases, APIs), set up a reproducible R dev environment, orchestrate an R-based MCP server container, or manage environment variables and volume mounts for R projects.

optimize-shiny-performance

9
from pjt222/agent-almanac

Profile and optimize Shiny application performance using profvis, bindCache, memoise, async/promises, debounce/throttle, and ExtendedTask for long-running computations. Use when the app feels slow or unresponsive during user interaction, when server resources are exhausted under concurrent load, when specific operations create bottlenecks, or when preparing an app for production deployment with many concurrent users.

optimize-cloud-costs

9
from pjt222/agent-almanac

Implement cloud cost optimization strategies for Kubernetes workloads using tools like Kubecost for visibility, right-sizing recommendations, horizontal and vertical pod autoscaling, spot/preemptible instances, and resource quotas. Covers cost allocation, showback reporting, and continuous optimization practices. Use when cloud costs are growing without proportional business value, when resource requests are misaligned with actual usage, when manual scaling leads to over-provisioning, or when implementing showback and chargeback for internal cost accountability.

create-r-dockerfile

9
from pjt222/agent-almanac

Create a Dockerfile for R projects using rocker base images. Covers system dependency installation, R package installation, renv integration, and optimized layer ordering for fast rebuilds. Use when containerizing an R application or analysis, creating reproducible R environments, deploying R-based services (Shiny, Plumber, MCP server), or setting up consistent development environments across machines.

create-multistage-dockerfile

9
from pjt222/agent-almanac

Create multi-stage Dockerfiles that separate build and runtime environments for minimal production images. Covers builder/runtime stage separation, artifact copying, scratch/distroless/alpine targets, and size comparison. Use when production images are too large, when build tools are included in the final image, when you need separate dev and prod images from one Dockerfile, or when deploying to constrained environments like edge or serverless.

build-tcg-deck

9
from pjt222/agent-almanac

Build a competitive or casual trading card game deck. Covers archetype selection, mana/energy curve analysis, win condition identification, meta-game positioning, and sideboard construction for Pokemon TCG, Magic: The Gathering, Flesh and Blood, and other TCGs. Use when building a new deck for a tournament format or casual play, adapting an existing deck to a changed meta-game, evaluating whether a new set warrants a deck change, or converting a deck concept into a tournament-ready list.

build-shiny-module

9
from pjt222/agent-almanac

Build reusable Shiny modules with proper namespace isolation using NS(). Covers module UI/server pairs, reactive return values, inter-module communication, and nested module composition. Use when extracting a reusable component from a growing Shiny app, building a UI widget used in multiple places, encapsulating complex reactive logic behind a clean interface, or composing larger applications from smaller, testable units.

build-sequential-circuit

9
from pjt222/agent-almanac

Build sequential (stateful) logic circuits including latches, flip-flops, registers, counters, and finite state machines. Covers SR latch, D and JK flip-flops, binary/BCD/ring counters, and Mealy/Moore FSM design with clock signal and timing analysis. Use when a circuit must remember past inputs, count events, or implement a state-dependent control sequence.

build-pkgdown-site

9
from pjt222/agent-almanac

Build and deploy a pkgdown documentation site for an R package to GitHub Pages. Covers _pkgdown.yml configuration, theming, article organization, reference index customization, and deployment methods. Use when creating a documentation site for a new or existing package, customizing layout or navigation, fixing 404 errors on a deployed site, or migrating between branch-based and GitHub Actions deployment methods.

build-parameterized-report

9
from pjt222/agent-almanac

Create parameterized Quarto or R Markdown reports that can be rendered with different inputs to generate multiple variations. Covers parameter definitions, programmatic rendering, and batch generation. Use when generating the same report for different departments, regions, or time periods; creating client-specific reports from a single template; building dashboards that filter to specific subsets; or automating recurring reports with varying inputs.

build-grafana-dashboards

9
from pjt222/agent-almanac

Create production-ready Grafana dashboards with reusable panels, template variables, annotations, and provisioning for version-controlled dashboard deployment. Use when creating visual representations of Prometheus, Loki, or other data source metrics, building operational dashboards for SRE teams, migrating from manual dashboard creation to version-controlled provisioning, or establishing executive-level SLO compliance reporting.

build-feature-store

9
from pjt222/agent-almanac

Build a feature store using Feast for centralized feature management, configure offline and online stores for batch and real-time serving, define feature views with transformations, and implement point-in-time correct joins for ML pipelines. Use when managing features for multiple ML models, ensuring training-serving consistency, serving low-latency features for real-time inference, reusing feature definitions across projects, or building a feature catalog for discovery and governance.