> ## Documentation Index
> Fetch the complete documentation index at: https://docs.reasonos.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Container Building

> Build OCI-compliant container images directly in RBS — no Docker daemon required.

# Container Building (OCI SDK)

RBS includes a built-in **OCI SDK** for building container images as part of your build process. No Docker daemon or Dockerfile is required — images are assembled programmatically using RBS DSL primitives.

## Why RBS for Containers?

| Feature               | RBS OCI        | Docker Build  | Buildpacks    |
| --------------------- | -------------- | ------------- | ------------- |
| **Daemon Required**   | No             | Yes           | Yes           |
| **Reproducible**      | Yes (hermetic) | Partial       | Partial       |
| **Layer Control**     | Full           | Limited       | None          |
| **Build Integration** | Native         | Separate tool | Separate tool |
| **Base Image Pull**   | Built-in       | Built-in      | Built-in      |

## Quick Start

### Using High-Level Rules

The simplest way to build container images:

```python filename="BUILD.rbs" theme={null}
load("@rbs//java/rules.rbs", "java_binary")
load("@rbs//oci/rules.rbs", "oci_image")

# Build your application
java_binary(
    name = "server",
    srcs = glob(["src/**/*.java"]),
    main = "com.example.Server",
    deps = ["@external://spring_boot_web"],
)

# Package it as a container image
oci_image(
    name = "server_image",
    binary = ":server",
    base = "eclipse-temurin:17-jre",
    tag = "my-app:latest",
    ports = [8080],
    env = {"SPRING_PROFILES_ACTIVE": "production"},
)
```

```bash theme={null}
# Build the image
rbs build :server_image

# The image tar is saved to .rbs/bin/{platform}/server_image/image.tar
# Load it into Docker:
docker load < .rbs/bin/darwin-arm64/server_image/image.tar
```

## OCI SDK Primitives

For advanced use cases, the OCI SDK provides low-level primitives accessible via `ctx.oci` within custom rules.

### `ctx.oci.create_layer()`

Creates a single OCI layer from files.

```python theme={null}
def _my_image_impl(ctx):
    # Create a layer with application files
    app_layer = ctx.oci.create_layer(
        name = "app",
        files = {
            "/app/server.jar": ctx.bin.output + "/server.jar",   # dest: source
            "/app/config.yaml": "config/production.yaml",
        },
        directory = "/app",             # Set working directory
        entrypoint = ["java", "-jar", "/app/server.jar"],
    )
    
    # Create a layer with static assets
    assets_layer = ctx.oci.create_layer(
        name = "assets",
        files = {
            "/app/static/": "frontend/dist/",   # Copy entire directory
        },
    )
```

### Layer Parameters

| Parameter    | Type   | Description                                       |
| ------------ | ------ | ------------------------------------------------- |
| `name`       | string | Unique name for the layer.                        |
| `files`      | dict   | Mapping of `destination_path: source_path`.       |
| `tar`        | string | Path to an existing tar file to use as the layer. |
| `directory`  | string | Sets the working directory (`WORKDIR`).           |
| `entrypoint` | list   | Sets the container entrypoint.                    |
| `cmd`        | list   | Sets the container CMD (default arguments).       |
| `env`        | dict   | Environment variables to set.                     |
| `ports`      | list   | Ports to expose.                                  |
| `user`       | string | User/group to run as (e.g., `"1000:1000"`).       |
| `labels`     | dict   | OCI image labels.                                 |

### `ctx.oci.pull()`

Pulls a base image from a registry.

```python theme={null}
def _my_image_impl(ctx):
    # Pull a base image
    base = ctx.oci.pull(
        image = "ubuntu:22.04",
        platform = "linux/amd64",         # Optional: specify platform
    )
    
    # Pull from a private registry
    private_base = ctx.oci.pull(
        image = "registry.example.com/base:latest",
        credentials = {
            "username": "env:REGISTRY_USER",
            "password": "env:REGISTRY_PASS",
        },
    )
```

### `ctx.oci.write_docker_tar()`

Assembles layers into a Docker-compatible image tarball.

```python theme={null}
def _my_image_impl(ctx):
    base = ctx.oci.pull(image = "ubuntu:22.04")
    
    app_layer = ctx.oci.create_layer(
        name = "app",
        files = {"/app/main": ctx.bin.executable},
        entrypoint = ["/app/main"],
    )
    
    # Write the final image
    ctx.oci.write_docker_tar(
        output = ctx.bin.output + "/image.tar",
        base = base,
        layers = [app_layer],
        tag = "my-app:latest",
        config = {
            "Env": ["APP_ENV=production"],
            "ExposedPorts": {"8080/tcp": {}},
            "WorkingDir": "/app",
        },
    )
```

### `ctx.oci.image_builder()`

A fluent builder API for creating images step by step.

```python theme={null}
def _my_image_impl(ctx):
    builder = ctx.oci.image_builder(base = "node:20-slim")
    
    # Add layers fluently
    builder.add_layer(
        name = "deps",
        files = {"/app/node_modules/": ctx.bin.run_files + "/node_modules/"},
    )
    
    builder.add_layer(
        name = "app",
        files = {"/app/": ctx.bin.run_files + "/src/"},
        directory = "/app",
        entrypoint = ["node", "index.js"],
    )
    
    builder.set_env({"NODE_ENV": "production"})
    builder.expose_ports([3000])
    builder.set_user("1000:1000")
    
    # Build and write
    builder.write(
        output = ctx.bin.output + "/image.tar",
        tag = "my-node-app:latest",
    )
```

## Complete Examples

### Java Spring Boot Container

```python filename="BUILD.rbs" theme={null}
load("@rbs//java/rules.rbs", "java_binary")
load("@rbs//oci/rules.rbs", "oci_image")

java_binary(
    name = "api_server",
    srcs = glob(["src/**/*.java"]),
    main = "com.example.api.Application",
    deps = [
        "@external://spring_boot_web",
        "@external://spring_boot_actuator",
    ],
    javaopts = ["-Xmx512m"],
)

oci_image(
    name = "api_image",
    binary = ":api_server",
    base = "eclipse-temurin:17-jre-alpine",
    tag = "api-server:latest",
    ports = [8080, 8081],
    env = {
        "SPRING_PROFILES_ACTIVE": "production",
        "JAVA_OPTS": "-Xmx512m -XX:+UseG1GC",
    },
    labels = {
        "org.opencontainers.image.title": "API Server",
        "org.opencontainers.image.version": "1.0.0",
    },
)
```

### Node.js Express Container

```python filename="BUILD.rbs" theme={null}
load("@rbs//nodejs/rules.rbs", "nodejs_binary")
load("@rbs//oci/rules.rbs", "oci_image")

nodejs_binary(
    name = "web_app",
    srcs = glob(["src/**/*.js"]),
    main = "src/index.js",
    deps = [
        "@external://express_repo",
        "@external://cors_repo",
    ],
)

oci_image(
    name = "web_image",
    binary = ":web_app",
    base = "node:20-slim",
    tag = "web-app:latest",
    ports = [3000],
    env = {"NODE_ENV": "production"},
)
```

### Python Django Container

```python filename="BUILD.rbs" theme={null}
load("@rbs//python/rules.rbs", "py_binary")
load("@rbs//oci/rules.rbs", "oci_image")

py_binary(
    name = "django_app",
    srcs = glob(["**/*.py"]),
    main = "manage.py",
    deps = [
        "@external://django_repo",
        "@external://gunicorn_repo",
    ],
    args = ["runserver", "0.0.0.0:8000"],
)

oci_image(
    name = "django_image",
    binary = ":django_app",
    base = "python:3.11-slim",
    tag = "django-app:latest",
    ports = [8000],
    env = {
        "DJANGO_SETTINGS_MODULE": "myapp.settings.production",
        "PYTHONDONTWRITEBYTECODE": "1",
    },
)
```

### Multi-Stage Build (Custom Rule)

For maximum control over the image layout:

```python filename="rules/docker.rbs" theme={null}
load("@native//:defs", "native")

def _optimized_image_impl(ctx):
    dirs = ctx.bin.create_dirs()
    
    # Pull base image
    base = ctx.oci.pull(image = ctx.attr.base)
    
    # Layer 1: Runtime dependencies (rarely changes — cached efficiently)
    deps_layer = ctx.oci.create_layer(
        name = "dependencies",
        files = {"/app/deps/": dirs.run_files + "/deps/"},
    )
    
    # Layer 2: Application code (changes frequently)
    app_layer = ctx.oci.create_layer(
        name = "application",
        files = {"/app/": dirs.run_files + "/src/"},
        directory = "/app",
        entrypoint = ctx.attr.entrypoint,
        env = ctx.attr.env,
        ports = ctx.attr.ports,
        user = "1000:1000",
    )
    
    # Write the image
    ctx.oci.write_docker_tar(
        output = dirs.output + "/image.tar",
        base = base,
        layers = [deps_layer, app_layer],
        tag = ctx.attr.tag,
    )

native.define_rule(
    name = "optimized_image",
    kind = "container",
    implementation = _optimized_image_impl,
    attrs = {
        "binary": attr.label(is_dep = True, mandatory = True),
        "base": attr.string(default = "ubuntu:22.04"),
        "tag": attr.string(mandatory = True),
        "entrypoint": attr.string_list(default = []),
        "env": attr.string_dict(default = {}),
        "ports": attr.list(default = []),
    },
)
```

## Image Optimization Tips

<AccordionGroup>
  <Accordion title="Layer Ordering">
    Place rarely-changing content (OS packages, runtime dependencies) in early layers and frequently-changing content (application code) in later layers. This maximizes layer cache reuse.
  </Accordion>

  <Accordion title="Minimal Base Images">
    Use slim or distroless base images to reduce image size and attack surface:

    * `eclipse-temurin:17-jre-alpine` instead of `eclipse-temurin:17-jdk`
    * `node:20-slim` instead of `node:20`
    * `python:3.11-slim` instead of `python:3.11`
  </Accordion>

  <Accordion title="Multi-Platform Images">
    Build images for multiple platforms by specifying the target platform:

    ```python theme={null}
    oci_image(
        name = "multi_arch_image",
        binary = ":server",
        base = "ubuntu:22.04",
        platforms = ["linux/amd64", "linux/arm64"],
    )
    ```
  </Accordion>

  <Accordion title="Non-Root User">
    Always run containers as a non-root user:

    ```python theme={null}
    app_layer = ctx.oci.create_layer(
        name = "app",
        files = {...},
        user = "1000:1000",
    )
    ```
  </Accordion>
</AccordionGroup>

## Output

Built images are saved as Docker-compatible tarballs:

```
.rbs/bin/{platform}/{package}/{target_name}/
└── image.tar       # Docker-compatible tar file
```

### Loading into Docker

```bash theme={null}
docker load < .rbs/bin/darwin-arm64/server_image/image.tar
docker run -p 8080:8080 my-app:latest
```

### Pushing to a Registry

```bash theme={null}
docker load < .rbs/bin/darwin-arm64/server_image/image.tar
docker tag my-app:latest registry.example.com/my-app:latest
docker push registry.example.com/my-app:latest
```
