Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/chaitu426/minibox/llms.txt

Use this file to discover all available pages before exploring further.

The BASE directive is the first thing MiniBox evaluates at the start of every build. It determines the root filesystem that all BLOCK layers are stacked on top of. MiniBox supports four distinct resolver types — OCI registry pull, scratch, local image reference, and local tar archive — selected automatically based on the value you provide.

Resolution Order

When BASE <image> is encountered, the builder (internal/builder/builder.goresolveBaseImage) tries resolvers in the following order:
1

scratch — skip all resolution

If the value is exactly scratch, no base filesystem is prepared. The block starts with a completely empty root.
2

Local image — check the OCI index

If the value matches an image name already registered in DataRoot/index.json (a previously built MiniBox image), that image’s layers are reused directly — no network call required.
3

Local archive — detect .tar / .tar.gz extension

If the value ends in .tar or .tar.gz and the file exists on disk, MiniBox extracts it into DataRoot/base_layers/ and uses that as the base rootfs.
4

OCI registry pull — fallback for everything else

Any other value is treated as an OCI image reference and pulled from a Docker V2-compatible registry.

OCI Registry Pull

MiniBox implements the Docker V2 Registry API directly in Go (internal/builder/fetch_oci.go). No Docker daemon is required.

Supported Registries

RegistryExample BASE value
Docker Hub (official)BASE alpine:latest
Docker Hub (namespaced)BASE library/ubuntu:22.04
GitHub Container RegistryBASE ghcr.io/user/repo:tag
Quay.ioBASE quay.io/coreos/etcd:v3.5.0
Any Docker V2 registryBASE registry.example.com/myimage:1.0
Any registry that implements the Docker Distribution Specification V2 is supported. The registry URL defaults to registry-1.docker.io for bare image names with no hostname prefix.

Authentication

For registries that require authentication (including Docker Hub for pulling private images), MiniBox follows the standard Bearer token challenge flow:
  1. Send unauthenticated manifest request.
  2. Receive 401 Unauthorized with a WWW-Authenticate: Bearer realm=… header.
  3. Exchange the realm/service/scope details for a short-lived token at the auth endpoint.
  4. Retry the manifest request with the bearer token in the Authorization header.
MiniBox currently uses anonymous pulls for public images. Private registry credentials are not yet configurable via the CLI. For private images, use a local archive or pre-pulled local image as the base.

Multi-Arch Manifest List Resolution

Modern registries serve manifest lists (OCI Image Index) that contain per-platform entries. MiniBox always resolves to linux/amd64:
// From fetch_oci.go
for _, m := range manifest.Manifests {
    if m.Platform.Architecture == "amd64" && m.Platform.OS == "linux" {
        targetDigest = m.Digest
        break
    }
}
If linux/amd64 is not found in the manifest list, MiniBox falls back to the first entry and logs a warning:
[base] warning: amd64 not found, falling back to sha256:a1b2c3d4…

Parallel Layer Downloads

MiniBox downloads all layers concurrently, capped at 3 simultaneous connections (maxConcurrentLayerPulls = 3) to avoid overwhelming the registry CDN. Each layer download retries up to 3 times (layerDownloadRetries = 3) with exponential back-off (2 s, 4 s) on transient failures.
[base] node:18-slim has 4 layers
[base] pulling layer 1/4 (sha256:a1b2c3)...
[base] pulling layer 2/4 (sha256:d4e5f6)...
[base] pulling layer 3/4 (sha256:g7h8i9)...
[base] extracting layer 1/4...
[base] extracting layer 2/4...
Layers are extracted in order after all downloads complete, preserving correct OCI overlay semantics (later layers overwrite earlier ones).

Cache Location

Pulled base images are extracted to:
DataRoot/base_layers/<image_tag>/
Where <image_tag> is the image reference with : and / replaced by _. For example, node:18-slim is cached at:
DataRoot/base_layers/node_18-slim/
On subsequent builds MiniBox checks whether DataRoot/base_layers/<image_tag>/bin exists. If it does, the pull is skipped entirely:
[base] node:18-slim: already cached

Examples

# Docker Hub official image
BASE alpine:latest

# Docker Hub official image — explicit library prefix
BASE library/debian:bookworm-slim

# Versioned Node.js slim image from Docker Hub
BASE node:18-slim

# GitHub Container Registry
BASE ghcr.io/myorg/mybase:2.1.0

# Quay.io
BASE quay.io/coreos/etcd:v3.5.9

# Self-hosted registry
BASE registry.internal.corp/platform/base:stable

BASE scratch — Static Binary Images

BASE scratch
scratch is a special keyword that starts the build with a completely empty filesystem — no shell, no libc, no utilities of any kind. This produces the smallest possible images and is the standard approach for statically linked Go and Rust binaries. When BASE scratch is used, resolveBaseImage returns empty lower directories and skips all fetching:
// From builder.go
if baseImage == "scratch" {
    return nil, nil, nil
}

Go Static Binary Example

BASE scratch

BLOCK builder
    run apk add --no-cache git
    workdir /src
    copy . .
    run CGO_ENABLED=0 go build -o /app/server .

BLOCK runtime
    BNEED builder
    copy FROM=builder /app/server /server
    env PORT=8080

START /server
BASE is a top-level directive only — it applies to the whole file, not to individual blocks. Set BASE scratch at the top to start with an empty filesystem for all blocks. The compiler tools needed in a builder block must be installed via RUN from within that empty base, or use a two-file approach where the compiler image is a separate pre-built local image.
BASE scratch images have no shell, so RUN commands will fail because there is no /bin/sh. All file population must come from COPY FROM=<builder-block> instructions. Healthchecks requiring curl, wget, or /bin/sh will also fail — use a minimal base like alpine or distroless if you need any system utilities at runtime.

What to copy into a scratch image

ArtifactWhy
Statically linked binaryThe application itself — no external library deps
/etc/ssl/certs/ca-certificates.crtRequired for HTTPS outbound calls
/etc/passwd (optional)Only if the binary reads user entries
Static asset directoriesHTML, templates, or embedded data not baked into the binary

Local Image Reference

BASE <image-name>[:<tag>]
If MiniBox can find <image-name> in its own DataRoot/index.json, it reuses that image’s layers directly without any network access. This is how you chain MiniBox-built images together.
# First, build a shared base
minibox build -t my-base .

# Then reference it in another MiniBox file
BASE my-base:latest

BLOCK app
    copy ./app /app
    workdir /app

START ./app
The layer directories of the local image are resolved via containerRuntime.ResolveImageLayers and used as the base lower directories for the overlay stack.
Local image references allow you to maintain a shared internal base image — pre-installed with your organisation’s CA certificates, monitoring agents, or common utilities — and use it across many application images without re-downloading anything.

Local Archive

BASE ./rootfs.tar.gz
BASE ./rootfs.tar
BASE /tmp/base.tar
Any path ending in .tar or .tar.gz that exists on the host filesystem is treated as a local archive base. This is useful for:
  • Importing rootfs images exported from other tools.
  • Bootstrapping from a custom-built Alpine/Ubuntu minimal rootfs.
  • Offline environments with no internet access.
MiniBox extracts the archive once and caches the result. The cache key is derived from the archive filename (special characters replaced with _) and stored at:
DataRoot/base_layers/archive_<filename>/
For example, ./rootfs.tar.gz is extracted to:
DataRoot/base_layers/archive_rootfs_tar_gz/
On subsequent builds the cached extraction is reused and the archive is not unpacked again:
[base] ./rootfs.tar.gz: using cached archive extract
The archive cache does not detect changes to the archive file itself. If you update the archive, run minibox system prune --build-cache or manually delete the corresponding DataRoot/base_layers/archive_* directory to force a re-extraction.

Examples

# Relative path to a gzipped rootfs in the build context
BASE ./rootfs.tar.gz

# Absolute path to a plain tar archive
BASE /tmp/custom-base.tar

# Pre-built Alpine minimal from a CI artifact
BASE ./alpine-minirootfs-3.19.tar.gz

Summary: Choosing a Base Image Type

OCI Registry

Use when: You want a well-maintained public image (Alpine, Ubuntu, Node.js, Python, Go) as a starting point.
BASE alpine:3.19
BASE node:20-slim
BASE python:3.12-alpine

scratch

Use when: Building a statically compiled binary (Go, Rust) that needs no OS utilities at runtime. Produces the smallest possible image.
BASE scratch

Local Image

Use when: You have a shared internal base image already built with MiniBox and want to avoid redundant layer re-downloads across many application images.
BASE internal-base:2.0

Local Archive

Use when: Working offline, importing from another tool, or using a custom rootfs that is not available from any OCI registry.
BASE ./custom-rootfs.tar.gz

Build docs developers (and LLMs) love