rules_img supports multiple push strategies optimized for different scenarios. Each strategy offers unique trade-offs between performance, infrastructure requirements, and use cases.
The eager push strategy is the traditional approach where all image layers are downloaded to the machine running Bazel and then uploaded to the target registry. This is similar to how most container build tools work, including rules_oci.
- Downloads all required blobs (layers, configs, manifests) to local machine
- Uploads all blobs to the target registry
- Writes the manifest to the registry
- ✅ Simple and straightforward
- ✅ Works with any standard container registry
- ✅ No special infrastructure required (works without remote cache)
- ✅ Predictable behavior
- ❌ Requires downloading all layers locally
- ❌ Uses significant bandwidth for large images
- ❌ Slower for images with many or large layers
- ❌ Not optimized for remote execution
# Enable eager push strategy (this is the default)
$ bazel run //your:push_target --@rules_img//img/settings:push_strategy=eager
# Or set in .bazelrc
common --@rules_img//img/settings:push_strategy=eagerNo additional infrastructure setup required.
The lazy push strategy optimizes uploads by checking the registry first and only uploading missing blobs. It streams blobs directly from Bazel's remote cache when needed, avoiding unnecessary downloads to the local machine.
- Downloads only image metadata to machine running Bazel
- Streams missing blobs from Bazel's remote cache to the registry
- Writes the manifest to the registry
- ✅ Work with huge container images without sacrificing local disk space
- ✅ Works with standard registries
- ✅ Supports Build without the Bytes
- ❌ Requires a Bazel remote cache
- ❌ Slightly more complex than eager push
- Ensure you have a Bazel remote cache configured:
# Example remote cache configuration.
# This also works with --remote_executor
build --remote_cache=grpc://your-cache-server:9092- Enable lazy push strategy:
# In .bazelrc
common --@rules_img//img/settings:push_strategy=lazy
# Optionally, configure remote cache and credential helper via rules_img settings
# instead of environment variables:
common --@rules_img//img/settings:remote_cache=grpc://your-cache-server:9092
common --@rules_img//img/settings:credential_helper=tweag-credential-helper
# If your remote cache requires a remote instance name, set it here:
common --@rules_img//img/settings:remote_instance_name=my-instance-name- Run your push target:
# Configure the push utility via environment variables:
export IMG_REAPI_ENDPOINT=grpc://your-cache-server:9092
export IMG_CREDENTIAL_HELPER=tweag-credential-helper
# Set the remote instance name if required by your RBE backend:
export IMG_REAPI_INSTANCE_NAME=my-instance-name
bazel run //your:push_target
# Or use the settings flags (if configured above):
bazel run //your:push_targetThe CAS (Content Addressable Storage) registry push strategy uses a special container registry that is directly integrated with Bazel's remote cache. This eliminates data duplication and provides the fastest possible push performance. Please note that the remote cache may evict cached data at any time, as per the specification. For that reason, using a remote cache as the backend of your container registry is only recommended during development. Also note that the regsitry doesn't offer TLS nor authentication, so it should only listen on localhost, or be protected by a VPN or other gateway.
- The special registry reads blobs directly from Bazel's CAS
- No blob transfer needed - registry and cache share storage
- Only metadata (manifests) need to be written
- Registry serves blobs on-demand from CAS
- ✅ Fastest push performance possible
- ✅ Zero data duplication
- ✅ Minimal bandwidth usage
- ✅ Perfect for development workflows
- ✅ Ideal for CI pipelines where images are tested shortly after a build
- ❌ Requires special registry implementation
- ❌ More complex infrastructure setup
- ❌ Registry must have access to CAS
- Deploy the CAS-integrated registry:
# Build the registry
bazel build @rules_img_tool//cmd/registry
# Start registry server
bazel-bin/external/rules_img_tool+/cmd/registry/registry_/registry \
--reapi-endpoint grpc://your-cas-server:9092 \
--credential-helper tweag-credential-helper \
--address localhost \
--port 80 \
--grpc-port 4444 \
--enable-blobcache \
--blob-store reapi- Configure Bazel to use CAS registry push:
# In .bazelrc
common --@rules_img//img/settings:push_strategy=cas_registry
# This also works with --remote_executor
build --remote_cache=grpc://your-cache-server:9092
# Optionally, configure credential helper via rules_img settings:
common --@rules_img//img/settings:credential_helper=tweag-credential-helper- Push to your CAS registry:
export IMG_BLOB_CACHE_ENDPOINT=grpc://localhost:4444
bazel run //your:push_targetThe registry can use multiple blob backends, including a remote cache (reapi, default), another container registry (upstream), and an S3 bucket (s3). Those backends are experimental.
The BES (Build Event Service) push strategy performs image pushes as a side-effect of Bazel's build event uploads. This is the most sophisticated strategy, designed for large organizations with thousands of builds per day. Note that the BES service doesn't offer TLS nor authentication, so it should only listen on localhost, or be protected by a VPN or other gateway.
- Bazel uploads build events to BES as normal
- BES backend detects image push events
- Images are assembled and pushed asynchronously
- No client-side push needed
- ✅ Zero client-side overhead
- ✅ Pushes happen asynchronously
- ✅ Extremely scalable
- ✅ Perfect for large organizations
- ✅ Centralized push management
- ❌ Requires custom BES backend
- ❌ Most complex setup
- ❌ Requires significant infrastructure
- Deploy the BES backend with image push support:
# Build the BES server
bazel build @rules_img_tool//cmd/bes
# Run with CAS backend
bazel-bin/external/rules_img_tool+/cmd/bes/bes_/bes \
--address localhost \
--port 8080 \
--cas-endpoint grpc://your-cas-server:9092 \
--credential-helper tweag-credential-helper- Configure Bazel to use your BES:
# In .bazelrc
build --bes_backend=grpc://localhost:8080
common --@rules_img//img/settings:push_strategy=bes- Build your targets normally - pushes happen automatically:
# Just build - no need to run push targets!
bazel build //your:image_target| Use Case | Recommended Strategy | Why |
|---|---|---|
| Local development | CAS Registry | Fast iteration, minimal bandwidth |
| Small team CI/CD | Lazy | Good performance, simple setup |
| Large organization | BES | Maximum scalability, centralized control |
| Simple deployments | Eager | No infrastructure requirements |
| Air-gapped environments | Eager | Works without external dependencies |