Install
To start using this module, make sure you have set up Bzlmod according to the user guide, and add the following to your MODULE.bazel
file:
Read the Release Notes
Version history
Dependency graph
Direct (2) at version 0.2.6
Dependents (2)
About
Modern Bazel rules for building OCI container images with advanced performance optimizations
Tested on
Platforms
Bazel versions
Maintainers
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 filedigest
: Digest of the image (sha256:...)oci_layout
: Complete OCI layout directory with blobsoci_tarball
: OCI layout packaged as a tar file for downstream use
Attribute | Type | Description |
---|---|---|
*name | name | A unique name for this target. |
base | label | Base image to inherit layers from. Should provide ImageManifestInfo or ImageIndexInfo. Default: None |
layers | list of labels | Layers to include in the image. Either a LayerInfo provider or a DefaultInfo with tar files. Default: [] |
platform | dictionary: String → String | Dict containing additional runtime requirements of the image. Default: {} |
user | string | The username or UID which is a platform-specific structure that allows specific control over which user the process run as. Default: "" |
env | dictionary: String → String | Default environment variables to set when starting a container based on this image. Subject to template expansion. Default: {} |
entrypoint | list 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: [] |
cmd | list 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_dir | string | 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: "" |
labels | dictionary: String → String | This field contains arbitrary metadata for the container. Subject to template expansion. Default: {} |
annotations | dictionary: String → String | This field contains arbitrary metadata for the manifest. Subject to template expansion. Default: {} |
stop_signal | string | 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_fragment | label | Optional JSON file containing a partial image config, which will be used as a base for the final image config. Default: None |
build_settings | dictionary: Strings → Label | Build settings for template expansion. Maps template variable names to string_flag targets. These values can be used in Example:
See template expansion for more details. Default: {} |
stamp | string | Enable build stamping for template expansion. Controls whether to include volatile build 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:
- Explicit manifests: Provide pre-built manifests for each platform
- 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 blobsoci_tarball
: OCI layout packaged as a tar file for downstream use
Attribute | Type | Description |
---|---|---|
*name | name | A unique name for this target. |
manifests | list of labels | List of manifests for specific platforms. Default: [] |
platforms | list 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: [] |
annotations | dictionary: String → String | Arbitrary metadata for the image index. Subject to template expansion. Default: {} |
build_settings | dictionary: Strings → Label | Build settings for template expansion. Maps template variable names to string_flag targets. These values can be used in Example:
See template expansion for more details. Default: {} |
stamp | string | Enable build stamping for template expansion. Controls whether to include volatile build 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", }, )
Attribute | Type | Description |
---|---|---|
*name | name | A unique name for this target. |
srcs | dictionary: Strings → Label | Files to include in the layer. Keys are paths in the image (e.g., "/app/bin/server"), Default: {} |
symlinks | dictionary: String → String | Symlinks to create in the layer. Keys are symlink paths in the image, Default: {} |
compress | string | Compression algorithm to use. If set to 'auto', uses the global default compression setting. Default: "auto" |
estargz | string | Whether to use estargz format. If set to 'auto', uses the global default estargz setting. Default: "auto" |
annotations | dictionary: String → String | Annotations to add to the layer metadata as key-value pairs. Default: {} |
default_metadata | string | JSON-encoded default metadata to apply to all files in the layer. Default: "" |
file_metadata | dictionary: String → String | Per-file metadata overrides as a dict mapping file paths to JSON-encoded 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", }, )
Attribute | Type | Description |
---|---|---|
*name | name | A unique name for this target. |
*src | label | The tar file to convert into a layer. Must be a valid tar file (optionally compressed). |
compress | string | Compression algorithm to use. If set to 'auto', uses the global default compression setting. Default: "auto" |
optimize | boolean | If set, rewrites the tar file to deduplicate it's contents. Default: False |
estargz | string | Whether to use estargz format. If set to 'auto', uses the global default estargz setting. Default: "auto" |
annotations | dictionary: 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
Attribute | Type | Description |
---|---|---|
*name | name | A unique name for this target. |
*image | label | Image to load. Should provide ImageManifestInfo or ImageIndexInfo. |
daemon | string | Container daemon to use for loading the image. Available options:
The best performance is achieved with:
Default: "auto" |
tag | string | Tag to apply when loading the image. Subject to template expansion. Default: "" |
strategy | string | Strategy for handling image layers during load. Available strategies:
Default: "auto" |
build_settings | dictionary: Strings → Label | Build settings to use for template expansion. Keys are setting names, values are labels to string_flag targets. Default: {} |
stamp | string | 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.
Attribute | Type | Description |
---|---|---|
*name | name | A unique name for this target. |
*operations | list of labels | List of operations to deploy together. Each operation must provide DeployInfo (typically from image_push or image_load rules). |
push_strategy | string | Push strategy to use for all push operations in the deployment. See push strategies documentation for detailed information. Default: "auto" |
load_strategy | string | Load strategy to use for all load operations in the deployment. Available strategies:
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
Attribute | Type | Description |
---|---|---|
*name | name | A unique name for this target. |
registry | string | Registry URL to push the image to. Common registries:
Subject to template expansion. Default: "" |
repository | string | Repository path within the registry. Subject to template expansion. Default: "" |
tag | string | Tag to apply to the pushed image. Optional - if omitted, the image is pushed by digest only. Subject to template expansion. Default: "" |
tag_list | list of strings | List of tags to apply to the pushed image. Useful for applying multiple tags in a single push:
Cannot be used together with Default: [] |
*image | label | Image to push. Should provide ImageManifestInfo or ImageIndexInfo. |
strategy | string | Push strategy to use. See push strategies documentation for detailed information. Default: "auto" |
build_settings | dictionary: Strings → Label | Build settings for template expansion. Maps template variable names to string_flag targets. These values can be used in Example:
See template expansion for more details. Default: {} |
stamp | string | Enable build stamping for template expansion. Controls whether to include volatile build information:
See template expansion for available stamp variables. Default: "auto" |