oci_image rule
packages a built binary (plus any extra files) into a Docker-compatible tar you can
docker load or podman load, and oci_push uploads it to a registry. Building
stays pure — the push is a deliberate side effect that only happens at rbs run.
The rules live in one module:
A service image, end to end
The typical shape: cross-compile the binary for the cluster’s platform, package it on a minimal base, and declare the push target next to it..rbs/bin/<platform>/<package>/<name>/<name>.tar and loads straight into a local
daemon:
oci_image
Creates an OCI image from a binary target and optional extra files. The packaged binary’s entire output directory (launcher,runfiles/, toolchain links) is
copied into the image at /app/<binary>/, with build caches excluded, so the
binary runs the same way in the container as it does under rbs run.
The image platform is checked against your binary
oci_image reads the packaged binary’s own executable header to decide (and
verify) the image’s os/architecture stamp. Packaging a macOS binary into an
image stamped linux is a build error, not a surprise at pod-scheduling time
— cross-compile the binary for the image platform instead (for Go, that is
go_binary’s goos/goarch attributes).
Working with scratch and other minimal bases
Two things bite everyone once, so they are worth knowing up front:
scratchhas no shell. The default entrypoint is the binary’s launcher script, which needs one. Onscratch, pointentrypointat the real executable inside the packaged directory:/app/<binary>/runfiles/_main/<binary>.bin.- Your image config replaces the base image’s config wholesale — the base
contributes layers only. If the base relied on its config (a
PATH, an entrypoint, default env), re-declare what you need inenv/entrypoint. A base likealpine/gitwon’t findgitat runtime unless you setPATHyourself.
scratch also carries no CA certificates: a service that makes outbound TLS
connections needs a base that ships them (or a mounted certificate bundle).
Base images are cached user-globally
Base images referenced bybase are pulled from the registry once and cached in
the user-global cache (~/.cache/rbs/oci-images, or under $RBS_CACHE_DIR when
set) — shared by every workspace on the machine. Layer content is
checksum-verified both when fetched and when loaded from cache, and the cache is
swept by the same age bounds as everything else under
rbs cache gc.
Referencing a locally built tar as a base (
base = "@some_target") is not
supported yet — base must be "scratch" or a registry reference.oci_tarball
Packages a plain directory (rather than a binary target) into a Docker-compatible image tar. Useful for static assets or prepared filesystem trees.Pushing to a registry with oci_push
oci_push declares where an image goes; the push itself happens only when you
rbs run the target. There is no rbs push subcommand — pushing is a run-time
side effect, keeping rbs build pure and repeatable.
Either
repository or uses must resolve to a repository — a push target with
neither fails with a clear error.
Credentials
The push authenticates from environment variables in the pushing shell:RBS_OCI_USERNAME=AWS with a password from
aws ecr get-login-password. Local registries on localhost need no
credentials.
Resolving the repository from infra
When your registry is provisioned through rbs infrastructure, skip the hardcodedrepository and let
the push target read it from applied state:
rbs infra apply creates (or confirms) the registry, the push target
resolves its repository URL from the resource’s exported REGISTRY_URL — a new
service’s push target needs no editing when registries move. Kubernetes deploy
targets resolve image references through the same path, so what you push and
what you deploy can never disagree — see
Deploying to Kubernetes.