rules_docker_compose
Bazel rules for running integration tests which use Docker-Compose to setup networked infrastructure.
Setup
bazel_dep(name = "rules_docker_compose", version = "{version}")
docker_compose = use_extension("@rules_docker_compose//docker_compose:extensions.bzl", "docker_compose")
docker_compose.toolchain(
name = "docker_compose_toolchains",
version = "2.24.0",
)
use_repo(docker_compose, "docker_compose_toolchains")
register_toolchains(
"@docker_compose_toolchains//:all",
)
Overview
rules_docker_compose provides Bazel rules for integrating Docker-Compose into your build and test workflows. These rules enable you to:
- Merge and validate docker-compose configurations using the
docker_compose_yamlrule, which combines multiple YAML files and ensures all referenced images have corresponding loader targets - Run integration tests with the
docker_compose_testrule, which automatically starts services, waits for them to be ready, runs your test binary, and cleans up containers - Verify image integrity through automatically generated lock files that map image tags to content digests, ensuring the correct images are loaded at runtime
The rules integrate seamlessly with rules_oci and rules_img, allowing you to use container images built with Bazel in your docker-compose configurations. They handle the complete lifecycle of docker-compose services during testing, including image loading, service startup, health checking, and cleanup.
Why not to use these rules
These rules are designed for integration testing scenarios where you need to test against real networked services. However, they come with trade-offs that may not be suitable for all use cases:
-
Requires network access: Tests must be marked with
requires-networkand cannot run in fully sandboxed environments. This means tests may be slower and less reproducible than pure unit tests. -
Docker dependency: Tests require Docker (or compatible container runtime) to be available on the host machine. This adds an external dependency and may not work in all CI/CD environments.
-
Resource tracking limitations: Bazel cannot track all resources created by docker-compose (spawned containers, networks, volumes). It's highly recommended to use resource tags to limit test execution and prevent resource exhaustion.
-
Port collisions: Tests that bind to the same host ports cannot run in parallel. If multiple tests use the same port mappings (e.g.,
"8080:80"), they will conflict when Bazel attempts to run them concurrently. Use unique port mappings per test or ensure tests are tagged to run serially. -
Platform-specific behavior: Container behavior may vary across different platforms and Docker versions, potentially affecting test reproducibility.
Consider using these rules when you need to test interactions with real services, but prefer lighter-weight alternatives for fast, hermetic unit tests.
Docker Compose
Rules
docker_compose_test
load("@rules_docker_compose//docker_compose:defs.bzl", "docker_compose_test")
docker_compose_test(name, data, delay, env, env_inherit, images, test, test_args, yamls)
Runs a test with docker-compose services.
This rule starts docker-compose services, waits for them to be ready, runs a test binary, and then tears down the services. The test lifecycle is:
- Loading container images into Docker using the specified loader targets
- Starting services with
docker-compose up - Waiting for containers to be running (with health checks)
- Verifying image digests match the expected values from the lock file
- Running the test binary with optional arguments
- Cleaning up with
docker-compose down
The test requires network access and Docker to be available on the host.
Supported image loader types:
| Loader | Source |
|---|---|
| oci_load | rules_oci |
| image_load | rules_img |
Example:
load("@rules_docker_compose//docker_compose:docker_compose_test.bzl", "docker_compose_test")
load("@rules_oci//oci:defs.bzl", "oci_load")
oci_load(
name = "my_service.load",
image = ":my_service",
repo_tags = ["my-service:latest"],
)
docker_compose_test(
name = "integration_test",
yamls = ["docker-compose.yaml"],
images = [":my_service.load"],
test = ":my_test_binary",
test_args = ["-host", "localhost:8080"],
delay = 2, # Wait 2 seconds after containers start
)
ATTRIBUTES
| Name | Description | Type | Mandatory | Default |
|---|---|---|---|---|
| name | A unique name for this target. | Name | required | |
| data | Additional runtime dependencies for the test. | List of labels | optional | [] |
| delay | Seconds to wait after containers are running before executing the test. Useful for services that need initialization time beyond their health checks. | Integer | optional | 1 |
| env | Dictionary of strings; values are subject to $(location) and "Make variable" substitution | Dictionary: String -> String | optional | {} |
| env_inherit | Specifies additional environment variables to inherit from the external environment when the test is executed by bazel test. | List of strings | optional | [] |
| images | Image loader targets that provide the container images for the docker-compose services. Each image will be loaded into Docker before docker-compose up is called. See the rule documentation for supported loader types. | List of labels | optional | [] |
| test | The test binary to execute after containers are running. The binary will receive arguments from test_args. | Label | optional | None |
| test_args | Arguments to pass to the test binary. | List of strings | optional | [] |
| yamls | One or more docker-compose YAML files defining the services to run. Files are merged using docker-compose config. | List of labels | optional | [] |
docker_compose_toolchain
load("@rules_docker_compose//docker_compose:defs.bzl", "docker_compose_toolchain")
docker_compose_toolchain(name, digest_mode, docker_compose, version)
A toolchain for providing Docker-Compose to Bazel rules.
The digest_mode attribute controls how image digests are computed for lock files:
| Mode | Description |
|---|---|
oci | Uses the OCI manifest digest directly from the image's index.json. Use this when images are pushed to an OCI-compliant registry. |
docker-legacy | Uses the config blob digest from the OCI manifest. This is the image ID that Docker reports after docker load when using legacy storage (without containerd). Use this for Linux CI runners like GitHub Actions. |
docker-containerd | Converts the OCI manifest to Docker V2 Schema 2 format and computes the manifest digest. This is the image ID that Docker reports after docker load when using containerd storage. Use this for Docker Desktop or Docker Engine with containerd enabled. |
See the OCI Image Manifest spec and the Docker V2 Schema 2 spec.
Example:
load("@rules_docker_compose//docker_compose:docker_compose_toolchain.bzl", "docker_compose_toolchain")
filegroup(
name = "docker_compose_bin",
srcs = ["docker_compose/docker_compose.exe"],
# Note that additional runfiles associated with a hermetic archive
# of docker_compose should be associated with the target passed to the
# `docker_compose` attribute.
data = glob(["docker_compose/**"]),
)
docker_compose_toolchain(
name = "docker_compose_toolchain",
docker_compose = ":docker_compose_bin",
visibility = ["//visibility:public"],
)
For users looking to use a system install of Docker-Compose, a shell/batch script should be added that points to the system install.
Example or non-hermetic toolchain:
docker_compose.sh
#!/usr/bin/env bash
set -euo pipefail
exec /usr/bin/docker_compose $@
docker_compose.bat
@ECHO OFF
C:\Program Files\Docker\Docker\resources\docker-compose.exe %*
set EXITCODE=%ERRORLEVEL%
exit /b %EXITCODE%
load("@rules_docker_compose//docker_compose:docker_compose_toolchain.bzl", "docker_compose_toolchain")
filegroup(
name = "docker_compose_bin",
srcs = select({
"@platforms//os:windows": ["docker_compose.bat"],
"//conditions:default": ["docker_compose.sh"],
}),
)
docker_compose_toolchain(
name = "docker_compose_toolchain",
docker_compose = ":docker_compose_bin",
visibility = ["//visibility:public"],
)
ATTRIBUTES
| Name | Description | Type | Mandatory | Default |
|---|---|---|---|---|
| name | A unique name for this target. | Name | required | |
| digest_mode | Controls how image digests are computed for lock files. See the rule documentation for details on available modes. Defaults to the value of --@rules_docker_compose//docker_compose/settings:toolchain_default_digest_mode. | String | optional | "" |
| docker_compose | The docker-compose executable. | Label | required | |
| version | The version of docker-compose. | String | required |
docker_compose_yaml
load("@rules_docker_compose//docker_compose:defs.bzl", "docker_compose_yaml")
docker_compose_yaml(name, out, images, yamls)
Merges multiple docker-compose YAML files and validates image references.
This rule uses docker-compose config to merge one or more docker-compose YAML files
into a single, canonical configuration. It also validates that all images referenced
in the configuration have corresponding loader targets specified in the images attribute.
A lock file is generated containing a mapping of image tags to their content digests, which can be used to verify that the correct images are loaded at runtime.
Supported image loader types:
| Loader | Source |
|---|---|
| oci_load | rules_oci |
| image_load | rules_img |
Example:
load("@rules_docker_compose//docker_compose:docker_compose_yaml.bzl", "docker_compose_yaml")
load("@rules_oci//oci:defs.bzl", "oci_load")
oci_load(
name = "my_image.load",
image = ":my_image",
repo_tags = ["my-app:latest"],
)
docker_compose_yaml(
name = "compose",
yamls = ["docker-compose.yaml"],
images = [":my_image.load"],
)
ATTRIBUTES
| Name | Description | Type | Mandatory | Default |
|---|---|---|---|---|
| name | A unique name for this target. | Name | required | |
| out | Optional output filename for the merged docker-compose YAML. Defaults to {name}/docker-compose.yaml. | Label; nonconfigurable | optional | None |
| images | Image loader targets that provide the container images referenced in the YAML files. Each image referenced in the docker-compose YAML must have a corresponding loader target. See the rule documentation for supported loader types. | List of labels | optional | [] |
| yamls | One or more docker-compose YAML files to merge. Files are merged in order using docker-compose config. | List of labels | required |