Podman can run the same HolyCode image as Docker. Use this guide when you prefer a daemonless or rootless container runtime, especially on Fedora, RHEL, CoreOS, Rocky, AlmaLinux, or similar Linux hosts.
This guide mirrors the minimal HolyCode web UI setup. For the full Docker Compose profile and sidecar services, use the main README and docker-compose.full.yaml.
- Running the HolyCode web UI with
podman run - Keeping OpenCode state, cache, and workspace files in bind mounts
- Loading provider keys from
.envwith--env-file .env - SELinux labels for Fedora/RHEL/CoreOS hosts
- Rootless Podman permission and user namespace notes
- Optional service boundaries for Paperclip, Hermes, and CLIProxyAPI
- Safe update and recreate behavior
Install Podman on the host and run the command from the HolyCode project folder, or from another folder that contains the same .env.example, data, cache, and workspace paths.
Copy the environment template and add at least one provider key:
cp .env.example .envEdit .env and set the provider you plan to use, for example ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY, or another supported provider variable.
Podman treats relative bind mount sources as paths relative to the directory where you run podman. Missing bind mount sources fail, so create them first.
Create the host directories:
mkdir -p ./data/opencode ./local-cache/opencode ./workspaceRun HolyCode:
podman run -d \
--name holycode \
--restart unless-stopped \
--shm-size=2g \
-p 4096:4096 \
-v ./data/opencode:/home/opencode \
-v ./local-cache/opencode:/home/opencode/.cache/opencode \
-v ./workspace:/workspace \
--env-file .env \
-e PUID=$(id -u) \
-e PGID=$(id -g) \
docker.io/coderluii/holycode:latestOpen http://localhost:4096.
What the important options do:
--shm-size=2ggives Chromium and browser automation enough shared memory.-p 4096:4096publishes the OpenCode web UI../data/opencode:/home/opencodepersists OpenCode config, sessions, plugins, and service state../local-cache/opencode:/home/opencode/.cache/opencodekeeps plugin and package cache on local disk../workspace:/workspacemounts your project files.--env-file .envloads provider keys and optional HolyCode toggles without putting secrets in shell history.PUIDandPGIDtell HolyCode which host UID/GID to use for file ownership inside mounted paths.docker.io/coderluii/holycode:latestfully qualifies the Docker Hub image for Podman.
If you use a different host folder, keep the container paths unchanged. /home/opencode, /home/opencode/.cache/opencode, and /workspace are the paths HolyCode expects inside the container.
On SELinux hosts such as Fedora, RHEL, or CoreOS, unlabeled bind mounts can look like permission problems from inside the container. Add :Z to each HolyCode bind mount for a private label used by this one container:
podman run -d \
--name holycode \
--restart unless-stopped \
--shm-size=2g \
-p 4096:4096 \
-v ./data/opencode:/home/opencode:Z \
-v ./local-cache/opencode:/home/opencode/.cache/opencode:Z \
-v ./workspace:/workspace:Z \
--env-file .env \
-e PUID=$(id -u) \
-e PGID=$(id -g) \
docker.io/coderluii/holycode:latestUse :z only when the same host path must be shared by multiple containers. Do not casually relabel broad system paths or your entire home directory.
Rootless Podman runs containers inside a user namespace. That is one of the main reasons people choose Podman, but it also means bind mount ownership can behave differently from Docker.
Check these if files are unexpectedly owned or blocked:
- Your user has ranges in
/etc/subuidand/etc/subgid. PUID=$(id -u)andPGID=$(id -g)match the host user that should own files in./workspace.- The host paths exist before running the container.
- SELinux hosts use
:Zor:zlabels as described above.
Some rootless setups use custom user namespace modes such as --userns=keep-id. Do not add that flag by default for HolyCode; it changes how the container process user is mapped. Use it only after testing that it matches your host policy and does not interfere with HolyCode's PUID/PGID remapping.
The command above runs the minimal HolyCode web UI on port 4096.
Paperclip and Hermes can be enabled with the same environment variables used by the Docker Compose examples:
ENABLE_PAPERCLIP=true
PAPERCLIP_PORT=3100
PAPERCLIP_DEPLOYMENT_MODE=authenticated
PAPERCLIP_ALLOWED_HOSTNAMES=192.168.1.50,my-host.local
ENABLE_HERMES=true
HERMES_PORT=8642If you enable them, publish the ports you need:
-p 4096:4096 \
-p 3100:3100 \
-p 8642:8642Paperclip listens on 3100 when enabled. Hermes exposes an API service on 8642; a 404 at / is normal as long as the process stays healthy.
CLIProxyAPI is different. In HolyCode it is documented as a full Compose sidecar profile, not as part of the one-container Podman command. Use docker-compose.full.yaml when you need the supported CLIProxyAPI sidecar workflow.
Pull the latest image:
podman pull docker.io/coderluii/holycode:latestThen recreate the container:
podman stop holycode
podman rm holycodeRun the podman run command again. Your data stays in ./data/opencode, ./local-cache/opencode, and ./workspace.
Do not use podman start holycode as an update path. It restarts the existing container with the old image, environment variables, ports, and mount settings.
--restart unless-stopped restarts the container after normal exits unless you explicitly stopped it. Reboot persistence depends on Podman's podman-restart.service. If you later manage HolyCode through systemd or Quadlet, use systemd's Restart= behavior instead of Podman's --restart flag.
Verify PUID, PGID, host directory ownership, and rootless user namespace setup:
id -u
id -gMake sure those values match the PUID and PGID passed to the container. If rootless Podman still maps ownership unexpectedly, check /etc/subuid and /etc/subgid for your user.
Add :Z to the three bind mounts for a private container label:
-v ./data/opencode:/home/opencode:Z \
-v ./local-cache/opencode:/home/opencode/.cache/opencode:Z \
-v ./workspace:/workspace:ZUse :z only when multiple containers must share the same host path.
Publish HolyCode on a different host port while keeping the container port at 4096:
-p 4097:4096Then open http://localhost:4097.
Make sure the command includes:
--shm-size=2gWithout enough /dev/shm, Chromium can crash or produce broken automation results.
Changing .env does not update an already-created container. Stop and remove the container, then run the command again so Podman creates a new container with the updated environment.