Isolated AI coding agent on Linux design template (Podman, overlayfs, bindfs, git) A developer published a reference architecture for running an AI coding agent in an isolated rootless Podman container on Linux, using overlayfs and bindfs to give the agent read-only access to the host home directory while confining all writes to a single git-versioned work directory. The design runs the agent as a dedicated system user (pi-agent) with host mounts performed by root via /etc/fstab, and routes model calls through a local gateway over a VPN to DeepSeek. Promotion of agent changes to the real home directory and any /etc modifications remain human-controlled, with etckeeper recording system configuration changes. Reference architecture for running a coding agent in an isolated container with controlled read/write access to a host home directory, versioned via git, and routed through a local gateway to remote models. Design template. Adapt values, paths, and tool choices to the target environment. - A Linux distribution with systemd and standard Unix permission semantics Debian 13 used as reference . - Podman, rootless, no daemon. - overlayfs and FUSE/bindfs available. - fuse-overlayfs and bindfs installed. - The fuse package providing fusermount installed, required for FUSE mounts via /etc/fstab . - yadm or an equivalent dotfiles manager for the host home repo. - A local LLM gateway reachable over a VPN interface. - etckeeper available for system configuration versioning. Pi agent Podman container, --network=host, host user: pi-agent | network - Bifrost wg0 VPN - DeepSeek v writes into /work /srv/pi/work overlayfs merged, the only directory mounted into the container |- overlayfs: | lowerdir = host home read-only | upperdir = /srv/pi/.overlay/upper writable, agent changes | workdir = /srv/pi/.overlay/work kernel scratch |- git repo: versioned agent changes |- controlled promotion test branch - merge v Host home yadm, umask 007, .gitignore excludes secrets and Work ^ bindfs fstab, root - ~/Work host user transparent access /etc system configuration, versioned with etckeeper ^ human applies agent-proposed changes with sudo Properties: - The agent writes only into /srv/pi/work the overlay merged layer . - The host home is the overlay lowerdir, read-only. The agent reads it without duplicating data and cannot write to it. - The agent runs as a separate user/group pi-agent , so Unix permissions protect host files. - Only work is mounted into the container. The agent cannot see .overlay , the host home, or any host paths. - Changes are versioned in git work . The overlay layer isolates the agent's writes from the host home. - Promotion to the real home is human-controlled. - System configuration /etc is never written by the agent directly; the agent proposes, the human applies with sudo, and etckeeper records it. - All mounts overlay and bindfs are performed by root via /etc/fstab . The host user lacks ownership of /srv/pi , and bindfs UID remapping requires root; therefore no user mount units are used. | Item | Configuration | |---|---| | Agent | Pi Mario Zechner terminal harness | | Runtime | Podman, rootless, no daemon | | Network | --network=host | | Host user | pi-agent , group pi-agent , system user | Inside the container: | Path | Role | |---|---| | /home/pi | Agent home config, caches - ephemeral | | /work | Work directory, mounted to /srv/pi/work | Only /work is mounted into the container. /home/pi is ephemeral; the agent does not retain config or caches between sessions. The agent has no access to .overlay , the host home, or host filesystem paths. Launch overlay mode : podman run --rm -it --network=host \ --env HOME=/home/pi \ --volume /srv/pi/work:/work:Z \ imagen-pi System user UID < 1000 decision: The agent is a system user pi-agent : it runs as a service rather than an interactive account, and its dedicated group reinforces permission separation from the host user. Adjustments required: - Assign a usable login shell system users default to nologin / false . - Add explicit user-namespace ranges for rootless Podman in /etc/subuid and /etc/subgid : pi-agent:100000:65536 Launch as the agent user with the runtime dir set: sudo -u pi-agent XDG RUNTIME DIR=/run/user/$ id -u pi-agent podman ... Critical requirement - host home protection: The agent MUST run as the pi-agent user, never as the host user's UID. Running the container with the host user's UID invalidates permission protection: the agent becomes the owner of host files and umask 007 no longer applies. The overlay lowerdir read-only mount prevents writes but not reads if the agent shares the host UID. Containerfile from official Pi plain-Docker pattern : FROM node:24-bookworm-slim RUN apt-get update \ && apt-get install -y --no-install-recommends bash ca-certificates git ripgrep \ && rm -rf /var/lib/apt/lists/ RUN npm install -g --ignore-scripts @earendil-works/pi-coding-agent RUN useradd -m -s /bin/bash -d /home/pi pi && chown -R pi:pi /home/pi WORKDIR /work ENV HOME=/home/pi COPY pi-config/ /home/pi/.pi/agent/ USER pi ENTRYPOINT "pi" The pi-config/ directory holds the agent configuration Bifrost endpoint as an OpenAI-compatible provider . Credentials are injected at runtime, not committed to the image. | Item | Configuration | |---|---| | Gateway | Bifrost local | | Access | Reachable via wg0 VPN interface | | Models | DeepSeek | | Agent link | OpenAI-compatible endpoint | | Network | --network=host ; proxy available as an outbound control point | The upstream model credential lives only in the gateway. The agent talks to the gateway and never sees the upstream key. | Item | Configuration | |---|---| | Work merged | /srv/pi/work | | overlayfs lowerdir | Host home, read-only | | overlayfs upperdir | /srv/pi/.overlay/upper , owned by pi-agent | | overlayfs workdir | /srv/pi/.overlay/work | | Host access | bindfs - ~/Work transparent | upperdir and workdir must not reside inside the lowerdir . Placing them under /srv/pi/.overlay outside the host home avoids overlay recursion. Transient overlay - per-session lifecycle: The overlay layer is disposable and recreated for each session to avoid divergence between the host home lowerdir and stale upperdir data. - Session start: discard the previous upperdir, mount a fresh overlay against the current host home. - During the session: the agent reads the host home and writes to the fresh upperdir. - Session end: promote approved changes to the host home via git, then discard the upperdir. Do not edit host-home files that the agent is concurrently modifying; overlayfs gives priority to the upperdir, hiding the host edit from the agent. Git resolves any conflict during promotion. Discarding the upperdir deletes uncommitted agent work; promote before discarding. Safe reset procedure: Before remounting for a new session, discard the upperdir without risking the host home. Use a script that rejects unsafe paths and deletes only content: bash /bin/bash set -euo pipefail UPPERDIR="/srv/pi/.overlay/upper" WORKDIR="/srv/pi/.overlay/work" Safety check: reject unsafe paths for d in "$UPPERDIR" "$WORKDIR"; do if "$d" = "$HOME" || "$d" = "/" || -z "$d" ; then echo "Refusing to discard: unsafe path $d " &2 exit 1 fi done Verify the agent is stopped if pgrep -u pi-agent -f "podman" /dev/null 2 &1; then echo "Agent still running; stop it before discarding" &2 exit 1 fi Delete only content, never the root directory find "$UPPERDIR" -mindepth 1 -delete find "$WORKDIR" -mindepth 1 -delete echo "Overlay layer discarded safely." The reset flow is: stop the agent, promote approved changes, unmount the overlay, run the reset script, remount, relaunch. The same image supports two modes of use. The only difference is what is mounted at /work . Mode 1 - Overlay home-aware : the agent sees the host home read-only via the overlay and writes into /srv/pi/work . podman run --rm -it --network=host \ --env HOME=/home/pi \ --volume /srv/pi/work:/work:Z \ imagen-pi Mode 2 - Project repository : the agent is limited to a single host directory a git repository mounted at /work . The host home and its secrets are entirely outside the container. This provides maximum isolation and is simpler than the overlay, since no overlayfs is involved. Reversion is handled by git in the repository. podman run --rm -it --network=host \ --env HOME=/home/pi \ --volume /home/TU USUARIO/proyectos/mi-repo:/work:Z \ imagen-pi Use Mode 2 for concrete tasks on individual repositories; use Mode 1 when the agent must read the host home/global config while writing only into work . Both mounts are performed by root via /etc/fstab . The host user does not own /srv/pi , and bindfs UID remapping requires root, so no user mount units are used. 1. Overlay fuse-overlayfs /srv/pi/.overlay/upper /srv/pi/work fuse.overlayfs lowerdir=/home/TU USUARIO,upperdir=/srv/pi/.overlay/upper,workdir=/srv/pi/.overlay/work,x-systemd.automount 0 0 2. Bindfs options comma-separated; map without leading -- /srv/pi/work /home/TU USUARIO/Work fuse.bindfs map=TU USUARIO/pi-agent,x-systemd.requires=/srv/pi/work,x-systemd.after=/srv/pi/work 0 0 Requirements: - The fuse package providing fusermount must be installed for FUSE mounts via fstab. - In fstab, options are comma-separated. The bindfs mapping option is map= , not --map= . - If root mounts the bindfs and the host user accesses ~/Work , set user allow other in /etc/fuse.conf . - Mount order: overlay first /srv/pi/work , bindfs second ~/Work , enforced with x-systemd.requires / x-systemd.after . The agent must never write directly to /etc . System configuration is handled through a propose-and-apply flow with etckeeper : 1. The agent proposes a /etc change as a draft in work. 2. The human reviews the draft. 3. The human applies the change to /etc with sudo. 4. etckeeper records the change automatic commit with diff . 5. If a change breaks the system, revert with git in /etc. etckeeper versions /etc with git and hooks into the package manager apt / dpkg on Debian , committing the state before and after package operations. Setup Debian : sudo apt install etckeeper sudo etckeeper init Notes: - etckeeper init creates a baseline commit of the current /etc without deleting anything; existing configuration becomes the starting point. - etckeeper excludes sensitive files by default /etc/shadow , /etc/gshadow , private keys . Verify exclusions on setup. - The /etc git repository may be exposed through the overlay if the host home is the lowerdir; protect or relocate it. | Scope | Tool | Notes | |---|---|---| | Host home | yadm | Git-based dotfiles management | | Work /srv/pi/work | git | Separate repo for agent changes | | /etc | etckeeper | System configuration, hook-driven | | yadm .gitignore | - | Exclude Work and all secrets | The yadm repository lives inside the host home and is exposed through the overlay. With umask 007 its files are group-only; relocate it outside the home for additional isolation. 1. Agent commits changes in work. 2. Reviewer inspects the diff. 3. Promote to a test branch on the home repo. 4. Validate on the branch. 5. Merge to the default branch, or apply a selective rsync. The human approves every promotion. Nothing moves from work to the home automatically. Promote before discarding the overlay layer. | Item | Configuration | |---|---| | umask | 007 user scope : new files/dirs 660/770 | | Agent user | Different group pi-agent , falls under "others" | | .ssh | Restricted 600/700 | | overlayfs | Respects permissions | umask 007 protects against the pi-agent user only while that user is not a member of a group with read access. It is not retroactive; existing files with others read bits require a one-time hardening pass. Host home protection is a primary requirement. All layers depend on the agent running as a separate user/group. Do not launch the agent with the host user's UID. - Secrets are never committed to git yadm, work, or /etc via etckeeper . - Command allowlist/denylist restricts the agent. - Host network is accepted; a proxy can act as an optional outbound allowlist. - Agent logs are reviewed to detect prompt injection or anomalous behavior. | Layer | Purpose | |---|---| | overlayfs | Discard the layer for a per-session reset | | git work | Fine-grained revert of changes | | etckeeper | Revert of /etc changes | | restic / rsnapshot | Cover binaries, caches, non-text data | | External | Off-machine copies; same-disk snapshots insufficient | - Containerfile for the agent, versioned in yadm. - fstab entries for the overlay and the bindfs. - etckeeper config/state for /etc . | Decision | Choice | Considered alternatives | |---|---|---| | Workspace isolation | overlayfs | btrfs snapshots, container volume, plain dir | | Home exposure | bindfs --map | symlink, mount --bind | | Default umask | 007 | 077 owner-only , 022 group-read | | Dotfiles manager | yadm | chezmoi, GNU Stow, plain git | | Container runtime | Podman | Docker, systemd-nspawn | | Sandboxing | container | bubblewrap, firejail, Landlock | | Agent account type | system user | regular user UID = 1000 | | Mount location | fstab root | user mount units | | System config | etckeeper | NixOS declarative config | Rationale: - overlayfs exposes the home read-only and stores only deltas. - bindfs --map resolves UID mismatch while keeping underlying files owned by pi-agent . - umask 007 permits group sharing while excluding "others"; 077 is stricter. - Podman is rootless and daemon-less, matching a minimal setup. - A system user models the agent as a service; the tradeoff is subuid/subgid setup. - Both mounts live in fstab: the host user does not own /srv/pi , and bindfs UID remapping requires root. User mount units cannot remap owners, so fstab root is required. - Mode 2 project/repository provides maximum isolation with no overlayfs and git-only reversion; Mode 1 overlay provides home-aware context when needed. - etckeeper versions /etc declaratively and integrates with the package manager, keeping the agent out of system paths. - Overlay recursion: upperdir/workdir must not reside inside lowerdir. - Whiteouts: an agent can create deletion markers in the upperdir, hiding files in its view without modifying the host home. - UID mismatch: a bindfs --map that remaps host files to the agent user's UID negates permission protection. - Exposed yadm repo: its git history is readable through the overlay if not protected or relocated. - Non-filesystem vectors: /proc, /sys, environment, network are not covered by file permissions. - umask is not retroactive: existing permissive files require a separate pass. - Rootless Podman as a system user UID < 1000 requires explicit subuid/subgid ranges. - Launching the container with the host user's UID invalidates permission protection on the host home. - A system user defaulting to a non-login shell must be assigned a usable shell. - Layer divergence: editing a host-home file the agent modified in the upperdir hides the host edit from the agent upperdir takes priority . Discard and remount per session. - The fuse package must be installed for FUSE mounts via fstab; otherwise the mount fails at boot even if the CLI mount works. - In fstab, bindfs options are comma-separated and use map= no leading -- . - If root mounts the bindfs and the host user accesses it, user allow other must be set in /etc/fuse.conf . - Mode 2 mounts a host directory directly; ensure it contains no secrets and that only the intended repository is exposed. - The etckeeper repository lives inside /etc and may hold sensitive configuration history; verify exclusions and protect access. 1. Create the pi-agent user/group; assign a home outside the host home. 2. Assign a usable login shell to pi-agent . 3. Add subuid/subgid ranges for pi-agent in /etc/subuid and /etc/subgid . 4. Install the agent in the container; wire it to the gateway. 5. Set umask 007 in the shell profile user scope . 6. Harden existing files with others read bits one-time pass . 7. Create /srv/pi/work , /srv/pi/.overlay/upper , /srv/pi/.overlay/work . 8. Initialize the git repo in /srv/pi/work . 9. Install fuse , fuse-overlayfs , bindfs ; set user allow other in /etc/fuse.conf if required. 10. Add the overlay and bindfs entries to /etc/fstab ; run mount -a . 11. Exclude Work and secrets from the dotfiles repo. 12. Define the agent command allowlist/denylist. 13. Configure backups and external copies. 14. Test the full flow: agent edits in work, diff, branch, merge. 15. Always launch the agent as pi-agent , never the host user's UID. 16. Discard and remount the overlay at each session start; promote before discarding. 17. Install and initialize etckeeper ; verify its sensitive-file exclusions. 18. Apply agent-proposed /etc changes manually with sudo; let etckeeper record them.