
Docker Syntax Buildkit
- 8 installs
- 9 repo stars
- Updated July 8, 2026
- openaec-foundation/docker-claude-skill-package
Helps with devops & ci/cd tasks.
About
docker-syntax-buildkit is a Claude Code skill for devops & ci/cd. It helps solo builders move faster with AI-assisted development.
- docker-syntax-buildkit
- DevOps & CI/CD
- AI-coding skill
Docker Syntax Buildkit by the numbers
- 8 all-time installs (skills.sh)
- Ranked #1,044 of 1,435 DevOps & CI/CD skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/openaec-foundation/docker-claude-skill-package --skill docker-syntax-buildkitAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 8 |
|---|---|
| repo stars | ★ 9 |
| Last updated | July 8, 2026 |
| Repository | openaec-foundation/docker-claude-skill-package ↗ |
What it does
Helps with devops & ci/cd tasks.
Files
docker-syntax-buildkit
Quick Reference
Syntax Directive
ALWAYS include at the very top of every Dockerfile, before any other instruction:
# syntax=docker/dockerfile:1This enables heredoc syntax, --mount flags, --chmod/--link/--parents/--exclude on COPY/ADD, the # check directive, and all other BuildKit extensions. ALWAYS use docker/dockerfile:1 (not 1.0 or a fixed minor version) to get the latest stable features.
Mount Types Overview
| Mount Type | Purpose | Key Use Case |
|---|---|---|
cache | Persistent cache directories across builds | Package manager caches (apt, npm, pip, go) |
secret | Access credentials without baking into layers | API keys, tokens, registry auth |
ssh | Forward host SSH agent during build | Cloning private Git repositories |
bind | Mount context or stage files (read-only default) | Large source trees, cross-stage file access |
tmpfs | Temporary in-memory filesystem | Scratch space for compilation, tests |
Platform ARGs (Automatic in BuildKit)
| ARG | Example Value | Purpose |
|---|---|---|
TARGETPLATFORM | linux/amd64 | Full target platform string |
TARGETOS | linux | Target operating system |
TARGETARCH | amd64 | Target architecture |
TARGETVARIANT | v7 | Target variant (e.g., ARM version) |
BUILDPLATFORM | linux/amd64 | Host platform running the build |
BUILDOS | linux | Host operating system |
BUILDARCH | amd64 | Host architecture |
These ARGs are available automatically without explicit ARG declaration. ALWAYS declare them with ARG TARGETOS TARGETARCH inside a stage to use them in RUN instructions.
Critical Warnings
NEVER put secrets in ENV or ARG instructions -- they are visible in docker history and image layers. ALWAYS use --mount=type=secret instead.
NEVER omit the syntax directive when using BuildKit features -- without # syntax=docker/dockerfile:1, mount flags and heredoc syntax cause parse errors.
NEVER use sharing=shared (the default) for apt cache mounts -- apt requires exclusive access. ALWAYS use sharing=locked for apt caches.
NEVER assume secret mount contents trigger cache invalidation -- they do NOT. If a secret changes and the build must reflect that change, pass a CACHEBUST build arg.
ALWAYS use set -e in heredoc RUN blocks -- without it, individual command failures are silently ignored and the build continues.
---
Mount Type Decision Tree
Need to mount something during RUN?
|
+-- Persisting package downloads between builds?
| --> type=cache (see Cache Mount Patterns below)
|
+-- Accessing credentials/tokens during build?
| --> type=secret (file or env mode)
|
+-- Cloning private Git repos via SSH?
| --> type=ssh
|
+-- Reading source files without creating a COPY layer?
| +-- From build context? --> type=bind,target=.
| +-- From another stage? --> type=bind,from=<stage>,target=<path>
|
+-- Need temporary scratch space (not persisted)?
--> type=tmpfs---
Heredoc Syntax
Multi-Line RUN
Run multi-line scripts without && chaining:
# syntax=docker/dockerfile:1
RUN <<EOF
#!/usr/bin/env bash
set -e
apt-get update
apt-get install -y --no-install-recommends curl git
rm -rf /var/lib/apt/lists/*
EOFALWAYS include set -e in heredoc RUN blocks. Without it, only the exit code of the LAST command determines success.
Inline File Creation
Create files without a separate COPY:
COPY <<EOF /etc/nginx/conf.d/default.conf
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
}
}
EOFMultiple Heredocs
RUN <<INSTALL && <<CONFIGURE
apt-get update && apt-get install -y nginx
INSTALL
echo "daemon off;" >> /etc/nginx/nginx.conf
CONFIGURE---
Cache Mount Patterns
ALWAYS use cache mounts for package managers. The cache is cumulative -- even when a layer rebuilds, only new/changed packages are downloaded.
| Package Manager | Cache Target(s) | Sharing Mode |
|---|---|---|
| apt | /var/cache/apt + /var/lib/apt | locked (required) |
| npm | /root/.npm | shared (default) |
| yarn | /usr/local/share/.cache/yarn | shared |
| pnpm | /root/.local/share/pnpm/store | shared |
| pip | /root/.cache/pip | shared |
| Go | /go/pkg/mod + /root/.cache/go-build | shared |
| Cargo (Rust) | /app/target/ + /usr/local/cargo/git/db + /usr/local/cargo/registry/ | shared |
| Maven | /root/.m2/repository | shared |
| Bundler (Ruby) | /root/.gem | shared |
| NuGet (.NET) | /root/.nuget/packages | shared |
| Composer (PHP) | /tmp/cache | shared |
See references/examples.md for complete patterns per package manager.
Cache Mount Full Syntax
--mount=type=cache,target=<path>[,id=<id>][,sharing=<shared|private|locked>][,from=<stage>][,source=<path>][,mode=<mode>][,uid=<uid>][,gid=<gid>]| Option | Default | Purpose |
|---|---|---|
target | (required) | Directory to cache |
id | value of target | Cache identity (share across stages with same id) |
sharing | shared | shared: concurrent access; locked: exclusive; private: per-build copy |
from | (none) | Initialize cache from a build stage |
source | (none) | Path within from to seed cache |
mode | 0755 | Directory permissions |
uid | 0 | Owner user ID |
gid | 0 | Owner group ID |
---
Secret Mounts
As File (default)
RUN --mount=type=secret,id=aws,target=/root/.aws/credentials \
aws s3 cp s3://bucket/file /destBuild: docker build --secret id=aws,src=$HOME/.aws/credentials .
As Environment Variable
RUN --mount=type=secret,id=TOKEN,env=TOKEN \
some-command # $TOKEN is availableBuild: docker build --secret id=TOKEN,src=./token.txt .
Secret Mount Options
| Option | Default | Purpose |
|---|---|---|
id | (required) | Secret identifier matching --secret id= |
target | /run/secrets/<id> | Mount path inside the container |
required | false | Fail build if secret is not provided |
env | (none) | Expose as environment variable instead of file |
mode | 0400 | File permissions |
uid | 0 | Owner user ID |
gid | 0 | Owner group ID |
---
SSH Mounts
RUN --mount=type=ssh \
git clone git@github.com:org/private-repo.git /appBuild: docker build --ssh default .
| Option | Default | Purpose |
|---|---|---|
id | default | SSH agent socket identifier |
target | /run/buildkit/ssh_agent.${N} | Mount path for socket |
required | false | Fail build if SSH agent is not available |
ALWAYS add GitHub/GitLab host keys before cloning:
RUN --mount=type=ssh \
mkdir -p ~/.ssh && ssh-keyscan github.com >> ~/.ssh/known_hosts \
&& git clone git@github.com:org/repo.git /app---
Bind Mounts
# Mount entire build context (avoids COPY layer)
RUN --mount=type=bind,target=. go build -o /app/hello
# Mount from another stage
RUN --mount=type=bind,from=build,source=/src,target=/source ls /source
# Mount single file
RUN --mount=type=bind,source=requirements.txt,target=/tmp/requirements.txt \
pip install -r /tmp/requirements.txt| Option | Default | Purpose |
|---|---|---|
target | (required) | Mount destination in container |
source | . (root of context/stage) | Source path |
from | build context | Named stage or image to mount from |
rw | false | Set true for read-write (changes NOT persisted) |
---
Tmpfs Mounts
RUN --mount=type=tmpfs,target=/tmp gcc -o /app/binary source.c| Option | Default | Purpose |
|---|---|---|
target | (required) | Mount path |
size | unlimited | Size limit in bytes |
---
Cross-Compilation Pattern
# syntax=docker/dockerfile:1
FROM --platform=$BUILDPLATFORM golang:1.22-alpine AS build
ARG TARGETOS TARGETARCH
WORKDIR /src
COPY go.mod go.sum ./
RUN --mount=type=cache,target=/go/pkg/mod go mod download
COPY . .
RUN --mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o /bin/app ./cmd
FROM alpine:3.19
COPY --from=build /bin/app /usr/bin/app
ENTRYPOINT ["/usr/bin/app"]Build multi-platform: docker buildx build --platform linux/amd64,linux/arm64 -t myapp:latest .
---
Cache Backends
| Backend | Flag | Use Case |
|---|---|---|
| Inline | type=inline | Embed cache metadata in output image |
| Registry | type=registry,ref=<image> | Store cache as separate registry image |
| Local | type=local,dest=<path> | Local filesystem directory |
| GitHub Actions | type=gha | GitHub Actions cache service |
| S3 | type=s3,bucket=<name>,region=<region> | AWS S3 storage |
| Azure Blob | type=azblob,account_url=<url> | Azure Blob storage |
Cache Modes
min(default) -- Only caches exported layers. Smaller cache, fewer hits.max-- Caches ALL intermediate layers. Larger cache, more hits. ALWAYS usemode=maxin CI/CD.
Registry Cache (CI/CD)
docker buildx build --push -t registry/app:latest \
--cache-to type=registry,ref=registry/app:buildcache,mode=max \
--cache-from type=registry,ref=registry/app:buildcache .GitHub Actions Cache
docker buildx build \
--cache-to type=gha,mode=max \
--cache-from type=gha .---
Reference Links
- references/mounts.md -- Complete reference for all 5 mount types with every option
- references/examples.md -- Cache mount patterns per package manager, secret patterns, SSH patterns
- references/anti-patterns.md -- BuildKit feature misuse and corrections
Official Sources
- https://docs.docker.com/reference/dockerfile/
- https://docs.docker.com/build/buildkit/
- https://docs.docker.com/build/cache/
- https://docs.docker.com/build/cache/backends/
- https://docs.docker.com/build/building/multi-stage/
BuildKit Anti-Patterns
Cache Mount Misuse
Using shared mode for apt
# BAD: apt uses lock files internally -- concurrent access causes failures
RUN --mount=type=cache,target=/var/cache/apt \
--mount=type=cache,target=/var/lib/apt \
apt-get update && apt-get install -y curl# GOOD: ALWAYS use sharing=locked for apt
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && apt-get install -y curlWhy: apt/dpkg uses internal lock files. Without sharing=locked, parallel builds corrupt the cache or fail with lock errors.
---
Removing cache in the same RUN with cache mount
# BAD: Cleaning the cache defeats the purpose of the cache mount
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && apt-get install -y curl \
&& rm -rf /var/lib/apt/lists/*# GOOD: Let the cache mount handle persistence -- no cleanup needed
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && apt-get install -y curlWhy: Cache mount contents are NOT part of the layer. Cleaning them is unnecessary and actually clears the persistent cache that saves time on the next build.
---
Forgetting to copy artifacts out of cache-mounted directories
# BAD: Binary is in cache-mounted target/ -- it disappears after RUN
RUN --mount=type=cache,target=/app/target/ \
--mount=type=cache,target=/usr/local/cargo/registry/ \
cargo build --release
# The binary at /app/target/release/myapp is GONE# GOOD: Copy the artifact to a non-cached path within the same RUN
RUN --mount=type=cache,target=/app/target/ \
--mount=type=cache,target=/usr/local/cargo/registry/ \
cargo build --release \
&& cp /app/target/release/myapp /usr/local/bin/myappWhy: Cache mount directories exist outside the layer. Files written there do NOT become part of the image. ALWAYS copy build artifacts to a non-mounted path before the RUN ends.
---
Not specifying cache id for multi-project builders
# BAD: Projects sharing a builder collide on /root/.npm cache
# Project A
RUN --mount=type=cache,target=/root/.npm npm ci
# Project B (different project, same builder)
RUN --mount=type=cache,target=/root/.npm npm ci# GOOD: Use explicit id to namespace caches
# Project A
RUN --mount=type=cache,target=/root/.npm,id=project-a-npm npm ci
# Project B
RUN --mount=type=cache,target=/root/.npm,id=project-b-npm npm ciWhy: Cache identity defaults to target path. On shared builders (CI/CD), unrelated projects share and potentially corrupt each other's caches.
---
Secret Mount Misuse
Using ARG or ENV for secrets
# BAD: Secret visible in docker history and image metadata
ARG DATABASE_PASSWORD=secret123
ENV API_KEY=sk-1234567890
RUN curl -H "Authorization: Bearer $API_KEY" https://api.example.com# GOOD: Secret available only during RUN, never in any layer
RUN --mount=type=secret,id=api_key,env=API_KEY \
curl -H "Authorization: Bearer $API_KEY" https://api.example.comWhy: ARG values appear in docker history. ENV values persist in the image and are visible via docker inspect. Secret mounts exist only during the RUN instruction and leave zero trace.
---
Assuming secret changes invalidate cache
# BAD: Secret changed but build uses cached layer with old secret's output
RUN --mount=type=secret,id=TOKEN,env=TOKEN \
curl -H "Authorization: $TOKEN" https://api.example.com/data > /data.json# GOOD: Use CACHEBUST arg to force rebuild when secret changes
ARG CACHEBUST
RUN --mount=type=secret,id=TOKEN,env=TOKEN \
curl -H "Authorization: $TOKEN" https://api.example.com/data > /data.jsondocker build --secret id=TOKEN,src=./token.txt \
--build-arg CACHEBUST=$(date +%s) .Why: Secret contents are deliberately excluded from cache keys for security. The RUN layer is cached based on the command string only.
---
Copying secret to a file in the image
# BAD: Secret ends up in a layer
RUN --mount=type=secret,id=creds,target=/tmp/creds \
cp /tmp/creds /app/credentials.json# GOOD: Use secret in-place, never copy it
RUN --mount=type=secret,id=creds,target=/tmp/creds \
my-tool --config /tmp/credsWhy: The whole point of secret mounts is that they leave no trace. Copying the secret to a regular file path bakes it into the layer permanently.
---
SSH Mount Misuse
Not adding known hosts
# BAD: SSH prompts for host key verification -- build hangs indefinitely
RUN --mount=type=ssh \
git clone git@github.com:org/repo.git /app# GOOD: Add known hosts before any SSH operation
RUN --mount=type=ssh \
mkdir -p ~/.ssh \
&& ssh-keyscan github.com >> ~/.ssh/known_hosts \
&& git clone git@github.com:org/repo.git /appWhy: Without known hosts, SSH prompts for interactive confirmation. Docker builds are non-interactive -- the build hangs until timeout.
---
Copying SSH keys into the image
# BAD: Private key baked into image layer
COPY id_rsa /root/.ssh/id_rsa
RUN chmod 600 /root/.ssh/id_rsa \
&& git clone git@github.com:org/repo.git /app# GOOD: Forward SSH agent -- key never touches the image
RUN --mount=type=ssh \
mkdir -p ~/.ssh \
&& ssh-keyscan github.com >> ~/.ssh/known_hosts \
&& git clone git@github.com:org/repo.git /appWhy: Even if you delete the key in a later RUN, it remains in the COPY layer and can be extracted. SSH mounts forward the agent socket -- the private key never enters the build.
---
Heredoc Misuse
Missing set -e in heredoc
# BAD: If apt-get update fails, install still runs (and may use stale packages)
RUN <<EOF
apt-get update
apt-get install -y curl
rm -rf /var/lib/apt/lists/*
EOF# GOOD: set -e ensures any failure stops the build
RUN <<EOF
#!/usr/bin/env bash
set -e
apt-get update
apt-get install -y curl
rm -rf /var/lib/apt/lists/*
EOFWhy: Without set -e, heredoc scripts report only the exit code of the LAST command. Earlier failures are silently ignored, leading to broken images.
---
Using heredoc for single commands
# BAD: Unnecessary complexity for a single command
RUN <<EOF
npm install
EOF# GOOD: Use standard RUN for simple commands
RUN npm installWhy: Heredoc syntax is for multi-line scripts that benefit from avoiding && chains. Single commands gain nothing from it and lose readability.
---
Syntax Directive Misuse
Placing syntax directive after comments or blank lines
# This is my Dockerfile
# syntax=docker/dockerfile:1
FROM alpine
RUN --mount=type=cache,target=/tmp echo hello# syntax=docker/dockerfile:1
# This is my Dockerfile
FROM alpine
RUN --mount=type=cache,target=/tmp echo helloWhy: Parser directives MUST be at the very top of the file, before any comments, blank lines, or instructions. A syntax directive after any other line is treated as a regular comment and ignored -- mount flags then cause parse errors.
---
Pinning to a specific minor version
# BAD: Misses bug fixes and new features
# syntax=docker/dockerfile:1.4# GOOD: Gets latest stable features within major version 1
# syntax=docker/dockerfile:1Why: docker/dockerfile:1 resolves to the latest 1.x release. Pinning to 1.4 misses improvements like --parents, --exclude, # check, and security fixes.
---
Bind Mount Misuse
Expecting bind mount writes to persist
# BAD: Writes to read-write bind mount are discarded after RUN
RUN --mount=type=bind,target=/src,rw=true \
echo "modified" > /src/file.txt
# /src/file.txt is NOT modified in the build context or any layer# GOOD: Write output to a non-mounted path
RUN --mount=type=bind,target=/src \
cp /src/template.txt /app/config.txt \
&& sed -i 's/PLACEHOLDER/value/' /app/config.txtWhy: Bind mount changes (even with rw=true) are never persisted. They do not modify the build context and are not part of any layer. ALWAYS write results to a non-mounted path.
---
Cache Backend Misuse
Using min mode in CI/CD
# BAD: Only caches exported layers -- intermediate stages are rebuilt every time
docker buildx build \
--cache-to type=registry,ref=registry/app:cache \
--cache-from type=registry,ref=registry/app:cache .# GOOD: Cache ALL layers including intermediates
docker buildx build \
--cache-to type=registry,ref=registry/app:cache,mode=max \
--cache-from type=registry,ref=registry/app:cache .Why: The default min mode only caches layers that end up in the final image. Multi-stage builds have many intermediate layers (dependency download, compilation) that provide the biggest cache benefit. ALWAYS use mode=max in CI/CD.
---
Not providing cache-from on first build
# BAD: First build has no cache source -- works but logs warnings
docker buildx build \
--cache-from type=registry,ref=registry/app:cache .This is actually fine -- BuildKit gracefully handles missing cache sources. No change needed. The --cache-from is silently ignored if the cache image does not exist. This is NOT an error.
---
Single cache source without branch fallback
# BAD: Feature branch has no cache -- full rebuild
docker buildx build \
--cache-from type=registry,ref=registry/app:cache-feature .# GOOD: Fall back to main branch cache
docker buildx build \
--cache-from type=registry,ref=registry/app:cache-feature \
--cache-from type=registry,ref=registry/app:cache-main .Why: Feature branches diverge from main. Without a fallback, the first build on a new branch starts from scratch. Multiple --cache-from sources let BuildKit find the best match.
BuildKit Examples -- Cache, Secret, and SSH Patterns
Cache Mount Patterns per Package Manager
apt (Debian/Ubuntu)
# syntax=docker/dockerfile:1
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && apt-get install -y --no-install-recommends \
curl \
git \
build-essentialALWAYS use `sharing=locked` -- apt uses internal lock files and FAILS with concurrent access.
Note: When using cache mounts for apt, do NOT add rm -rf /var/lib/apt/lists/* -- the cache mount handles persistence, and the lists directory is the cache.
---
npm
# syntax=docker/dockerfile:1
WORKDIR /app
COPY package.json package-lock.json ./
RUN --mount=type=cache,target=/root/.npm \
npm ci --prefer-offlineFor non-root builds:
USER node
WORKDIR /app
COPY --chown=node:node package.json package-lock.json ./
RUN --mount=type=cache,target=/home/node/.npm,uid=1000,gid=1000 \
npm ci --prefer-offline---
yarn (v1 Classic)
# syntax=docker/dockerfile:1
WORKDIR /app
COPY package.json yarn.lock ./
RUN --mount=type=cache,target=/usr/local/share/.cache/yarn \
yarn install --frozen-lockfile---
pnpm
# syntax=docker/dockerfile:1
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN --mount=type=cache,target=/root/.local/share/pnpm/store \
corepack enable pnpm && pnpm install --frozen-lockfile---
pip (Python)
# syntax=docker/dockerfile:1
WORKDIR /app
COPY requirements.txt ./
RUN --mount=type=cache,target=/root/.cache/pip \
pip install --no-compile -r requirements.txtWith bind mount (avoids COPY layer for requirements):
RUN --mount=type=cache,target=/root/.cache/pip \
--mount=type=bind,source=requirements.txt,target=/tmp/requirements.txt \
pip install --no-compile -r /tmp/requirements.txt---
Go
# syntax=docker/dockerfile:1
FROM golang:1.22 AS build
WORKDIR /src
# Cache module download
COPY go.mod go.sum ./
RUN --mount=type=cache,target=/go/pkg/mod \
go mod download
# Cache build artifacts
COPY . .
RUN --mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
go build -o /bin/app ./cmdCross-compilation with cache:
FROM --platform=$BUILDPLATFORM golang:1.22 AS build
ARG TARGETOS TARGETARCH
WORKDIR /src
COPY go.mod go.sum ./
RUN --mount=type=cache,target=/go/pkg/mod go mod download
COPY . .
RUN --mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o /bin/app ./cmd---
Cargo (Rust)
# syntax=docker/dockerfile:1
FROM rust:1.77 AS build
WORKDIR /app
COPY Cargo.toml Cargo.lock ./
COPY src/ src/
RUN --mount=type=cache,target=/app/target/ \
--mount=type=cache,target=/usr/local/cargo/git/db \
--mount=type=cache,target=/usr/local/cargo/registry/ \
cargo build --release \
&& cp target/release/myapp /usr/local/bin/myappIMPORTANT: ALWAYS copy the binary OUT of the cache-mounted target/ directory before the RUN ends. The cache mount is not part of the layer -- if you do not copy, the binary is lost.
---
Maven (Java)
# syntax=docker/dockerfile:1
FROM maven:3.9-eclipse-temurin-21 AS build
WORKDIR /app
COPY pom.xml ./
RUN --mount=type=cache,target=/root/.m2/repository \
mvn dependency:go-offline -B
COPY src/ src/
RUN --mount=type=cache,target=/root/.m2/repository \
mvn package -B -DskipTests \
&& cp target/*.jar /app.jar---
Gradle (Java/Kotlin)
# syntax=docker/dockerfile:1
FROM gradle:8.6-jdk21 AS build
WORKDIR /app
COPY build.gradle.kts settings.gradle.kts ./
COPY gradle/ gradle/
RUN --mount=type=cache,target=/home/gradle/.gradle/caches \
gradle dependencies --no-daemon
COPY src/ src/
RUN --mount=type=cache,target=/home/gradle/.gradle/caches \
gradle build --no-daemon -x test \
&& cp build/libs/*.jar /app.jar---
Bundler (Ruby)
# syntax=docker/dockerfile:1
WORKDIR /app
COPY Gemfile Gemfile.lock ./
RUN --mount=type=cache,target=/root/.gem \
bundle install --jobs 4 --retry 3---
NuGet (.NET)
# syntax=docker/dockerfile:1
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /app
COPY *.csproj ./
RUN --mount=type=cache,target=/root/.nuget/packages \
dotnet restore
COPY . .
RUN --mount=type=cache,target=/root/.nuget/packages \
dotnet publish -c Release -o /out---
Composer (PHP)
# syntax=docker/dockerfile:1
WORKDIR /app
COPY composer.json composer.lock ./
RUN --mount=type=cache,target=/tmp/cache \
composer install --no-dev --no-scripts --prefer-dist---
Secret Mount Patterns
Private Registry Authentication
# syntax=docker/dockerfile:1
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \
npm ci --prefer-offlineBuild:
docker build --secret id=npmrc,src=$HOME/.npmrc .---
API Key During Build
# syntax=docker/dockerfile:1
# As environment variable
RUN --mount=type=secret,id=api_key,env=API_KEY \
curl -H "Authorization: Bearer $API_KEY" https://api.example.com/data > /app/data.json
# As file
RUN --mount=type=secret,id=api_key,target=/run/secrets/api_key \
curl -H "Authorization: Bearer $(cat /run/secrets/api_key)" https://api.example.com/data > /app/data.jsonBuild:
# From file
docker build --secret id=api_key,src=./api-key.txt .
# From environment variable
docker build --secret id=api_key,env=API_KEY .---
Multiple Secrets
# syntax=docker/dockerfile:1
RUN --mount=type=secret,id=aws_access,env=AWS_ACCESS_KEY_ID \
--mount=type=secret,id=aws_secret,env=AWS_SECRET_ACCESS_KEY \
aws s3 cp s3://private-bucket/model.bin /app/model.binBuild:
docker build \
--secret id=aws_access,env=AWS_ACCESS_KEY_ID \
--secret id=aws_secret,env=AWS_SECRET_ACCESS_KEY .---
Required Secrets
# syntax=docker/dockerfile:1
# Build FAILS if secret not provided (instead of silently continuing)
RUN --mount=type=secret,id=deploy_key,required=true,target=/root/.ssh/deploy_key \
chmod 600 /root/.ssh/deploy_key \
&& git clone git@github.com:org/config.git /app/config---
Docker Compose Build Secrets
# docker-compose.yml
services:
app:
build:
context: .
secrets:
- npmrc
secrets:
npmrc:
file: ~/.npmrc---
SSH Mount Patterns
Clone Private Repository
# syntax=docker/dockerfile:1
RUN --mount=type=ssh \
mkdir -p ~/.ssh \
&& ssh-keyscan github.com >> ~/.ssh/known_hosts \
&& git clone git@github.com:org/private-repo.git /appBuild:
# Forward running SSH agent
eval $(ssh-agent)
ssh-add ~/.ssh/id_ed25519
docker build --ssh default .---
Multiple SSH Identities
# syntax=docker/dockerfile:1
# Clone from GitHub with default identity
RUN --mount=type=ssh,id=github \
mkdir -p ~/.ssh \
&& ssh-keyscan github.com >> ~/.ssh/known_hosts \
&& git clone git@github.com:org/repo1.git /app/repo1
# Clone from GitLab with deploy key
RUN --mount=type=ssh,id=gitlab \
ssh-keyscan gitlab.com >> ~/.ssh/known_hosts \
&& git clone git@gitlab.com:org/repo2.git /app/repo2Build:
docker build \
--ssh github=$HOME/.ssh/github_key \
--ssh gitlab=$HOME/.ssh/gitlab_deploy_key .---
Go Private Modules via SSH
# syntax=docker/dockerfile:1
FROM golang:1.22 AS build
WORKDIR /src
RUN --mount=type=ssh \
mkdir -p ~/.ssh \
&& ssh-keyscan github.com >> ~/.ssh/known_hosts \
&& git config --global url."git@github.com:".insteadOf "https://github.com/"
COPY go.mod go.sum ./
RUN --mount=type=ssh \
--mount=type=cache,target=/go/pkg/mod \
go mod download
COPY . .
RUN --mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
go build -o /bin/app ./cmd---
Bind Mount Patterns
Build Without COPY Layer (Go)
# syntax=docker/dockerfile:1
FROM golang:1.22 AS build
WORKDIR /src
RUN --mount=type=bind,target=. \
--mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
go build -o /bin/app ./cmdThe source code is mounted read-only. Only /bin/app becomes part of the layer.
---
Cross-Stage File Access
# syntax=docker/dockerfile:1
FROM alpine AS configs
COPY configs/ /configs/
FROM alpine AS app
RUN --mount=type=bind,from=configs,source=/configs,target=/tmp/configs \
cp /tmp/configs/production.yaml /etc/app/config.yaml---
Bind + Cache Combined (Python)
# syntax=docker/dockerfile:1
FROM python:3.12-slim AS build
RUN --mount=type=cache,target=/root/.cache/pip \
--mount=type=bind,source=requirements.txt,target=/tmp/requirements.txt \
pip install --prefix=/install -r /tmp/requirements.txt
FROM python:3.12-slim
COPY --from=build /install /usr/localNo requirements.txt COPY layer exists in the final image. The pip cache persists across builds.
---
Cache Backend Patterns
Inline Cache
docker build -t myapp:latest \
--build-arg BUILDKIT_INLINE_CACHE=1 \
--push .
# Reuse on next build
docker build -t myapp:latest \
--cache-from myapp:latest .Simple but limited -- only caches final stage layers.
---
Registry Cache (CI/CD)
# Build with registry cache
docker buildx build --push -t registry/app:latest \
--cache-to type=registry,ref=registry/app:buildcache,mode=max \
--cache-from type=registry,ref=registry/app:buildcache .
# Feature branch with fallback to main
docker buildx build --push -t registry/app:feature \
--cache-to type=registry,ref=registry/app:cache-feature \
--cache-from type=registry,ref=registry/app:cache-feature \
--cache-from type=registry,ref=registry/app:cache-main .---
Local Cache
docker buildx build \
--cache-to type=local,dest=/tmp/buildcache \
--cache-from type=local,src=/tmp/buildcache .---
GitHub Actions Cache
# .github/workflows/build.yml
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: docker/setup-buildx-action@v4
- uses: docker/build-push-action@v7
with:
push: true
tags: ghcr.io/org/app:latest
cache-from: type=gha
cache-to: type=gha,mode=maxBuildKit Mount Types -- Complete Reference
Cache Mount (--mount=type=cache)
Persists a directory across builds. Contents survive layer rebuilds, enabling incremental package downloads.
Full Syntax
RUN --mount=type=cache,target=<path>[,id=<id>][,sharing=<mode>][,from=<stage>][,source=<path>][,mode=<perms>][,uid=<uid>][,gid=<gid>] <command>All Options
| Option | Required | Default | Description |
|---|---|---|---|
target | YES | -- | Absolute path to the cache directory inside the build container |
id | no | value of target | Unique identifier for the cache. Mounts with the same id share cache storage across stages and builds |
sharing | no | shared | Concurrency mode: shared (concurrent read/write), locked (exclusive access, one build at a time), private (each build gets a fresh copy) |
from | no | (empty) | Build stage or image to initialize cache contents from |
source | no | (empty) | Path within from to use as initial cache seed |
mode | no | 0755 | Directory permissions (octal) |
uid | no | 0 | Owner user ID of the cache directory |
gid | no | 0 | Owner group ID of the cache directory |
Sharing Mode Details
| Mode | Behavior | When to Use |
|---|---|---|
shared | Multiple concurrent builds can read and write simultaneously | MOST package managers (npm, pip, go, cargo) |
locked | Only one build can access the cache at a time; others wait | apt/dpkg (REQUIRED -- apt uses lock files internally) |
private | Each build gets its own copy of the cache; changes are discarded | Test runs that modify cache contents destructively |
Cache Identity Rules
- Caches with the same
idare shared across stages and builds on the same builder. - If
idis omitted,targetis used as the identity. - To namespace caches (e.g., per-project), set explicit
idvalues:id=myproject-npm. - The
BUILDKIT_CACHE_MOUNT_NSARG can prefix all cache IDs globally.
Cache Lifecycle
- Cache mounts are NOT cleared between builds by default.
docker builder pruneremoves all build cache including mount caches.docker builder prune --filter type=exec.cachemountremoves only mount caches.- Cache content is NOT part of any image layer -- it exists only on the builder.
---
Secret Mount (--mount=type=secret)
Exposes sensitive data during build without persisting in any layer. Secret contents are NEVER written to the image or build cache.
Full Syntax
RUN --mount=type=secret,id=<id>[,target=<path>][,required=<bool>][,env=<name>][,mode=<perms>][,uid=<uid>][,gid=<gid>] <command>All Options
| Option | Required | Default | Description |
|---|---|---|---|
id | YES | -- | Identifier matching the --secret id= flag in the build command |
target | no | /run/secrets/<id> | File path where the secret is mounted inside the container |
required | no | false | If true, the build fails when the secret is not provided |
env | no | (none) | Expose the secret as an environment variable with this name instead of a file |
mode | no | 0400 | File permissions (octal). Default is owner-read-only |
uid | no | 0 | Owner user ID |
gid | no | 0 | Owner group ID |
Build Command Syntax
# From file
docker build --secret id=mytoken,src=./token.txt .
# From environment variable
docker build --secret id=mytoken,env=MY_TOKEN .Security Properties
- Secret contents are NEVER part of any image layer.
- Secret contents are NEVER in the build cache.
- Secret contents do NOT appear in
docker history. - Secret files are mounted read-only and exist only during that single RUN instruction.
- Secret changes do NOT trigger cache invalidation -- the RUN layer is cached based on the command string only.
Cache Invalidation Workaround
ARG CACHEBUST
RUN --mount=type=secret,id=TOKEN,env=TOKEN some-commanddocker build --secret id=TOKEN,src=./token.txt --build-arg CACHEBUST=$(date +%s) .---
SSH Mount (--mount=type=ssh)
Forwards the host SSH agent socket into the build container for Git authentication and other SSH operations.
Full Syntax
RUN --mount=type=ssh[,id=<id>][,target=<path>][,required=<bool>][,mode=<perms>][,uid=<uid>][,gid=<gid>] <command>All Options
| Option | Required | Default | Description |
|---|---|---|---|
id | no | default | SSH agent identity, matching --ssh <id>=<path> in the build command |
target | no | /run/buildkit/ssh_agent.${N} | Mount path for the SSH agent socket |
required | no | false | If true, the build fails when the SSH agent is not available |
mode | no | 0600 | Socket file permissions |
uid | no | 0 | Owner user ID |
gid | no | 0 | Owner group ID |
Build Command Syntax
# Forward default SSH agent
docker build --ssh default .
# Forward specific key
docker build --ssh default=$HOME/.ssh/id_ed25519 .
# Multiple SSH identities
docker build --ssh default --ssh deploy=$HOME/.ssh/deploy_key .Security Properties
- The SSH agent socket is forwarded, NOT the private key itself.
- The socket exists only during the RUN instruction execution.
- SSH agent access is NEVER persisted in any layer.
Host Key Verification
ALWAYS add known hosts before using SSH to avoid interactive prompts that hang the build:
RUN --mount=type=ssh \
mkdir -p ~/.ssh \
&& ssh-keyscan github.com >> ~/.ssh/known_hosts \
&& ssh-keyscan gitlab.com >> ~/.ssh/known_hosts \
&& git clone git@github.com:org/repo.git /app---
Bind Mount (--mount=type=bind)
Mounts files from the build context or another stage directly into the build container. Read-only by default. Mounted files are NOT persisted in any layer.
Full Syntax
RUN --mount=type=bind[,target=<path>][,source=<path>][,from=<stage|image>][,rw=<bool>] <command>All Options
| Option | Required | Default | Description |
|---|---|---|---|
target | YES | -- | Mount destination path inside the build container |
source | no | . (root of context or stage) | Source path within the build context or the from stage |
from | no | build context | Named build stage or external image to mount from |
rw | no | false | If true, the mount is read-write. Changes are NOT persisted in any layer or back to the source |
Key Behaviors
- Read-only by default. Write attempts fail unless
rw=trueis set. - Changes with `rw=true` are discarded after the RUN instruction completes. They do NOT modify the source and are NOT part of any layer.
- Avoids creating COPY layers. Only the RUN output is kept. This reduces image size when source files are only needed to produce artifacts.
- Cross-stage mounting with
from=<stage>enables reading files from other stages without COPY.
Common Patterns
# Mount entire build context (Go compilation without COPY)
RUN --mount=type=bind,target=. go build -o /app/hello
# Mount from another stage
FROM builder AS compile
RUN --mount=type=bind,from=source,source=/src,target=/build/src \
make -C /build/src
# Mount single file (e.g., requirements without COPY layer)
RUN --mount=type=bind,source=requirements.txt,target=/tmp/requirements.txt \
pip install -r /tmp/requirements.txt---
Tmpfs Mount (--mount=type=tmpfs)
Creates a temporary in-memory filesystem. Contents are discarded after the RUN instruction completes. NEVER persisted in any layer.
Full Syntax
RUN --mount=type=tmpfs,target=<path>[,size=<bytes>] <command>All Options
| Option | Required | Default | Description |
|---|---|---|---|
target | YES | -- | Mount path inside the build container |
size | no | unlimited (limited by available memory) | Maximum size in bytes |
Use Cases
| Scenario | Why Tmpfs |
|---|---|
| Compilation scratch space | Avoids writing temp files to a layer |
| Test execution temp data | Automatically cleaned up, no layer bloat |
| Sensitive intermediate files | Guaranteed not persisted anywhere |
Example
# Compilation with tmpfs for intermediate objects
RUN --mount=type=tmpfs,target=/tmp \
gcc -o /app/binary -O2 source.c
# Test execution with tmpfs for test artifacts
RUN --mount=type=tmpfs,target=/test-output \
pytest --junitxml=/test-output/results.xml tests/---
Mount Comparison Table
| Property | cache | secret | ssh | bind | tmpfs |
|---|---|---|---|---|---|
| Persists between builds | YES | no | no | no | no |
| Part of image layer | no | no | no | no | no |
| Read-write by default | YES | no | n/a | no | YES |
Supports from stage | YES | no | no | YES | no |
Supports required | no | YES | YES | no | no |
Supports env mode | no | YES | no | no | no |
| Triggers cache invalidation | no | no | no | YES (content changes) | no |