Skip to content

Build Docker images with BuildKit

  • Tier: Free, Premium, Ultimate
  • Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated

BuildKit is the build engine used by Docker and provides multi-platform builds and build caching.

BuildKit methods

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

Prerequisites

  • GitLab Runner with Docker executor
  • Docker 19.03 or later to use Docker Buildx
  • A project with a Dockerfile

BuildKit rootless

BuildKit 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:

  • Uses the moby/buildkit:rootless image
  • Includes BUILDKITD_FLAGS: --oci-worker-no-process-sandbox for rootless operation
  • Uses buildctl-daemonless.sh to manage BuildKit daemon automatically
  • No Docker daemon or privileged container dependency
  • Requires manual registry authentication setup

Authenticate with container registries

GitLab CI/CD provides automatic authentication for the GitLab container registry through predefined variables. For BuildKit rootless, you must manually create the Docker configuration file.

Authenticate with the GitLab container registry

GitLab automatically provides these predefined variables:

  • CI_REGISTRY: Registry URL
  • CI_REGISTRY_USER: Registry username
  • CI_REGISTRY_PASSWORD: Registry password

To 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

Authenticate with multiple registries

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

Authenticate with the dependency proxy

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.

Build images in rootless mode

To build images without Docker daemon dependency, add a job similar to this example:

build-rootless:
  image: moby/buildkit:rootless
  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

Build multi-platform images in rootless mode

To build images for multiple architectures in rootless mode, configure your job to specify the target platforms. For example:

build-multiarch-rootless:
  image: moby/buildkit:rootless
  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

Use caching in rootless mode

To enable registry-based caching for faster subsequent builds, configure cache import and export in your build job. For example:

build-cached-rootless:
  image: moby/buildkit:rootless
  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

Configure proxy settings

If your GitLab Runner operates behind an HTTP(S) proxy, configure proxy settings as variables in your job. For example:

build-behind-proxy:
  image: moby/buildkit:rootless
  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.

Add custom certificates

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: moby/buildkit:rootless
  stage: build
  variables:
    BUILDKITD_FLAGS: --oci-worker-no-process-sandbox
  before_script:
    - |
      echo "-----BEGIN CERTIFICATE-----
      ...
      -----END CERTIFICATE-----" >> /etc/ssl/certs/ca-certificates.crt
    - 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, replace the certificate placeholder with your actual certificate content.

Migrate from Kaniko to BuildKit

BuildKit rootless is a secure alternative for Kaniko. It offers improved performance, better caching, and enhanced security features while maintaining rootless operation.

Update your configuration

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: moby/buildkit:rootless
  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

Alternative BuildKit methods

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

Docker Buildx extends Docker build capabilities with BuildKit features while maintaining familiar command syntax. This method requires the docker:dind service.

Build basic images

To build Docker images with Buildx, configure your job with the docker:dind service and create a buildx builder. 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
```0

#### Build multi-platform images

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:

```yaml
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
```1

#### Use build caching

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:

```yaml
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
```2

### Native BuildKit

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:

```yaml
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
```3

## Troubleshooting

### Build fails with authentication errors

If you encounter registry authentication failures:

- Verify that `CI_REGISTRY_USER` and `CI_REGISTRY_PASSWORD` variables are available.
- Check that you have push permissions to the target registry.
- For external registries, ensure authentication credentials are correctly configured
  in your project's CI/CD variables.

### Rootless build fails with permission errors

For permission-related issues in rootless mode:

- Ensure `BUILDKITD_FLAGS: --oci-worker-no-process-sandbox` is set.
- Verify that the GitLab Runner has sufficient resources allocated.
- Check that no privileged operations are attempted in your `Dockerfile`.

### Multi-platform builds fail

For multi-platform build issues:

- Verify that base images in your `Dockerfile` support the target architectures.
- Check that architecture-specific dependencies are available for all target platforms.
- Consider using conditional statements in your `Dockerfile` for architecture-specific logic.