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.

MiniBox is a lightweight Linux container engine consisting of two binaries: minibox (CLI) and miniboxd (daemon). The CLI is a pure HTTP client; all container logic lives in the daemon. This page describes the full system — from parsing a MiniBox file to a running container process with networking, cgroups, and health checks.

Two-Binary Architecture

BinaryEntry pointResponsibility
miniboxdcmd/daemon/main.goHTTP API, builder, runtime, networking, state
miniboxcmd/cli/main.goCLI commands → HTTP requests → formatted output
The CLI never directly touches container namespaces, overlay mounts, or iptables. Every operation is delegated to miniboxd over HTTP (http://127.0.0.1:8080 by default).

End-to-End Flowchart


Build Pipeline

1. Parse phase

internal/parser/parser.go reads a MiniBox file and produces a Cfile model containing:
  • BaseImage — the base rootfs to pull
  • Blocks — a list of named DAG build units
  • Cmd, Env, Workdir — image-level defaults
  • HealthcheckCmd, HealthcheckIntervalSec — for health monitoring
  • Instructions — legacy linear mode fallback
DAG directives:
DirectiveMeaning
BASE <image>Base rootfs (Alpine supported)
BLOCK <name>Named build unit
NEED <block>Dependency edge
RUN ...Shell command in block context
COPY <src> <dst>Copy from context or another block (COPY FROM=)
WORKDIR <path>Set working directory
ENV KEY=VALEnvironment variable
PKG <name>Expands to apk add --no-cache <name>
AUTO-DEPSDetects manifests and runs installers (npm, pip, go)
START ...Image default command
HEALTHCHECK [--interval=N] <cmd>Health check definition

2. Base image prep

internal/builder/fetch_oci.go pulls the base image from Docker Hub (or any Docker V2 registry):
  1. Fetches the manifest from auth.docker.io
  2. Resolves multi-arch manifest lists to linux/amd64
  3. Downloads layer blobs
  4. Caches extracted rootfs in DataRoot/base_layers/<image_tag>

3. DAG execution (wave-based parallelism)

builder.BuildImage() calls buildFromBlocks():
1

Compute wavefronts

Identify all blocks whose NEED dependencies are already complete. These form a wave and run concurrently.
2

Execute each block

For each block:
  • Create a temp overlay root (tmp/<hash>/{upper,work,root})
  • For RUN instructions: mount overlay and execute chroot /bin/sh -c ...
  • For COPY instructions: copy from the host context or from another block’s layer
  • For WORKDIR: create path and update effective working directory
3

Cache check

The cache key is derived from: parent hash + dependency names + workdir + instruction stream + COPY source content hash. If DataRoot/layers/<hash> exists, the block is CACHED.
4

Finalize layer

Move the overlay upper directory into DataRoot/layers/<hash>.
5

Advance

Mark the wave complete and compute the next wavefront.
Build-only blocks (BNEED) are executed but excluded from the final image layer stack unless files are explicitly copied from them.

4. OCI finalization

After all blocks complete:
  1. Tar + gzip each layer → blobs/sha256/<digest>
  2. Generate OCIConfig blob (cmd, env, workdir, healthcheck labels, layer diff IDs)
  3. Generate OCIManifest blob (config + layer descriptors)
  4. Upsert image reference into index.json with annotation org.opencontainers.image.ref.name = <imageName>

Build log format

[build] START image=myapp
[base] pulling alpine ...
[build] mode=dag blocks=4
[dag] wave=1 ready=runtime,layout
[block] START runtime
[block] CACHED source
[dag] wave=1 done elapsed=1.2s
[dag] wave=2 ready=deps
[block] START deps
[dag-summary] cached=1 built=3
[finalize] writing blobs ...
[build] DONE image=myapp

Runtime Execution Path

Parent launcher (internal/runtime/process.go)

RunCommand() / RunCommandStream():
  1. Resolve image config and layer stack from OCI index
  2. Prepare DataRoot/containers/<id>/ and mount overlay rootfs (MountRootfs)
  3. Spawn child as /proc/self/exe child ... with clone flags: CLONE_NEWPID | CLONE_NEWUTS | CLONE_NEWNS | CLONE_NEWNET | CLONE_NEWIPC
  4. Call SetupContainerNetwork(pid, id, ip, portMap)
  5. Register container in state.json via RegisterContainer()
  6. Start health monitor goroutine (if image has healthcheck labels)
  7. Wait for child; record exit code; teardown network

Child runtime (internal/runtime/container.go)

RunContainer():
  1. Validate args and container ID
  2. Ensure mount namespace isolation (env guard + optional unshare fallback)
  3. Apply cgroup writes: memory.max, cpu.max, cgroup.procs, io.weight
  4. Set hostname
  5. chroot into container rootfs
  6. Mount /proc, minimal /dev
  7. Apply security: capability drop, rlimits, PR_SET_NO_NEW_PRIVS, seccomp deny-list
  8. Exec tiny init: /proc/self/exe init -- <cmd...>

Tiny init / PID 1 (internal/runtime/init.go)

RunInit():
  • Starts the target command
  • Forwards signals to the process group
  • Reaps zombie processes with wait4(-1, ...)
  • Exits with child status/signal semantics
This prevents zombie accumulation when the workload forks subprocesses, providing PID 1 semantics without a full init system.

Networking Architecture

Implemented in internal/network/network.go.

Host bridge setup (SetupBridge)

  • Creates minibox0 bridge and assigns 172.19.0.1/24
  • Enables IP forwarding sysctls
  • Adds iptables NAT masquerade rules
When MINIBOX_BRIDGE_ON_STARTUP=0, bridge creation is deferred to the first container run, making daemon startup near-instant.

Per-container networking (SetupContainerNetwork)

For each container:
  1. Create veth pair: veth-<id> (host) and vetp-<id> (peer)
  2. Attach host veth to minibox0 bridge
  3. Move peer veth into the container’s network namespace (via PID)
  4. Inside the container netns: bring up loopback, rename peer to eth0, assign IP, add default route via 172.19.0.1
  5. Program iptables DNAT and FORWARD rules for each -p host:container mapping

Teardown

  • TeardownContainerNetwork() removes veth pairs and per-container iptables rules
  • TeardownBridge() removes minibox0 and the base NAT setup (called on daemon shutdown)

Security Controls

API layer

  • Method-scoped routes enforce correct HTTP verbs
  • Body size limits: 4 MB (run/exec), 64 MB (build)
  • Optional bearer token authentication middleware
  • Input validation via internal/security/security.go (image names, container IDs, path traversal)

Runtime layer

ControlImplementation
Namespace isolationPID, UTS, mount, network, IPC via clone flags
SeccompDeny-list filter in internal/runtime/seccomp_linux.go
Capability dropinternal/runtime/drop_linux.go (skipped in db_mode)
No new privilegesPR_SET_NO_NEW_PRIVS set before seccomp
Path traversalContainer ID and path validation before any file operations

Build context restrictions

Build contexts are validated against MINIBOX_BUILD_PREFIXES using security.ResolveAllowedPath(). Requests with contexts outside the allowed prefixes are rejected at the handler level.

Health Checks

  1. HEALTHCHECK [--interval=N] <cmd> is parsed from the MiniBox file
  2. Stored as OCI config labels: mini.healthcheck.cmd, mini.healthcheck.interval
  3. On container start, a monitor goroutine runs nsenter ... /bin/sh -c <cmd> periodically
  4. Container Health field transitions: startinghealthy or unhealthy
  5. minibox ps shows the current HEALTH column value
Health check support is basic. Retries, start-period thresholds, and full OCI HealthConfig semantics are not yet implemented.

Compose Orchestration

cmd/cli/main.go implements the compose orchestrator on top of the daemon API:

Service DAG

  • minibox-compose.yaml is parsed with gopkg.in/yaml.v3
  • A dependency graph is built from depends_on declarations
  • Topological sort determines startup order: services with no dependencies start first

Automated builds

  • Services with a build context trigger POST /containers/build before starting
  • The resulting image is tagged <project>-<service>

Service discovery

When a container starts as part of a project:
  1. The daemon fetches all running containers in the same project
  2. Builds a hosts-file mapping service names and container IDs → IP addresses
  3. Writes the mapping to rootfs/etc/hosts before PID 1 executes
This allows web to connect to db:5432 without any DNS configuration.

File Map

PathResponsibility
cmd/daemon/main.goDaemon entry point
cmd/cli/main.goCLI + compose orchestrator
internal/api/router.goHTTP route registration, body limits, middleware chain
internal/api/auth.goBearer token middleware
internal/api/handler/*.goPer-endpoint request handlers
internal/builder/builder.goDAG build orchestration
internal/builder/fetch_oci.goOCI image pulling from registries
internal/parser/parser.goMiniBox file parser
internal/runtime/process.goParent container launcher
internal/runtime/container.goChild namespace + chroot setup
internal/runtime/init.goPID 1 tiny init / zombie reaper
internal/runtime/state.goContainer state persistence
internal/runtime/seccomp_linux.goSeccomp deny-list filter
internal/runtime/drop_linux.goCapability dropping
internal/network/network.goBridge, veth, iptables
internal/storage/image_archive.gosave / load tar operations
internal/storage/prune.goBlob garbage collection
internal/models/oci.goOCI data model types
internal/security/security.goInput validation helpers
internal/config/config.goDaemon configuration from env vars

Build docs developers (and LLMs) love