Files
2026-07-14 20:47:29 +00:00

4.1 KiB

The claudaris executable

./claudaris is a single bash script at the repo root that wraps every Docker operation. It cds to its own directory first, so it can be invoked from anywhere. Run it with no arguments (or help) for usage.

Usage: ./claudaris <command>

Commands:
  config, configure  Interactive wizard to write .env with your settings
  build              Build the container image (always --no-cache --pull)
  start              Start (or create) the container
  connect            Start the container if needed, then attach a tmux session
  remove, stop, rm   Stop and remove the container
  help               Show this message

All commands source .env if present (gitignored, written by config), falling back to NAME=claudaris and the defaults listed below.

config (alias: configure)

Interactive wizard: prompts for NAME, DATA_DIR, and WORKSPACE_DIR, showing the current/default value for each (enter keeps it), then writes all three to .env. Safe to re-run any time. See Getting started for what each variable means.

build

sudo ./claudaris build

Runs docker build --no-cache --pull -t "$NAME" .. Always uncached so a rebuild picks up the latest Arch packages, the latest Claude Code release (installed straight into /root in the image), and the latest MCP server releases.

start

Creates and starts the container — or just docker starts it if a container named $NAME already exists (in that case none of the mount setup below is re-evaluated; use remove first to pick up mount changes).

On the host side it first seeds, without overwriting anything that already exists:

  • $DATA_DIR/home/.bashrc and $DATA_DIR/home/bash_aliases — copied from files/bashrc and files/bash_aliases.
  • $DATA_DIR/claude/credentials.json and $DATA_DIR/claude/claude.json — created empty. Seeding them as files matters: if Docker had to create the mount targets itself it would make directories, breaking the login mounts.

Then it runs the container with:

Mount / flag Purpose
--name "$NAME" --hostname "$NAME" Prompt reads root@$NAME instead of a random container ID.
$DATA_DIR/home/.bashrc/root/.bashrc Per-host shell config, editable without a rebuild.
$DATA_DIR/home/bash_aliases/opt/dotfiles/bash_aliases Per-host aliases and MCP env vars; sourced by .bashrc.
$DATA_DIR/claude/credentials.json/root/.claude/.credentials.json Claude Code OAuth token.
$DATA_DIR/claude/claude.json/root/.claude.json Claude Code account/onboarding state and user-scope MCP config.
$WORKSPACE_DIR/projects Your project workspace.
$DATA_DIR/ssh/root/.ssh (read-only, only if the host dir exists) Opt-in SSH access to other machines.

The login files are individual file mounts rather than a directory volume on purpose: Claude Code saves them atomically (write a temp file, then rename() over the target), and rename() onto a symlink replaces the symlink instead of writing through it — which would silently break persistence. A bind mount doesn't have that failure mode.

connect

sudo ./claudaris connect

Runs docker start "$NAME" (a no-op if already running) and then docker exec -it "$NAME" tmux. The in-container tmux is a wrapper that attaches to the main session if it exists and creates it otherwise, so connect always lands you in the same session.

The explicit docker start matters because the container stops itself: the entrypoint creates the main tmux session and exits once that session ends (tmux defaults — a window closes when its shell exits, the session closes with its last window). There's no --restart policy, so after a Ctrl+D the container sits stopped until connect (or start) brings it back.

remove (aliases: stop, rm)

Stops (ignoring errors if already stopped) and removes the container, so the next start recreates it fresh — needed after a build to actually run the new image, or after changing mounts. Nothing under $DATA_DIR or $WORKSPACE_DIR is touched.