Files
roc-lang-tools/docs/specs/2026-06-04-roc-oci-images-design.md
T

5.8 KiB

Roc Language OCI Images

Date: 2026-06-04 Status: Design — approved

Overview

Build and publish OCI container images for the Roc language compiler/development environment. Three images (base, dev, ci) defined in a single multi-stage Containerfile, published to a self-hosted Gitea container registry (git.roo.lol).

Image Architecture

debian:bookworm-slim
  └── roc-tools-base      — Roc compiler binary + lib/wasi-libc.a + LICENSE
        ├── roc-tools-dev  — Base + LSP + git + curl + bash + ca-certificates
        └── roc-tools-ci   — Base + ca-certificates (minimal runner)

Repository Structure

roc-lang-tools/
├── Containerfile              # Multi-stage: base / dev / ci targets
├── Makefile                   # Build & push convenience commands
├── scripts/
│   └── download-roc.sh        # Download & extract a specific Roc release tarball
├── docs/
│   └── specs/
│       └── 2026-06-04-roc-oci-images-design.md
└── .gitea/
    └── workflows/
        └── build.yaml         # Gitea Actions CI: build & push on version tags

Image Registry

All images published to git.roo.lol/jbrechtel/ with the following naming:

Image Repository Tags
base git.roo.lol/jbrechtel/roc-tools-base :X.Y.Z, :X.Y.Z-alphaN
dev git.roo.lol/jbrechtel/roc-tools-dev :X.Y.Z, :X.Y.Z-alphaN
ci git.roo.lol/jbrechtel/roc-tools-ci :X.Y.Z, :X.Y.Z-alphaN

Tags mirror Roc release tags (e.g., 0.0.0-alpha2). The :latest tag follows the most recently built version tag.

Build Arguments

The Containerfile accepts two build arguments:

Argument Description Default
ROC_VERSION Roc GitHub release tag to download 0.0.0-alpha2
TARGETARCH Target CPU architecture (set by buildx) amd64

Multi-arch is supported via docker buildx — the download script maps TARGETARCH to Roc tarball names (x86_64 / aarch64).

Image Details

Base (roc-tools-base)

The base image provides just the Roc compiler and its runtime library.

Installed:

  • /opt/roc/bin/roc — Roc compiler binary
  • /opt/roc/lib/wasi-libc.a — WASI libc runtime library (required by some platforms)
  • /opt/roc/LICENSE — Roc license file
  • ca-certificates (build-time only, removed from final layer)
  • roc user/group (UID 1000)
  • PATH includes /opt/roc/bin

Entrypoint: roc

Dev (roc-tools-dev)

Full interactive development environment.

Inherits: Base image

Additional packages: git, curl, bash, ca-certificates

Additional files:

  • /opt/roc/bin/roc_language_server — Roc LSP server (included in the release tarball)

Entrypoint: bash

Use case:

docker run -it --rm -v $(pwd):/work -w /work git.roo.lol/jbrechtel/roc-tools-dev

CI (roc-tools-ci)

Minimal image for CI/CD runner pipelines (Gitea Actions, GitLab CI, etc.).

Inherits: Base image

Additional packages: ca-certificates

Entrypoint: roc

Use case:

- run: docker run --rm git.roo.lol/jbrechtel/roc-tools-ci roc build

Release Download Strategy

The scripts/download-roc.sh script:

  1. Accepts ROC_VERSION and TARGETARCH arguments
  2. Constructs the GitHub release URL:
    https://github.com/roc-lang/roc/releases/download/{ROC_VERSION}/roc-linux_{ARCH}-{ROC_VERSION}.tar.gz
    
    Where ARCH is x86_64 (for amd64) or aarch64 (for arm64).
  3. Downloads and extracts the tarball
  4. Copies the roc and roc_language_server binaries to /opt/roc/bin/
  5. Copies lib/wasi-libc.a to /opt/roc/lib/
  6. Copies LICENSE to /opt/roc/

The tarball contains a top-level directory with a nightly-derived name (e.g., roc_nightly-linux_x86_64-2025-09-09-d73ea109/), so the script uses a glob/wildcard rather than assuming a fixed directory name.

Build Workflow

Local Development

# Build all three images
make build-all ROC_VERSION=0.0.0-alpha2

# Build a specific target
make build-dev ROC_VERSION=0.0.0-alpha2

# Build and push
make push-all ROC_VERSION=0.0.0-alpha2

# Override registry
make build-base ROC_VERSION=0.0.0-alpha2 REGISTRY=localhost:5000

CI/CD (Gitea Actions)

The .gitea/workflows/build.yaml workflow triggers on Git tags matching v* (e.g., v0.0.0-alpha2). It:

  1. Extracts the Roc version from the tag (v0.0.0-alpha20.0.0-alpha2)
  2. Sets up Docker Buildx with multi-arch support (linux/amd64, linux/arm64)
  3. Logs into the Gitea container registry using the Gitea Actions token
  4. Builds and pushes all three image targets in a single build command
  5. Tags each image with both the version and :latest

Makefile Targets

Target Description
build-base Build the base image
build-dev Build the dev image
build-ci Build the ci image
push-all Build all images and push to registry
build-all Build all three images (no push)

Variables: ROC_VERSION (default 0.0.0-alpha2), REGISTRY (default git.roo.lol/jbrechtel).

Security Considerations

  • Images run as non-root roc user (UID 1000)
  • debian:bookworm-slim base receives regular security patches
  • Only ca-certificates and essential packages are added beyond the binary
  • The base image has no shell or package manager in the final stage
  • Dev image has a shell (bash) by design for interactive use