Simple layer with files

July 3, 2026 ยท View on GitHub

Public API for container image layer rules.

image_layer

load("@rules_img//img:layer.bzl", "image_layer")

image_layer(name, srcs, annotations, annotations_file, compress, create_parent_directories,
            default_metadata, estargz, file_metadata, include_runfiles, media_type, multi_file_layout,
            symlinks, tree_artifact_handling)

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 and any additional default outputs
  • 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",
    },
)

Output groups

  • mtree: a single mtree text file

ATTRIBUTES

NameDescriptionTypeMandatoryDefault
nameA unique name for this target.Namerequired
srcsFiles to include in the layer. Keys are paths in the image (e.g., "/app/bin/server"), values are labels to files or executables.

When a value is an executable, the executable is placed at the path key and its runfiles tree is included (unless include_runfiles is set to False). Any additional default outputs of the target (the rest of DefaultInfo.files beyond the executable) are also copied, each placed at the same location relative to the executable that it has in the source tree.

When a value is a non-executable target that produces more than one default output, the path key is treated as a directory and the outputs are placed inside it according to multi_file_layout.
Dictionary: String -> Labeloptional{}
annotationsAnnotations to add to the layer metadata as key-value pairs.Dictionary: String -> Stringoptional{}
annotations_fileFile containing annotations for the layer, as JSON or newline-delimited text.

The file is parsed in one of the following formats, auto-detected from its contents:

- JSON object with string values: {"key": "value"} - JSON object with list values: {"key": ["value1", "value2"]} (the last value wins) - JSON array of KEY=VALUE strings: ["key=value"] - newline-delimited KEY=VALUE text (one per line; blank lines and # comments are ignored)

Values in JSON objects are used verbatim, so they can encode arbitrary strings including values that contain =, spaces, or newlines. The KEY=VALUE forms (JSON array and text) split on the first = and trim surrounding whitespace from the key and value.

Annotations from this file are merged with annotations specified via the annotations attribute, which take precedence for matching keys.

Example file content:
version=1.0.0
build.date=2024-01-15
source.url=https://github.com/...
LabeloptionalNone
compressCompression algorithm to use. If set to 'auto', uses the global default compression setting.Stringoptional"auto"
create_parent_directoriesWhether to automatically create parent directory entries in the tar file for all files. If set to 'auto', uses the global default create_parent_directories setting. When enabled, parent directories will be created automatically for all files in the layer.Stringoptional"auto"
default_metadataJSON-encoded default metadata to apply to all files in the layer. Can include fields like mode, uid, gid, uname, gname, mtime, and pax_records.Stringoptional""
estargzWhether 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.Stringoptional"auto"
file_metadataPer-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.Dictionary: String -> Stringoptional{}
include_runfilesWhether to include runfiles for executable targets. When True (default), executables in srcs will include their runfiles tree. When False, only the executable file itself is included, without runfiles.

Either way, any additional default outputs of the target (the rest of DefaultInfo.files beyond the executable) are copied into the layer, placed relative to the executable.
BooleanoptionalTrue
media_typeOverride the layer media type. By default, the media type is auto-detected from the compression algorithm.Stringoptional""
multi_file_layoutHow to place a non-executable src that produces MORE THAN ONE default output.

- "package_relative" (default): treat the path key as a directory and place each file inside it, preserving its path relative to the producing target's package. - "flatten": place each file directly in the directory by basename (restores the older behavior).

A src that produces a single output is always placed exactly at its path key, regardless of this setting.
Stringoptional"package_relative"
symlinksSymlinks to create in the layer. Keys are symlink paths in the image, values are the targets they point to.Dictionary: String -> Stringoptional{}
tree_artifact_handlingHow to handle duplicate tree artifacts (directories) in the layer. If set to 'full', each tree artifact is stored at its intended path (no deduplication). If set to 'deduplicate_symlink', duplicate tree artifacts are replaced with symlinks to the first occurrence. If set to 'auto', uses the global default from --@rules_img//img/settings:layer_tree_artifact_handling.Stringoptional"auto"

layer_from_binary

load("@rules_img//img:layer.bzl", "layer_from_binary")

layer_from_binary(name, annotations, annotations_file, binary, compress, create_parent_directories,
                  estargz, include_runfiles, layer_budget, media_type, path, runfiles_path,
                  runfiles_shared_path, runfiles_sharing_mode, tree_artifact_handling)

Creates a container image layer from a *_binary target.

This rule packages a binary executable and its runfiles into a layer, and additionally provides image configuration (entrypoint, cmd, env, working_dir) via ImageLayerConfigInfo. When used as a layer in image_manifest, the configuration is automatically applied to the image with Dockerfile-like semantics.

The binary's args attribute becomes the image cmd, its env attribute (or RunEnvironmentInfo provider) becomes env, and the binary path becomes the entrypoint. When include_runfiles is True (default), the working directory is set to the runfiles root.

In addition to the executable and its runfiles, any other default outputs of the binary target (the rest of DefaultInfo.files) are copied into the layer, each placed at the same location relative to the executable that it has in the source tree.

If the binary provides RunfilesGroupInfo (from rules_runfiles_group), the runfiles are split into separate layers based on the groups. This allows for better caching: stable layers (interpreter, stdlib) change infrequently and can be shared, while the application code layer changes with each build. The resolution protocol respects RunfilesGroupTransformInfo and RunfilesGroupMetadataInfo from the binary's aspect_hints.

When the number of groups exceeds what is practical for a container image, use layer_budget to merge groups down to a maximum count. The merge algorithm respects group rank (only merges within the same rank), do_not_merge flags, and weight hints (lighter groups merge first).

Example:

load("@rules_img//img:layer.bzl", "layer_from_binary")
load("@rules_img//img:image.bzl", "image_manifest")

# Package a Go binary with its runfiles
layer_from_binary(
    name = "app_layer",
    binary = "//cmd/server",
)

# Use in an image - entrypoint, cmd, env, and working_dir are set automatically
image_manifest(
    name = "image",
    base = "@distroless_base",
    layers = [":app_layer"],
)

# Override the path inside the image
layer_from_binary(
    name = "custom_path_layer",
    binary = "//cmd/server",
    path = "/usr/local/bin/",
)

# Without runfiles (static binary)
layer_from_binary(
    name = "static_layer",
    binary = "//cmd/server",
    path = "/usr/local/bin/server",
    include_runfiles = False,
)

Output groups

  • mtree: one mtree text file per produced layer

ATTRIBUTES

NameDescriptionTypeMandatoryDefault
nameA unique name for this target.Namerequired
annotationsAnnotations to add to the layer metadata as key-value pairs.Dictionary: String -> Stringoptional{}
annotations_fileFile containing annotations for the layer, as JSON or newline-delimited text.

The file is parsed in one of the following formats, auto-detected from its contents:

- JSON object with string values: {"key": "value"} - JSON object with list values: {"key": ["value1", "value2"]} (the last value wins) - JSON array of KEY=VALUE strings: ["key=value"] - newline-delimited KEY=VALUE text (one per line; blank lines and # comments are ignored)

Values in JSON objects are used verbatim, so they can encode arbitrary strings including values that contain =, spaces, or newlines. The KEY=VALUE forms (JSON array and text) split on the first = and trim surrounding whitespace from the key and value.

Annotations from this file are merged with annotations specified via the annotations attribute, which take precedence for matching keys.

Example file content:
version=1.0.0
build.date=2024-01-15
source.url=https://github.com/...
LabeloptionalNone
binaryThe *_binary target to package into the layer.

The binary's args and env attributes are extracted and provided as image configuration (cmd and env) via ImageLayerConfigInfo. The data attribute is used for $(location) expansion in args and env values.

If the binary provides RunfilesGroupInfo, the runfiles are split into separate layers per group.
Labelrequired
compressCompression algorithm to use. If set to 'auto', uses the global default compression setting.Stringoptional"auto"
create_parent_directoriesWhether to automatically create parent directory entries in the tar file for all files. If set to 'auto', uses the global default create_parent_directories setting. When enabled, parent directories will be created automatically for all files in the layer.Stringoptional"auto"
estargzWhether 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.Stringoptional"auto"
include_runfilesWhether to include runfiles for executable targets. When True (default), executables in srcs will include their runfiles tree. When False, only the executable file itself is included, without runfiles.

Either way, any additional default outputs of the target (the rest of DefaultInfo.files beyond the executable) are copied into the layer, placed relative to the executable.
BooleanoptionalTrue
layer_budgetMaximum total number of layers produced by this rule. If set to a value > 0 and the binary provides RunfilesGroupInfo, groups are merged using the merge algorithm from rules_runfiles_group. The algorithm respects group rank (only merges within the same rank), do_not_merge flags, and weight hints (lighter groups merge first).

When a group is marked as executable_group in RunfilesGroupMetadataInfo, the binary executable and supporting files are merged into that group's layer, and the full budget is available for runfiles groups. When no executable_group exists, one layer is reserved for a separate binary layer, and the remaining budget (layer_budget - 1) is used for groups; layer_budget=1 without an executable_group skips the grouped path entirely.

0 means no limit (all groups become separate layers, plus a binary layer unless an executable_group absorbs it).
Integeroptional0
media_typeOverride the layer media type. By default, the media type is auto-detected from the compression algorithm.Stringoptional""
pathOptional path of the binary inside the image. If the path ends with a slash ("/"), the basename of the binary will be automatically appended. If unset, this defaults to the rlocationpath of the binary (e.g., "main/cmd/server/server/server").Stringoptional""
runfiles_pathOptional path of the runfiles directory of the binary inside the image. If unset, this defaults to the path of the binary with a .runfiles suffix (e.g., "main/cmd/server/server/server.runfiles"). Note: depending on the runfiles_sharing_mode, this may be a symlink to a shared runfiles directory.Stringoptional""
runfiles_shared_pathOptional path of the shared runfiles directory inside the image. This is only used when runfiles sharing is enabled and has a global default.Stringoptional""
runfiles_sharing_modeHow to process runfiles. Runfiles can either be placed next to the executable (in a directory with a .runfiles suffix, the runfiles_path attribute), or placed in a shared runfiles path. When sharing runfiles, there will be symlink added: {runfiles_path} -> {runfiles_shared_path}.

Possible settings:

* "auto": Share runfiles based on the global default and based on the presence of RunfilesGroupInfo. Globally, runfiles sharing can be set to "shared", "private", or "auto", where auto shares runfiles if RunfilesGroupInfo is provided. * "shared": Always share runfiles. * "private": Never share runfiles
Stringoptional"auto"
tree_artifact_handlingHow to handle duplicate tree artifacts (directories) in the layer. If set to 'full', each tree artifact is stored at its intended path (no deduplication). If set to 'deduplicate_symlink', duplicate tree artifacts are replaced with symlinks to the first occurrence. If set to 'auto', uses the global default from --@rules_img//img/settings:layer_tree_artifact_handling.Stringoptional"auto"

layer_from_file

load("@rules_img//img:layer.bzl", "layer_from_file")

layer_from_file(name, src, annotations, base_name_annotations, diff_id, diff_id_annotations,
                media_type)

Creates a container image layer from an arbitrary file.

This rule uses any file as a container image layer blob, computing the necessary metadata (digest, size) without any tar-specific processing like compression or optimization.

This is useful for non-tar layer content such as Helm charts, WASM modules, or other OCI artifacts.

If you want to use an existing tar file as a layer, use layer_from_tar instead.

Example:

load("@rules_img//img:layer.bzl", "layer_from_file")

# Use an arbitrary artifact as a layer
layer_from_file(
    name = "artifact_layer",
    src = ":artifact.bin",
    media_type = "application/octet-stream",
    annotations = {
        "org.opencontainers.image.title": "artifact.bin",
    },
)

ATTRIBUTES

NameDescriptionTypeMandatoryDefault
nameA unique name for this target.Namerequired
srcThe file to use as a layer blob.Labelrequired
annotationsAnnotations to add to the layer metadata as key-value pairs.Dictionary: String -> Stringoptional{}
base_name_annotationsList of annotations that are set to the basename of the file.List of stringsoptional[]
diff_idIf set, interprets the file as a (potentially compressed) tar file and calculates the diff_id. Warning: if you do this, you probably want to use layer_from_tar instead.BooleanoptionalFalse
diff_id_annotationsList of annotations that are set to the diff_id of the file. Only works with tar files.List of stringsoptional[]
media_typeLayer media type. Defaults to "application/vnd.oci.image.layer.v1.tar" if not set.Stringoptional"application/vnd.oci.image.layer.v1.tar"

layer_from_tar

load("@rules_img//img:layer.bzl", "layer_from_tar")

layer_from_tar(name, src, annotations, compress, estargz, media_type, optimize)

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",
    },
)

Output groups

  • mtree: a single mtree text file

ATTRIBUTES

NameDescriptionTypeMandatoryDefault
nameA unique name for this target.Namerequired
srcThe tar file to convert into a layer. Must be a valid tar file (optionally compressed).Labelrequired
annotationsAnnotations to add to the layer metadata as key-value pairs.Dictionary: String -> Stringoptional{}
compressCompression algorithm to use. If set to 'auto', it keeps the existing compression, unless the layer is being optimized.Stringoptional"auto"
estargzWhether 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.Stringoptional"auto"
media_typeOverride layer media type. Use e.g. "application/vnd.cncf.helm.chart.content.v1.tar" for Helm charts.Stringoptional""
optimizeIf 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.BooleanoptionalFalse

file_metadata

load("@rules_img//img:layer.bzl", "file_metadata")

file_metadata(*, mode, uid, gid, uname, gname, mtime, pax_records)

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

NameDescriptionDefault Value
modeFile permission mode (e.g., "0755", "0644"). String format.None
uidUser ID of the file owner. Integer.None
gidGroup ID of the file owner. Integer.None
unameUser name of the file owner. String.None
gnameGroup name of the file owner. String.None
mtimeModification time in RFC3339 format (e.g., "2023-01-01T00:00:00Z"). String.None
pax_recordsDict of extended attributes to set via PAX records.None

RETURNS

JSON-encoded string containing the file metadata.