rules_img0.2.6

About

Modern Bazel rules for building OCI container images with advanced performance optimizations

Tested on

Platforms

🐧Debian 10
🐧Ubuntu 2004
🍎macOS
🍎macOS Apple Silicon
🪟Windows
🪟Windows ARM64

Bazel versions

Bazel 7.x
Bazel 8.x
🔄Bazel Rolling

Maintainers

  • Malte Poll

Starlark API Documentation

@rules_img//img:image.bzl

Rules to build container images from layers.

Use image_manifest to create a single-platform container image,
and image_index to compose a multi-platform container image index.

Rules

image_manifest

Builds a single-platform OCI container image from a set of layers.

This rule assembles container images by combining:

  • Optional base image layers (from another image_manifest or image_index)
  • Additional layers created by image_layer rules
  • Image configuration (entrypoint, environment, labels, etc.)

The rule produces:

  • OCI manifest and config JSON files
  • An optional OCI layout directory or tar (via output groups)
  • ImageManifestInfo provider for use by image_index or image_push

Example:

image_manifest( name = "my_app", base = "@distroless_cc", layers = [ ":app_layer", ":config_layer", ], entrypoint = ["/usr/bin/app"], env = { "APP_ENV": "production", }, )

Output groups:

  • descriptor: OCI descriptor JSON file
  • digest: Digest of the image (sha256:...)
  • oci_layout: Complete OCI layout directory with blobs
  • oci_tarball: OCI layout packaged as a tar file for downstream use
AttributeTypeDescription
*namename

A unique name for this target.

baselabel

Base image to inherit layers from. Should provide ImageManifestInfo or ImageIndexInfo.

Default: None
layerslist of labels

Layers to include in the image. Either a LayerInfo provider or a DefaultInfo with tar files.

Default: []
platformdictionary: String → String

Dict containing additional runtime requirements of the image.

Default: {}
userstring

The username or UID which is a platform-specific structure that allows specific control over which user the process run as.
This acts as a default value to use when the value is not specified when creating a container.

Default: ""
envdictionary: String → String

Default environment variables to set when starting a container based on this image.

Subject to template expansion.

Default: {}
entrypointlist of strings

A list of arguments to use as the command to execute when the container starts. These values act as defaults and may be replaced by an entrypoint specified when creating a container.

Default: []
cmdlist of strings

Default arguments to the entrypoint of the container. These values act as defaults and may be replaced by any specified when creating a container. If an Entrypoint value is not specified, then the first entry of the Cmd array SHOULD be interpreted as the executable to run.

Default: []
working_dirstring

Sets the current working directory of the entrypoint process in the container. This value acts as a default and may be replaced by a working directory specified when creating a container.

Default: ""
labelsdictionary: String → String

This field contains arbitrary metadata for the container.

Subject to template expansion.

Default: {}
annotationsdictionary: String → String

This field contains arbitrary metadata for the manifest.

Subject to template expansion.

Default: {}
stop_signalstring

This field contains the system call signal that will be sent to the container to exit. The signal can be a signal name in the format SIGNAME, for instance SIGKILL or SIGRTMIN+3.

Default: ""
config_fragmentlabel

Optional JSON file containing a partial image config, which will be used as a base for the final image config.

Default: None
build_settingsdictionary: Strings → Label

Build settings for template expansion.

Maps template variable names to string_flag targets. These values can be used in
env, labels, and annotations attributes using {{.VARIABLE_NAME}} syntax (Go template).

Example:

build_settings = { "REGISTRY": "//settings:docker_registry", "VERSION": "//settings:app_version", }

See template expansion for more details.

Default: {}
stampstring

Enable build stamping for template expansion.

Controls whether to include volatile build information:

  • auto (default): Uses the global stamping configuration
  • enabled: Always include stamp information (BUILD_TIMESTAMP, BUILD_USER, etc.) if Bazel's "--stamp" flag is set
  • disabled: Never include stamp information

See template expansion for available stamp variables.

Default: "auto"
image_index

Creates a multi-platform OCI image index from platform-specific manifests.

This rule combines multiple single-platform images (created by image_manifest) into
a multi-platform image index. The index allows container runtimes to automatically
select the appropriate image for their platform.

The rule supports two usage patterns:

  1. Explicit manifests: Provide pre-built manifests for each platform
  2. Platform transitions: Provide one manifest target and a list of platforms

The rule produces:

  • OCI image index JSON file
  • An optional OCI layout directory or tar (via output groups)
  • ImageIndexInfo provider for use by image_push

Example (explicit manifests):

image_index( name = "multiarch_app", manifests = [ ":app_linux_amd64", ":app_linux_arm64", ":app_darwin_amd64", ], )

Example (platform transitions):

image_index( name = "multiarch_app", manifests = [":app"], platforms = [ "//platform:linux-x86_64", "//platform:linux-aarch64", ], )

Output groups:

  • digest: Digest of the image (sha256:...)
  • oci_layout: Complete OCI layout directory with all platform blobs
  • oci_tarball: OCI layout packaged as a tar file for downstream use
AttributeTypeDescription
*namename

A unique name for this target.

manifestslist of labels

List of manifests for specific platforms.

Default: []
platformslist of labels

(Optional) list of target platforms to build the manifest for. Uses a split transition. If specified, the 'manifests' attribute should contain exactly one manifest.

Default: []
annotationsdictionary: String → String

Arbitrary metadata for the image index.

Subject to template expansion.

Default: {}
build_settingsdictionary: Strings → Label

Build settings for template expansion.

Maps template variable names to string_flag targets. These values can be used in
the annotations attribute using {{.VARIABLE_NAME}} syntax (Go template).

Example:

build_settings = { "REGISTRY": "//settings:docker_registry", "VERSION": "//settings:app_version", }

See template expansion for more details.

Default: {}
stampstring

Enable build stamping for template expansion.

Controls whether to include volatile build information:

  • auto (default): Uses the global stamping configuration
  • enabled: Always include stamp information (BUILD_TIMESTAMP, BUILD_USER, etc.) if Bazel's "--stamp" flag is set
  • disabled: Never include stamp information

See template expansion for available stamp variables.

Default: "auto"

@rules_img//img:layer.bzl

Public API for container image layer rules.

Functions & Macros

file_metadata

Creates a JSON-encoded file metadata string for use with image_layer rules.

This function generates JSON metadata that can be used to customize file attributes
in container image layers, such as permissions, ownership, and timestamps.

Parameters
mode

File permission mode (e.g., "0755", "0644"). String format.

Default: None
uid

User ID of the file owner. Integer.

Default: None
gid

Group ID of the file owner. Integer.

Default: None
uname

User name of the file owner. String.

Default: None
gname

Group name of the file owner. String.

Default: None
mtime

Modification time in RFC3339 format (e.g., "2023-01-01T00:00:00Z"). String.

Default: None
pax_records

Dict of extended attributes to set via PAX records.

Default: None

Rules

image_layer

Creates a container image layer from files, executables, and directories.

This rule packages files into a layer that can be used in container images. It supports:

  • Adding files at specific paths in the image
  • Setting file permissions and ownership
  • Creating symlinks
  • Including executables with their runfiles
  • Compression (gzip, zstd) and eStargz optimization

Example:

load("@rules_img//img:layer.bzl", "image_layer", "file_metadata") # Simple layer with files image_layer( name = "app_layer", srcs = { "/app/bin/server": "//cmd/server", "/app/config.json": ":config.json", }, ) # Layer with custom permissions image_layer( name = "secure_layer", srcs = { "/etc/app/config": ":config", "/etc/app/secret": ":secret", }, default_metadata = file_metadata( mode = "0644", uid = 1000, gid = 1000, ), file_metadata = { "/etc/app/secret": file_metadata(mode = "0600"), }, ) # Layer with symlinks image_layer( name = "bin_layer", srcs = { "/usr/local/bin/app": "//cmd/app", }, symlinks = { "/usr/bin/app": "/usr/local/bin/app", }, )
AttributeTypeDescription
*namename

A unique name for this target.

srcsdictionary: Strings → Label

Files to include in the layer. Keys are paths in the image (e.g., "/app/bin/server"),
values are labels to files or executables. Executables automatically include their runfiles.

Default: {}
symlinksdictionary: String → String

Symlinks to create in the layer. Keys are symlink paths in the image,
values are the targets they point to.

Default: {}
compressstring

Compression algorithm to use. If set to 'auto', uses the global default compression setting.

Default: "auto"
estargzstring

Whether to use estargz format. If set to 'auto', uses the global default estargz setting.
When enabled, the layer will be optimized for lazy pulling and will be compatible with the estargz format.

Default: "auto"
annotationsdictionary: String → String

Annotations to add to the layer metadata as key-value pairs.

Default: {}
default_metadatastring

JSON-encoded default metadata to apply to all files in the layer.
Can include fields like mode, uid, gid, uname, gname, mtime, and pax_records.

Default: ""
file_metadatadictionary: String → String

Per-file metadata overrides as a dict mapping file paths to JSON-encoded metadata.
The path should match the path in the image (the key in srcs attribute).
Metadata specified here overrides any defaults from default_metadata.

Default: {}
layer_from_tar

Creates a container image layer from an existing tar archive.

This rule converts tar files into container image layers, useful for incorporating
pre-built artifacts, third-party distributions, or legacy build outputs.

The rule can:

  • Use tar files as-is or recompress them
  • Optimize tar contents by deduplicating files
  • Add annotations to the layer metadata

Example:

load("@rules_img//img:layer.bzl", "layer_from_tar") # Use an existing tar file as a layer layer_from_tar( name = "third_party_layer", src = "@third_party_lib//:lib.tar.gz", ) # Optimize and recompress layer_from_tar( name = "optimized_layer", src = "//legacy:build_output.tar", optimize = True, # Deduplicate contents compress = "zstd", # Use zstd compression ) # Add metadata annotations layer_from_tar( name = "annotated_layer", src = "//vendor:dependencies.tar.gz", annotations = { "org.opencontainers.image.title": "Vendor Dependencies", "org.opencontainers.image.version": "1.2.3", }, )
AttributeTypeDescription
*namename

A unique name for this target.

*srclabel

The tar file to convert into a layer. Must be a valid tar file (optionally compressed).

compressstring

Compression algorithm to use. If set to 'auto', uses the global default compression setting.

Default: "auto"
optimizeboolean

If set, rewrites the tar file to deduplicate it's contents.
This is useful for reducing the size of the image, but will take extra time and space to store the optimized layer.

Default: False
estargzstring

Whether to use estargz format. If set to 'auto', uses the global default estargz setting.
When enabled, the layer will be optimized for lazy pulling and will be compatible with the estargz format.

Default: "auto"
annotationsdictionary: String → String

Annotations to add to the layer metadata as key-value pairs.

Default: {}

@rules_img//img:load.bzl

Public API for loading container images into a daemon.

The image_load rule creates an executable target that loads container images into a local daemon (containerd or Docker).

Example

load("@rules_img//img:image.bzl", "image_manifest") load("@rules_img//img:load.bzl", "image_load") load("@rules_img//img:layer.bzl", "image_layer") # Create a simple layer image_layer( name = "app_layer", srcs = { "/app/hello.txt": "hello.txt", }, ) # Build an image image_manifest( name = "my_image", base = "@alpine", layers = [":app_layer"], ) # Create a load target image_load( name = "load", image = ":my_image", tag = "my-app:latest", )

Then run:

# Load the image into your local daemon bazel run //:load

Platform Selection

When running the load target, you can use the --platform flag to filter which platforms to load from multi-platform images:

# Load all platforms (default) bazel run //path/to:load_target # Load only linux/amd64 bazel run //path/to:load_target -- --platform linux/amd64

Note: Docker daemon only supports loading a single platform at a time. If multiple platforms are specified with Docker, an error will be returned.

Rules

image_load

Loads container images into a local daemon (Docker or containerd).

This rule creates an executable target that imports OCI images into your local
container runtime. It supports both Docker and containerd, with intelligent
detection of the best loading method for optimal performance.

Key features:

  • Incremental loading: Skips blobs that already exist in the daemon
  • Multi-platform support: Can load entire image indexes or specific platforms
  • Direct containerd integration: Bypasses Docker for faster imports when possible
  • Platform filtering: Use --platform flag at runtime to select specific platforms

The rule produces an executable that can be run with bazel run.

Output groups:

  • tarball: Docker save compatible tarball (only available for single-platform images)

Example:

load("@rules_img//img:load.bzl", "image_load") # Load a single-platform image image_load( name = "load_app", image = ":my_app", # References an image_manifest tag = "my-app:latest", ) # Load a multi-platform image image_load( name = "load_multiarch", image = ":my_app_index", # References an image_index tag = "my-app:latest", daemon = "containerd", # Explicitly use containerd ) # Load with dynamic tagging image_load( name = "load_dynamic", image = ":my_app", tag = "my-app:{{.BUILD_USER}}", # Template expansion build_settings = { "BUILD_USER": "//settings:username", }, )

Runtime usage:

# Load all platforms bazel run //path/to:load_app # Load specific platform only bazel run //path/to:load_multiarch -- --platform linux/arm64 # Build Docker save tarball bazel build //path/to:load_app --output_groups=tarball

Performance notes:

  • When Docker uses containerd storage (Docker 23.0+), images are loaded directly
    into containerd for better performance if the containerd socket is accessible.
  • For older Docker versions, falls back to docker load which requires building
    a tar file (slower and limited to single-platform images)
  • The --platform flag filters which platforms are loaded from multi-platform images
AttributeTypeDescription
*namename

A unique name for this target.

*imagelabel

Image to load. Should provide ImageManifestInfo or ImageIndexInfo.

daemonstring

Container daemon to use for loading the image.

Available options:

  • auto (default): Uses the global default setting (usually docker)
  • containerd: Loads directly into containerd namespace. Supports multi-platform images
    and incremental loading.
  • docker: Loads via Docker daemon. When Docker uses containerd storage (23.0+),
    loads directly into containerd. Otherwise falls back to docker load command which
    is slower and limited to single-platform images.

The best performance is achieved with:

  • Direct containerd access (daemon = "containerd")
  • Docker 23.0+ with containerd storage enabled and accessible containerd socket
Default: "auto"
tagstring

Tag to apply when loading the image. Subject to template expansion.

Default: ""
strategystring

Strategy for handling image layers during load.

Available strategies:

  • auto (default): Uses the global default load strategy
  • eager: Downloads all layers during the build phase. Ensures all layers are
    available locally before running the load command.
  • lazy: Downloads layers only when needed during the load operation. More
    efficient for large images where some layers might already exist in the daemon.
Default: "auto"
build_settingsdictionary: Strings → Label

Build settings to use for template expansion. Keys are setting names, values are labels to string_flag targets.

Default: {}
stampstring

Whether to use stamping for template expansion. If 'enabled', uses volatile-status.txt and version.txt if present. 'auto' uses the global default setting.

Default: "auto"

@rules_img//img:multi_deploy.bzl

Public API for container image multi deploy rule.

Rules

multi_deploy

Merges multiple deploy operations into a single unified deployment command.

This rule takes multiple operations (typically from image_push or image_load rules)
that provide DeployInfo and merges them into a single command that can deploy all
operations in parallel. This is useful for scenarios where you need to push and/or
load multiple related images as a coordinated deployment.

The rule produces an executable that can be run with bazel run.

Example:

load("@rules_img//img:push.bzl", "image_push") load("@rules_img//img:load.bzl", "image_load") load("@rules_img//img:multi_deploy.bzl", "multi_deploy") # Individual operations image_push( name = "push_frontend", image = ":frontend", registry = "gcr.io", repository = "my-project/frontend", tag = "latest", ) image_push( name = "push_backend", image = ":backend", registry = "gcr.io", repository = "my-project/backend", tag = "latest", ) image_load( name = "load_database", image = ":database", tag = "my-database:latest", ) # Unified deployment multi_deploy( name = "deploy_all", operations = [ ":push_frontend", ":push_backend", ":load_database", ], push_strategy = "lazy", load_strategy = "eager", )

Runtime usage:

# Deploy all operations together bazel run //path/to:deploy_all

The deploy-merge subcommand will execute all push and load operations in sequence,
allowing for coordinated deployment of related container images.

AttributeTypeDescription
*namename

A unique name for this target.

*operationslist of labels

List of operations to deploy together.

Each operation must provide DeployInfo (typically from image_push or image_load rules).
All operations will be merged and executed in the order specified.

push_strategystring

Push strategy to use for all push operations in the deployment.

See push strategies documentation for detailed information.

Default: "auto"
load_strategystring

Load strategy to use for all load operations in the deployment.

Available strategies:

  • auto (default): Uses the global default load strategy
  • eager: Downloads all layers during the build phase
  • lazy: Downloads layers only when needed during the load operation
Default: "auto"

@rules_img//img:pull.bzl

Public API for pulling base container images.

@rules_img//img:push.bzl

Public API for container image push rules.

Rules

image_push

Pushes container images to a registry.

This rule creates an executable target that uploads OCI images to container registries.
It supports multiple push strategies optimized for different use cases, from simple
uploads to advanced content-addressable storage integration.

Key features:

  • Multiple push strategies: Choose between eager, lazy, CAS-based, or BES-integrated pushing
  • Template expansion: Dynamic registry, repository, and tag values using build settings
  • Stamping support: Include build information in image tags
  • Incremental uploads: Skip blobs that already exist in the registry

The rule produces an executable that can be run with bazel run.

Example:

load("@rules_img//img:push.bzl", "image_push") # Simple push to Docker Hub image_push( name = "push_app", image = ":my_app", registry = "index.docker.io", repository = "myorg/myapp", tag = "latest", ) # Push multi-platform image with multiple tags image_push( name = "push_multiarch", image = ":my_app_index", # References an image_index registry = "gcr.io", repository = "my-project/my-app", tag_list = ["latest", "v1.0.0"], ) # Dynamic push with build settings image_push( name = "push_dynamic", image = ":my_app", registry = "{{.REGISTRY}}", repository = "{{.PROJECT}}/my-app", tag = "{{.VERSION}}", build_settings = { "REGISTRY": "//settings:registry", "PROJECT": "//settings:project", "VERSION": "//settings:version", }, ) # Push with stamping for unique tags image_push( name = "push_stamped", image = ":my_app", registry = "index.docker.io", repository = "myorg/myapp", tag = "latest-{{.BUILD_TIMESTAMP}}", stamp = "enabled", ) # Digest-only push (no tag) image_push( name = "push_by_digest", image = ":my_app", registry = "gcr.io", repository = "my-project/my-app", # No tag specified - will push by digest only )

Push strategies:

  • eager: Materializes all layers next to push binary. Simple, correct, but may be inefficient.
  • lazy: Layers are not stored locally. Missing layers are streamed from Bazel's remote cache.
  • cas_registry: Uses content-addressable storage for extreme efficiency. Requires
    CAS-enabled infrastructure.
  • bes: Image is pushed as side-effect of Build Event Stream upload. No "bazel run" command needed.
    Requires Build Event Service integration.

See push strategies documentation for detailed comparisons.

Runtime usage:

# Push to registry bazel run //path/to:push_app # The push command will output the image digest
AttributeTypeDescription
*namename

A unique name for this target.

registrystring

Registry URL to push the image to.

Common registries:

  • Docker Hub: index.docker.io
  • Google Container Registry: gcr.io or us.gcr.io
  • GitHub Container Registry: ghcr.io
  • Amazon ECR: 123456789.dkr.ecr.us-east-1.amazonaws.com

Subject to template expansion.

Default: ""
repositorystring

Repository path within the registry.

Subject to template expansion.

Default: ""
tagstring

Tag to apply to the pushed image.

Optional - if omitted, the image is pushed by digest only.

Subject to template expansion.

Default: ""
tag_listlist of strings

List of tags to apply to the pushed image.

Useful for applying multiple tags in a single push:

tag_list = ["latest", "v1.0.0", "stable"]

Cannot be used together with tag. Each tag is subject to template expansion.

Default: []
*imagelabel

Image to push. Should provide ImageManifestInfo or ImageIndexInfo.

strategystring

Push strategy to use.

See push strategies documentation for detailed information.

Default: "auto"
build_settingsdictionary: Strings → Label

Build settings for template expansion.

Maps template variable names to string_flag targets. These values can be used in
registry, repository, and tag attributes using {{.VARIABLE_NAME}} syntax (Go template).

Example:

build_settings = { "REGISTRY": "//settings:docker_registry", "VERSION": "//settings:app_version", }

See template expansion for more details.

Default: {}
stampstring

Enable build stamping for template expansion.

Controls whether to include volatile build information:

  • auto (default): Uses the global stamping configuration
  • enabled: Always include stamp information (BUILD_TIMESTAMP, BUILD_USER, etc.) if Bazel's "--stamp" flag is set
  • disabled: Never include stamp information

See template expansion for available stamp variables.

Default: "auto"