Apple Container
Run the agent in a fully isolated macOS VM — full Rails stack, nothing exposed to your Mac.
Apple Container runs each agent in its own lightweight VM with a complete Rails stack and every native library preinstalled. The agent's work — clones, bundle install, migrations, asset builds — stays inside its volumes, and nothing in your Mac's home folder is exposed to it.
Apple Container
You're hereTightest isolation — each agent runs in its own lightweight VM. The lean, native choice on a recent Mac.
Apple silicon · macOS 15+
Docker & OrbStack
Runs anywhere Docker does, Intel or ARM, with a ready-made Compose file. OrbStack keeps it light on a Mac.
Docker or OrbStack · Intel or Apple silicon
Not the right fit? Set up Docker & OrbStack instead.
Install the runtime
Install the Apple Container CLI from its releases page — a .pkg you double-click — then start the runtime. The first start installs a Linux VM kernel; answer Y when it asks.
open https://github.com/apple/container/releases/latest
container --version
container system start
container system status
Copied
The agent runs entirely inside the container, so the only other tool you need on the Mac is Claude Code — used once to mint the agent's token. Skip this if you already have it.
npm install -g @anthropic-ai/claude-code
Copied
Get the image
Pull the published image and tag it local/fragua:latest so the rest of the guide's commands line up. Pulling skips the 10–15 minute build and gives you the exact released image.
container image pull ghcr.io/maquina-app/fragua-container:latest
container image tag ghcr.io/maquina-app/fragua-container:latest local/fragua:latest
container image list | grep fragua
Copied
What the image bundles
The image is a complete Rails environment built on Ubuntu 24.04. You don't have to trust that blindly — here's what's inside, and a command to confirm it yourself.
- Language runtimes
- Ruby and Node, managed by mise. A project's own .mise.toml overrides the global versions automatically.
- Ruby toolchain
- Bundler and Rails preinstalled, with build flags wired to the system libraries so native gems like nokogiri, pg, mysql2, and hiredis compile without fuss.
- Native libraries
- The full bundle-install stack: build tools, SSL and crypto, SQLite, PostgreSQL and MySQL, image and media (ImageMagick, libvips, WebP, ffmpeg), PDF (wkhtmltopdf plus fonts), libxml, Protobuf and gRPC, geospatial (GEOS, PROJ), and Pango/Cairo rendering.
- Command-line tools
- GitHub CLI, git, Claude Code, and the Fragua CLI — everything the agent needs to clone, build, and push.
Confirm the stack from inside the container:
ruby --version && node --version && rails --version
sqlite3 --version && pg_config --version && mysql_config --version
ffmpeg -version | head -1 && wkhtmltopdf --version
gh --version && claude --version && fragua --version
Copied
Run these in the one-time setup shell below, or any container shell. Every line should print a version.
Want to change what's installed — add a library, pin a version, build multi-arch? The Dockerfile and build script live in the fragua-tools repo, with notes on customizing and publishing your own image.
Create volumes and sign in once
Everything the agent needs lives in four named volumes, so the running agent depends on nothing from your Mac. You set them up once; they survive every rebuild.
Create the four volumes
container volume create fragua-config
container volume create fragua-secrets
container volume create fragua-workdir
container volume create fragua-data
container volume list
Copied
fragua-config
/fragua-config
Fragua token and local status, your Git identity, the Claude token, and Claude Code's session and per-project state.
fragua-secrets
/fragua-secrets
The GitHub CLI token and the container's own SSH key, together. Read-only once the agent is running, so it can use but never rewrite them.
fragua-workdir
/fragua-workdir
The agent's working tree — clones, bundle install, databases, compiled assets.
fragua-data
/fragua-data
The Claude Code and fragua CLIs, plus the gems and Node packages the agent installs at runtime — kept so a rebuild doesn't refetch them.
Generate the Claude token (on your Mac)
Claude Code's interactive login needs a browser, which a headless container can't open. Mint a long-lived token on the Mac instead — you'll paste it into a volume next, and the container reads it from there on every run.
claude setup-token
Copied
Open the one-time setup shell
Open a one-time shell with the config and secrets volumes mounted writable. This is the only time the secrets volume is writable — at runtime it drops to read-only, so the agent can use the GitHub token and SSH key but never rewrite them.
container run --rm -it \\
-v fragua-config:/fragua-config:rw \\
-v fragua-secrets:/fragua-secrets:rw \\
local/fragua:latest bash
Copied
Inside the container, create the secret directories, then sign in to GitHub. The device-code flow works headless — it prints a code and a URL. The --insecure-storage flag writes the token onto the secrets volume so it persists; without it gh may stash it in an in-VM keyring that's gone on the next run.
mkdir -p /fragua-secrets/gh /fragua-secrets/ssh && chmod 700 /fragua-secrets/ssh
gh auth login --insecure-storage
Copied
Give the container its own SSH key rather than copying your personal one. A dedicated key is isolated from your Mac and you can revoke it on its own. Set a Git identity, generate the key, and register it on GitHub:
git config --global user.name "Your Name"
git config --global user.email "[email protected]"
chmod 700 /root/.ssh
ssh-keygen -t ed25519 -f /root/.ssh/id_ed25519 -N "" -C "fragua-container"
gh auth refresh -h github.com -s admin:public_key
gh ssh-key add /root/.ssh/id_ed25519.pub --title "fragua-container"
ssh -o IdentitiesOnly=yes -i /root/.ssh/id_ed25519 -T [email protected]
Copied
- The private key has no passphrase — a headless agent can't type one — so it sits unencrypted in the
fragua-secretsvolume. Anyone who can read that volume can read the key, but it's a single-purpose key, not your personal one. - Registering it with
gh ssh-key addgrants push access to your GitHub account. Treat it as a live credential and give it a clear title, so you can find and revoke it later withgh ssh-key deleteor in GitHub → Settings → SSH keys. - Commits are attributed to the name and email you set here, not your usual host identity — set them deliberately so agent commits are recognizable.
gh auth refresh -s admin:public_keywidens the gh token's scope so it can add the key. That token lives infragua-secretsand is mounted read-only when the agent runs.- It's still the safer posture: the key is dedicated, revocable, and never touches
~/.ssh. Reusing your host key would copy it into the volume, and a passphrase-protected key would have to be stripped to work headless — leaving an unencrypted copy of your real key.
Using https:// remotes instead of [email protected]? Skip the key entirely and run gh auth setup-git so gh acts as git's credential helper.
Advanced: you can reuse an existing host key by mounting it in and copying it once, but only if it has no passphrase — at your own risk.
Paste the token from claude setup-token into the config volume. The image's entrypoint reads this file on every run, so you never pass it again.
printf '%s' 'PASTE_THE_setup-token_VALUE' > /fragua-config/claude-oauth-token
chmod 600 /fragua-config/claude-oauth-token
claude -p "reply with the single word: ok"
Copied
Finally, connect the Fragua CLI with the AgentToken from your Settings page, check the setup, and leave the shell.
fragua login
fragua doctor
exit
Copied
Run the agent
The agent mounts only these named volumes — no host paths. The config and work volumes stay writable; the GitHub token and SSH key drop to read-only so the agent can use but never rewrite them.
fragua-config
rw
/fragua-config
fragua-secrets
ro
/fragua-secrets
fragua-workdir
rw
/fragua-workdir
fragua-data
rw
/fragua-data
container run \\
--name fragua-agent \\
--detach \\
-v fragua-config:/fragua-config:rw \\
-v fragua-secrets:/fragua-secrets:ro \\
-v fragua-workdir:/fragua-workdir:rw \\
-v fragua-data:/fragua-data:rw \\
local/fragua:latest
Copied
Day to day
Watch the agent, rebuild it, or wipe it. Every volume survives a rebuild, so there's no re-login and no lost work.
container logs --follow fragua-agent
container exec -it fragua-agent bash
# rebuild — every volume survives, no re-login
container exec fragua-agent fragua prune --yes
container stop fragua-agent && container rm fragua-agent
container build --no-cache -t local/fragua:latest .
# update the CLIs (Claude Code + fragua) a running agent uses — no rebuild
container exec fragua-agent fragua-refresh-cli # both (or: claude / fragua)
# (refresh only the image's offline baseline instead: ./build.sh --refresh-cli)
# drop the runtime gem/node cache + CLIs (re-bootstrapped on next start, keeps creds)
container volume delete fragua-data && container volume create fragua-data
Copied
Troubleshooting
- container system start hangs on "Verifying machine API server"
- The VM kernel isn't installed. Run
container system kernel set --recommended, thencontainer system startagain. - claude /login fails with "Invalid OAuth Request / Unknown scope"
- The browser redirect can't finish in a headless container. Don't use
/login— runclaude setup-tokenon your Mac and write the result to/fragua-config/claude-oauth-token. - git push fails with "Permission denied (publickey)"
- The container's key isn't on GitHub. Re-run
gh ssh-key add /root/.ssh/id_ed25519.pubin the setup shell, then test withssh -T [email protected]. - HTTPS git fails "could not read Username for https://github.com"
- No git credential helper is wired to the gh token — the agent was set up SSH-only. Run
gh auth setup-gitonce in the setup shell; it writes the helper into/fragua-config/gitconfigand persists. - claude refuses --dangerously-skip-permissions "cannot be used with root/sudo privileges"
- The image runs as root by design and sets
IS_SANDBOX=1so Claude Code recognizes the isolated container. If you hit this, you're on an older image built before that env var — rebuild, or setIS_SANDBOX=1when you invokeclaude. - An x86 host can't run the image
- Images built on Apple silicon are arm64, and Apple Container is arm64-only. For Intel hosts, build and publish a matching image using the Docker guide's multi-arch build.