Docker & OrbStack
Run the agent in an isolated container with Docker or OrbStack — full Rails stack, a ready-made Compose file.
Docker and OrbStack run the agent in an isolated container 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 the repo ships a Compose file so a single command brings it up.
Apple Container
Tightest isolation — each agent runs in its own lightweight VM. The lean, native choice on a recent Mac.
Apple silicon · macOS 15+
Docker & OrbStack
You're hereRuns 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 Apple Container instead.
Install the runtime
Install OrbStack — recommended on a Mac — or Docker Desktop, then confirm the daemon is running.
brew install orbstack # recommended — or install Docker Desktop
docker --version && docker info
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 guide and the Compose file line up. Pulling skips the 10–15 minute build and gives you the exact released image.
docker pull ghcr.io/maquina-app/fragua-docker:latest
docker tag ghcr.io/maquina-app/fragua-docker:latest local/fragua:latest
docker image ls | 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
docker volume create fragua-config
docker volume create fragua-secrets
docker volume create fragua-workdir
docker volume create fragua-data
docker volume ls | grep fragua
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.
docker 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-docker"
gh auth refresh -h github.com -s admin:public_key
gh ssh-key add /root/.ssh/id_ed25519.pub --title "fragua-docker"
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
A compose.yaml mounts only the four volumes, no host paths. Grab it below, bring it up, follow the logs, and check the agent's status.
fragua-config
rw
/fragua-config
fragua-secrets
ro
/fragua-secrets
fragua-workdir
rw
/fragua-workdir
fragua-data
rw
/fragua-data
compose.yaml
Save this as compose.yaml in the directory you run docker compose from. It mounts only the four volumes — no host paths, nothing wired into your Mac.
services:
fragua-agent:
image: local/fragua:latest
volumes:
- fragua-config:/fragua-config:rw # fragua status/DB, git identity, claude token + state
- fragua-secrets:/fragua-secrets:ro # gh token + SSH key (read-only at runtime)
- fragua-workdir:/fragua-workdir:rw # agent working tree
- fragua-data:/fragua-data:rw # claude + fragua CLIs, runtime gems + node modules
restart: unless-stopped
volumes:
fragua-config:
external: true
fragua-secrets:
external: true
fragua-workdir:
external: true
fragua-data:
external: true
Copied
docker compose up -d
docker compose logs --follow
docker compose exec fragua-agent fragua status
Copied
Prefer a one-off docker run? Here's the equivalent of the Compose file:
docker run -d --name fragua-agent --restart unless-stopped \\
-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
Rebuild or refresh the agent. Every volume survives docker compose down and image rebuilds, so there's no re-setup.
# rebuild — every volume survives, no re-setup
docker compose exec fragua-agent fragua prune --yes
docker compose down
docker build --no-cache -t local/fragua:latest .
docker compose up -d --force-recreate # recreate so the new image's CLIs are adopted (plain up -d/start keeps the old container)
# update the CLIs (Claude Code + fragua) a running agent uses — no rebuild
docker compose exec fragua-agent fragua-refresh-cli # both (or: claude / fragua)
# (refresh only the image's offline baseline instead: ./build.sh --refresh-cli)
# inspect the workdir without touching the running agent
docker run --rm -v fragua-workdir:/data:ro alpine ls -la /data
# drop the runtime gem/node cache + CLIs (re-bootstrapped on next start, keeps creds)
docker volume rm fragua-data && docker volume create fragua-data
Copied
Troubleshooting
- claude /login fails with "Invalid OAuth Request / Unknown scope"
- The browser redirect can't finish in a headless container. Run
claude 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]. - gh says "not logged in" once the agent is running
- The
fragua-secretsvolume isn't mounted or is empty. Re-rungh auth login --insecure-storagein the setup shell, and confirm the volume is listed in your compose.yaml. - 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 an arm64 image
- Publish a multi-arch image with
./build.sh --platform linux/amd64,linux/arm64so both Intel and ARM hosts can pull the same tag.