More about CodingBooth

A guided tour through the pieces that make a booth — from the wrapper script on disk to the sidecars beside the container — with the picture for each.

← Back to home

On this page

How it works

How CodingBooth works

CodingBooth maps the container's user to your host UID and GID, mounts your project into /home/coder/code, and runs everything as the unprivileged coder user. Files created inside the booth are owned by you on the host — no root-owned artifacts, no permission dance.

Read the full guide →

The wrapper

The booth wrapper architecture

The booth script you commit to your repo is a small bash wrapper that downloads, verifies (SHA-256), pins, and executes the platform-specific codingbooth binary. A shared cache (~/.cache/codingbooth/) keeps versions reusable across projects.

Wrapper implementation →

User permissions

User permissions in the booth

The booth runs as coder with the same UID/GID as your host user, plus passwordless sudo when you need it. The .booth/ folder is mounted read-only by default; opt in to writes with --writable-booth, or harden further with the no-sudo template.

User permissions →

Variants in detail

Variants in detail

Five built-in variants share one base image: base (ttyd terminal), codeserver (VS Code in the browser), notebook (Jupyter Lab with multi-language kernels), and desktop-xfce / desktop-kde (full Linux desktops via noVNC). Same toolchain underneath — different way to drive it.

Variants guide →

Running a booth

Running a booth

./booth picks the variant from .booth/config.toml, builds (or reuses) the image, maps your UID/GID, mounts your code, and launches the UI. Use --daemon for background, -- <cmd> for command passthrough, or --keep-alive to keep the container after exit.

booth run →

Commands

Booth commands cheatsheet

One CLI, many verbs: run, config, build, list, start / stop / restart, shell / exec, example try, template list, install / update. Most options also live in config.toml, so the same setup works across machines without re-typing flags.

User manual →

Built-in tools

Built-in tools available in every booth

Every booth ships with a curated baseline: bash/zsh, git/gh/tig, curl/wget/httpie, jq/yq/tree, nano/tilde/ranger/less, plus tini, socat, locales and CA certs. You build on top — not from scratch.

booth config

booth config interactive TUI

booth config scaffolds a complete .booth/ from 77+ templates — languages, IDEs, AI tools, browsers, databases, desktops, education. Browse and pick in the TUI, or script it with a selection DSL like go:1.25+linter/python:3.13+uv/claude-code.

booth config guide →

Config TUI

booth config interactive TUI

The interactive sibling of booth config --no-tui: a multi-tab terminal UI for browsing templates by category, previewing what will be emitted, and toggling extensions with auto-select awareness. Round-trips with .booth/ — open it on an existing project and the current selection is pre-populated.

Config TUI →
  • Create a fresh project: mkdir my-bun-app && cd my-bun-app.
  • Run booth config to scaffold a .booth/ interactively.
  • Switch to the Config tab () — global booth settings live here.
  • Open the Variant cycle-field (Enter) and cycle to codeserver with .
  • Commit (Enter) — variant captured.
  • Back to Languages () — Space selects bun, Enter opens its parameter editor (BUN_VERSION, BUN_PKGS).
  • Esc leaves the editor (selection preserved); Ctrl+S saves.
  • Terminal: .booth/ initialized; Boothfile shows setup bun + setup bun-code-extension.

The Boothfile

Boothfile compiles to Dockerfile

A declarative, intent-based config that compiles to a standard Dockerfile — setup, install, run, env, copy, expose. No more Dockerfile boilerplate, but the generated Dockerfile is always inspectable.

Boothfile implementation →

Build & publish

Build and publish a booth image

booth build compiles the Boothfile into a Docker image and (optionally) pushes it to a registry. The default tag is a content hash of the configuration, so identical configurations produce identical tags — clean caching, exact reproducibility, and a stable target for deploy pipelines.

booth build →

Setup scripts

Setup script pattern

Each *--setup.sh follows a three-artifact pattern: a build-time install, an on-start startup script, and an on-shell profile script — with LEVEL ordering (50–79) so language, extension, and tool layers compose in the right sequence.

Booth setup guide →

Customization

Customizing your booth

Everything CodingBooth needs travels with your repo under .booth/: config.toml, Boothfile, .env, setups/, startups/, home/, home-seed/, cache/. Edit by hand, generate via booth config, or mix the two.

Customization guide →

Variable expansion

Variable expansion in booth config and .env

Values you write in .booth/.env, .booth/config.toml, and CB_* env vars are resolved by booth before docker sees them — a small bash-like subset: $VAR, ${VAR:-default}, ${VAR:?required}, leading ~, and "..." / '...' quoting. Docker's own --env-file wouldn't normally expand any of these, so booth parses .booth/.env first and hands docker a resolved copy.

Variable expansion →

The .booth/.tmp/ directory

The .booth/.tmp/ ephemeral runtime directory

A per-session ephemeral runtime dir: wiped on start, cleaned on exit. Features like booth--expose drop their control files here; your startup scripts can use it too. Visible from the host while the booth is running; --leave-tmp-on-exit and --keep-tmp-on-start preserve contents for debugging.

booth tmp →

Home directory

Home directory seeding and persistence

Seed the container's home from four layered sources — team defaults / overrides + host defaults / overrides — with .mount-this markers for fine-grained control. Make the whole /home/coder persistent with --persist-home (Docker named volume), and ship it around with home-volume export/import.

Home directory →

Local cache

The .booth/cache/ local cache

.booth/cache/ mirrors the container's filesystem layout — anything you drop here is auto-bind-mounted at the matching path inside the booth. Perfect for shell history, tool configs, and AI agent caches that should persist across sessions on this machine. Gitignored, host-local, safe to delete any time.

Local cache →

Persistent home

Persistent home via Docker named volume

Add --persist-home and /home/coder is backed by a Docker named volume — IDE settings, browser sessions, shell history all survive exit and rebuild. Manage volumes with home-volume list / export / import to move state between machines. Experimental.

Persistent home →

Lifecycle

Booth lifecycle

Treat booths like cattle: list, start, stop, restart, remove, prune. Add session timers (--show-count-down) and idle auto-shutdown (--idle-time) so an abandoned booth doesn't quietly burn resources.

Lifecycle guide →

shell & exec

Connecting to a running booth with shell and exec

Every variant has its own way to spawn extra terminals (Code Server's integrated terminal, Notebook's Launcher, a desktop terminal emulator). shell and exec give the terminal variant the same capability — and work everywhere else too. No SSH server, no extra ports; both use docker exec under the hood.

booth connect →

booth message

booth message overlay dialogs and toasts

Show overlays, prompts, and toasts inside a running booth's web UI — from scripts, CI, or another shell. Types: yes-no, ok, text, password, choice, radio, checkbox, toast. Same overlay renders identically on terminal, Code Server, Notebook, and the desktop variants.

booth message →

booth expose

booth expose: runtime TCP tunnel from host to container
# Inside the booth, after starting a server on a new port:
$ booth--expose 8080
→ TCP tunnel: host localhost:8080 → container localhost:8080

# Save it to .booth/config.toml so it survives restarts
$ booth--expose 8080 --permanent

Started a server on a port that wasn't published at docker run time? booth--expose opens a TCP tunnel at runtime — no container restart, no port re-mapping. The host-side booth process picks up the request via a control file in .booth/.tmp/ and sets up the listener automatically.

booth expose →

Session timers

Session timers — run time and count-down

--show-run-time displays elapsed session time next to Restart / Shutdown; --show-count-down arms a wall-clock budget with a live countdown and color warnings at 15 / 10 / 5 minutes remaining. --count-down-exit-code lets cron, CI, or a wrapper distinguish a timeout from a clean exit.

Session timers →

Idle auto-shutdown

Idle auto-shutdown with Pause and Disable controls

--idle-time <s>[,t] arms an in-container watchdog. After s seconds with no activity it shows a "Still using this booth?" prompt; if no reply within t seconds the booth shuts itself down. A persistent Idle chip in the lifecycle panel lets you Pause (time-boxed hold) or Disable (indefinite) — so a long compile is never mistaken for an abandoned session.

Idle guide →

Health & info

Health and info HTTP endpoints

Wrapped variants (codeserver, notebook, desktop-xfce, desktop-kde) expose two HTTP endpoints under the reserved /__booth/ namespace. /__booth/health actively probes the inner service every call — so a hung code-server can't quietly look healthy. /__booth/info returns container identity (name, variant, version, port). Both are Cache-Control: no-store.

Health & info →

Examples

Pre-built example workspaces

45+ ready-to-run workspaces: go, python, nodejs, rust, java, flask, fastapi, django, nextjs, react, angular, spring-boot, rails, lamp, lemp, mern, pern, wordpress — and more. List them with booth example list; spin one up with booth example try.

Examples guide →

Advanced features

The pieces that show up when one container isn't enough.

Docker-in-Docker

Docker-in-Docker via sidecar

Need docker, kind, or k3d inside the booth? --dind spins up a sibling Docker daemon in a sidecar, linked back via labels — the booth gets a real Docker socket without polluting the host.

DinD implementation →

Sandbox

Sandbox egress filtering

--sandboxed routes the booth's egress through an Envoy proxy with a domain allowlist, enforced by iptables. Useful when you run third-party AI agents or untrusted code and want to bound where they can phone home.

Sandbox implementation →

Booth-in-Booth

Booth-in-Booth nested environments

Open a booth from inside another booth — useful when an outer environment is your "host" and you want an inner one for a specific task. CodingBooth detects nesting, warns you on booth run, and routes Docker through the right daemon.

Booth-in-Booth →

Desktop & noVNC

Desktop variants over noVNC

The desktop variants ship a full Linux desktop (XFCE or KDE Plasma) accessible from your browser via noVNC — perfect for IDEs that don't run in a browser (IntelliJ, PyCharm, Eclipse, KiCad). Configure resolution with GEOMETRY=1920x1080 and pick a resize mode (remote, scale, off).

Desktop & noVNC →

AI agent integration

AI agent integration

Every booth includes /opt/codingbooth/AGENT.md — operational instructions for in-container AI agents — plus a symlink farm (CLAUDE.md, COPILOT.md, CURSOR.md, …). Templates exist for Claude Code, Codex, Aider, Cursor, GH Copilot, Ollama, and friends, so adding an agent is one line in booth config.

Agent guide →