doc/ci/docker/using_buildkit.md
{{< details >}}
{{< /details >}}
BuildKit is the build engine used by Docker and provides multi-platform builds and build caching.
BuildKit offers the following methods to build Docker images:
| Method | Security requirement | Commands | Use when you need |
|---|---|---|---|
| BuildKit rootless | No privileged containers | buildctl-daemonless.sh | Maximum security or a replacement for Kaniko |
| Docker Buildx | Requires docker:dind | docker buildx | Familiar Docker workflow |
| Native BuildKit | Requires docker:dind | buildctl | Advanced BuildKit control |
DockerfileBuildKit in standalone mode provides rootless image builds without Docker daemon dependency. This method eliminates privileged containers entirely and provides a direct replacement for Kaniko builds.
Key differences from other methods:
moby/buildkit:rootless imageBUILDKITD_FLAGS: --oci-worker-no-process-sandbox for rootless operationbuildctl-daemonless.sh to manage BuildKit daemon automaticallyGitLab CI/CD provides automatic authentication for the GitLab container registry through predefined variables. For BuildKit rootless, you must manually create the Docker configuration file.
GitLab automatically provides these predefined variables:
CI_REGISTRY: Registry URLCI_REGISTRY_USER: Registry usernameCI_REGISTRY_PASSWORD: Registry passwordTo configure authentication for rootless builds, add a before_script configuration
to your jobs. For example:
before_script:
- mkdir -p ~/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > ~/.docker/config.json
To authenticate with additional container registries, combine authentication entries
in your before_script section. For example:
before_script:
- mkdir -p ~/.docker
- |
echo "{
\"auths\": {
\"${CI_REGISTRY}\": {
\"auth\": \"$(printf "%s:%s" "${CI_REGISTRY_USER}" "${CI_REGISTRY_PASSWORD}" | base64 | tr -d '\n')\"
},
\"docker.io\": {
\"auth\": \"$(printf "%s:%s" "${DOCKER_HUB_USER}" "${DOCKER_HUB_PASSWORD}" | base64 | tr -d '\n')\"
}
}
}" > ~/.docker/config.json
To pull images through the GitLab dependency proxy, configure the authentication
in your before_script section. For example:
before_script:
- mkdir -p ~/.docker
- |
echo "{
\"auths\": {
\"${CI_REGISTRY}\": {
\"auth\": \"$(printf "%s:%s" "${CI_REGISTRY_USER}" "${CI_REGISTRY_PASSWORD}" | base64 | tr -d '\n')\"
},
\"$(echo -n $CI_DEPENDENCY_PROXY_SERVER | awk -F[:] '{print $1}')\": {
\"auth\": \"$(printf "%s:%s" ${CI_DEPENDENCY_PROXY_USER} "${CI_DEPENDENCY_PROXY_PASSWORD}" | base64 | tr -d '\n')\"
}
}
}" > ~/.docker/config.json
For more information, see authenticate within CI/CD.
To build images without Docker daemon dependency, add a job similar to this example:
build-rootless:
image:
name: moby/buildkit:rootless
entrypoint: [""]
stage: build
variables:
BUILDKITD_FLAGS: --oci-worker-no-process-sandbox
before_script:
- mkdir -p ~/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > ~/.docker/config.json
script:
- |
buildctl-daemonless.sh build \
--frontend dockerfile.v0 \
--local context=. \
--local dockerfile=. \
--output type=image,name=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA,push=true
To build images for multiple architectures in rootless mode, configure your job to specify the target platforms. For example:
build-multiarch-rootless:
image:
name: moby/buildkit:rootless
entrypoint: [""]
stage: build
variables:
BUILDKITD_FLAGS: --oci-worker-no-process-sandbox
before_script:
- mkdir -p ~/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > ~/.docker/config.json
script:
- |
buildctl-daemonless.sh build \
--frontend dockerfile.v0 \
--local context=. \
--local dockerfile=. \
--opt platform=linux/amd64,linux/arm64 \
--output type=image,name=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA,push=true
To enable registry-based caching for faster subsequent builds, configure cache import and export in your build job. For example:
build-cached-rootless:
image:
name: moby/buildkit:rootless
entrypoint: [""]
stage: build
variables:
BUILDKITD_FLAGS: --oci-worker-no-process-sandbox
CACHE_IMAGE: $CI_REGISTRY_IMAGE:cache
before_script:
- mkdir -p ~/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > ~/.docker/config.json
script:
- |
buildctl-daemonless.sh build \
--frontend dockerfile.v0 \
--local context=. \
--local dockerfile=. \
--export-cache type=registry,ref=$CACHE_IMAGE \
--import-cache type=registry,ref=$CACHE_IMAGE \
--output type=image,name=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA,push=true
Registry mirrors provide faster image pulls and can help with rate limiting or network restrictions.
To configure registry mirrors, create a buildkit.toml file that specifies the mirror endpoints. For example:
build-mirror-rootless:
image:
name: moby/buildkit:rootless
entrypoint: [""]
stage: build
variables:
BUILDKITD_FLAGS: --oci-worker-no-process-sandbox --config /tmp/buildkit.toml
before_script:
- mkdir -p ~/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > ~/.docker/config.json
- cat <<'EOF' > /tmp/buildkit.toml
[registry."docker.io"]
mirrors = ["mirror.example.com"]
EOF
script:
- |
buildctl-daemonless.sh build \
--frontend dockerfile.v0 \
--local context=. \
--local dockerfile=. \
--output type=image,name=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA,push=true
In this example, replace mirror.example.com with your registry mirror URL.
If your GitLab Runner operates behind an HTTP(S) proxy, configure proxy settings as variables in your job. For example:
build-behind-proxy:
image:
name: moby/buildkit:rootless
entrypoint: [""]
stage: build
variables:
BUILDKITD_FLAGS: --oci-worker-no-process-sandbox
http_proxy: <your-proxy>
https_proxy: <your-proxy>
no_proxy: <your-no-proxy>
before_script:
- mkdir -p ~/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > ~/.docker/config.json
script:
- |
buildctl-daemonless.sh build \
--frontend dockerfile.v0 \
--local context=. \
--local dockerfile=. \
--build-arg http_proxy=$http_proxy \
--build-arg https_proxy=$https_proxy \
--build-arg no_proxy=$no_proxy \
--output type=image,name=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA,push=true
In this example, replace <your-proxy> and <your-no-proxy> with your proxy configuration.
To push to a registry using custom CA certificates, add the certificate to the container's certificate store before building. For example:
build-with-custom-certs:
image:
name: moby/buildkit:rootless
entrypoint: [""]
stage: build
variables:
BUILDKITD_FLAGS: --oci-worker-no-process-sandbox
before_script:
- export SSL_CERT_FILE="$HOME/ca_chain.pem"
- cat /etc/ssl/certs/ca-certificates.crt > "$SSL_CERT_FILE"
- echo "$MY_CA_CERT" >> "$SSL_CERT_FILE"
- mkdir -p ~/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > ~/.docker/config.json
script:
- |
buildctl-daemonless.sh build \
--frontend dockerfile.v0 \
--local context=. \
--local dockerfile=. \
--output type=image,name=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA,push=true
In this example, populate the MY_CA_CERT variable with the full contents of your CA certificate, including both the root and any intermediate certificates.
BuildKit rootless is a secure alternative for Kaniko. It offers improved performance, better caching, and enhanced security features while maintaining rootless operation.
Update your existing Kaniko configuration to use the BuildKit rootless method. For example:
Before, with Kaniko:
build:
image:
name: gcr.io/kaniko-project/executor:debug
entrypoint: [""]
script:
- /kaniko/executor
--context $CI_PROJECT_DIR
--dockerfile $CI_PROJECT_DIR/Dockerfile
--destination $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
After, with BuildKit rootless:
build:
image:
name: moby/buildkit:rootless
entrypoint: [""]
variables:
BUILDKITD_FLAGS: --oci-worker-no-process-sandbox
before_script:
- mkdir -p ~/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > ~/.docker/config.json
script:
- |
buildctl-daemonless.sh build \
--frontend dockerfile.v0 \
--local context=. \
--local dockerfile=. \
--output type=image,name=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA,push=true
If you don't need rootless builds, BuildKit offers additional methods that require
the docker:dind service but provide familiar workflows or advanced features.
Docker Buildx extends Docker build capabilities with BuildKit features while maintaining
familiar command syntax. This method requires the docker:dind service.
To build Docker images with Buildx, configure your job with the docker:dind service
and create a buildx builder. For example:
variables:
DOCKER_TLS_CERTDIR: "/certs"
build-image:
image: docker:cli
services:
- docker:dind
stage: build
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker buildx create --use --driver docker-container --name builder
- docker buildx inspect --bootstrap
script:
- docker buildx build --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA --push .
after_script:
- docker buildx rm builder
Multi-platform builds create images for different architectures in a single build command. The resulting manifest supports multiple architectures, and Docker automatically selects the appropriate image for each deployment target.
To build images for multiple architectures, add the --platform flag to specify
target architectures. For example:
variables:
DOCKER_TLS_CERTDIR: "/certs"
build-multiplatform:
image: docker:cli
services:
- docker:dind
stage: build
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker buildx create --use --driver docker-container --name multibuilder
- docker buildx inspect --bootstrap
script:
- docker buildx build
--platform linux/amd64,linux/arm64
--tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
--push .
after_script:
- docker buildx rm multibuilder
Registry-based caching stores build layers in a container registry for reuse across builds.
The mode=max option exports all layers to the cache
and provides maximum reuse potential for subsequent builds.
To use build caching, add cache options to your build command. For example:
variables:
DOCKER_TLS_CERTDIR: "/certs"
CACHE_IMAGE: $CI_REGISTRY_IMAGE:cache
build-with-cache:
image: docker:cli
services:
- docker:dind
stage: build
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker buildx create --use --driver docker-container --name cached-builder
- docker buildx inspect --bootstrap
script:
- docker buildx build
--cache-from type=registry,ref=$CACHE_IMAGE
--cache-to type=registry,ref=$CACHE_IMAGE,mode=max
--tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
--push .
after_script:
- docker buildx rm cached-builder
Use native BuildKit buildctl commands for more control over the build process.
This method requires the docker:dind service.
To use BuildKit directly, configure your job with the BuildKit image and docker:dind service. For example:
variables:
DOCKER_TLS_CERTDIR: "/certs"
build-with-buildkit:
image: moby/buildkit:latest
services:
- docker:dind
stage: build
before_script:
- mkdir -p ~/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > ~/.docker/config.json
script:
- |
buildctl build \
--frontend dockerfile.v0 \
--local context=. \
--local dockerfile=. \
--output type=image,name=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA,push=true
If you encounter registry authentication failures:
CI_REGISTRY_USER and CI_REGISTRY_PASSWORD variables are available.For permission-related issues in rootless mode:
BUILDKITD_FLAGS: --oci-worker-no-process-sandbox is set.Dockerfile.If you receive [rootlesskit:child ] error: failed to share mount point: /: permission denied
on a Kubernetes runner, AppArmor is blocking the mount syscall required for BuildKit.
To resolve this issue, add the following to your runner configuration:
[runners.kubernetes.pod_annotations]
"container.apparmor.security.beta.kubernetes.io/build" = "unconfined"
invalid local: stat path/to/image/Dockerfile: not a directoryYou might get an error that states invalid local: stat path/to/image/Dockerfile: not a directory.
This issue occurs when you specify a file path instead of a directory path for the
--local dockerfile= parameter. BuildKit expects a directory path that contains
a file named Dockerfile.
To resolve this issue, use the directory path instead of the full file path. For example:
--local dockerfile=path/to/image--local dockerfile=path/to/image/DockerfileFor multi-platform build issues:
Dockerfile support the target architectures.Dockerfile for architecture-specific logic.