Configuration
Resource limits
Every instance gets a memory/CPU/PID ceiling, so a runaway build can't take down the machine. Defaults are 6 GB memory, 4 CPUs, 512 PIDs — sized against your container runtime's memory budget, not your host's total RAM. Override per-repo in .claudio.yml, or per-instance:
A flag wins over the repo's .claudio.yml, and create says so when it does. The value is written to .claudio.yml, so it survives claudio restart — restart takes no resource flags and re-derives every limit from that file.
An instance killed for exceeding its memory limit is reported as such by claudio ls and claudio status, distinct from a plain stop.
.claudio.yml and config.yml
Two files, both local to your machine and neither committed:
.claudio.yml— what this repo needs: ports, services, setup commands, instance IDs.~/.claudio/config.yml— what this machine allows: resource ceilings, port range, editor, SSH keys. Applies to every instance.
~/.claudio/ (a directory) is Claudio's own state — repos, worktrees, the database.
Where .claudio.yml lives
In the folder you ran claudio create . from, not in the worktree:
Edits take effect on the next create or restart. No commit required — Claudio reads the file on disk and adds it to .git/info/exclude.
An instance created from a remote URL has no folder of yours, so its config is read from the worktree. create prints that path too.
.claudio.yml?
Claudio still reads it from your folder, and tells you once:
Git keeps tracking a file it already tracks, so your edits show as changes until you untrack it. Claudio never touches your git index.
.claudio.yml (all fields optional):
~/.claudio/config.yml (all fields optional):
editor
The editor claudio open <id> launches against an instance's worktree. Set it with the CLI rather than by hand — it checks the binary resolves on PATH before writing, so a typo fails immediately:
A bare binary name or absolute path; arguments (code -n) aren't supported. There's no default — install.sh offers to set one. Only claudio open needs it.
ssh_keys
The private keys git inside an instance may use to reach a git@host:path remote. Empty by default: the agent can read any key it is handed, for every host that key opens, so none crosses the sandbox boundary unless you list it.
Manage the list with the CLI rather than by hand — add checks the path is a private key file that exists before writing, so a typo fails immediately:
add stores the path absolute and is a no-op for a key already listed. remove does not need the file to still exist, so it can always clear a stale entry. Either way the file ends up as:
- Each entry is a key file (
~expands), mounted read-only. List files, not the directory holding them; two entries can't share a file name. - A hand-written entry that doesn't exist fails
create/start/restartwith the path named, rather than being skipped. - Nothing else from
~/.sshis shared — noconfig, noknown_hosts. The container records a host's key on first connection and refuses it if it later changes. - A passphrase-protected key can't be used by the agent's non-interactive
git. Keys held only in an agent or hardware token aren't supported. - A change applies to new instances immediately and to a running one on its next
claudio restart <id>. An instance created before the image shipped an SSH client needsclaudio rebuild <id>once.
Lifecycle hooks: post_create vs post_start
The two hooks are not interchangeable. A Claudio restart replaces the container rather than restarting a process inside it, so work that persists on disk and work that has to be running have different schedules.
The rule: if the command produces files, it belongs in post_create. If it produces a process, it belongs in post_start.
npm start in post_create blocks claudio create forever waiting for a server that never exits — and wouldn't come back after a restart anyway.
Running a dev server on every start
claudio create installs dependencies and starts the server; claudio restart <id> brings the server back.
The server must bind 0.0.0.0, not localhost. Published ports forward to the container's external interface, so an app on loopback is unreachable from the host even with its port mapped. Use vite --host 0.0.0.0, next dev -H 0.0.0.0, or server.listen(port, '0.0.0.0'). Prefer the explicit flag: HOST=0.0.0.0 is ignored by some tools, Vite among them.
How post_start reports failures
post_start commands are launched detached, so they have no exit status at launch. Shortly after, Claudio checks whether each is still running and reports one that already exited, with the tail of its log:
This catches a command that can't start at all, not a server that dies a minute later. Either way the instance stays running: a dead post_start command is reported, never fatal.
For the full output:
Holds stdout and stderr from every post_start command. Truncated at each provision, so it describes the container currently running.
A repo declaring an image: section with apt/npm_global/dockerfile needs its own image layer, built on top of the shared base — build it explicitly:
create never builds an image as a side effect. If the image it needs doesn't exist, it prints the exact claudio image build --repo ... command to run.