* docs(podman): clarify Podman Machine deployment * style(podman): format guidance and regression test
Podman Deployment
Run OmniRoute with podman compose on Linux, macOS, or Windows, or with Quadlet on a Linux host that runs systemd.
Choose the deployment path
- Linux with a local Podman engine and user systemd: Compose or Quadlet.
- macOS or Windows: Compose. Podman runs containers in a remote Linux VM
managed by Podman Machine; host-side
systemctlandpodman unsharedo not operate on that engine. - Any other remote Podman connection (including an optional Linux Podman Machine): treat it like Podman Machine, not like a local rootless engine.
Option A: Quadlet (Linux + systemd only)
Use this option only when the Podman engine and user systemd instance run on the
same Linux host. The systemctl --user commands below do not configure a Podman
Machine from a macOS or Windows host.
1. Build the image
cd /path/to/omniroute
podman build --target runner-base -t omniroute:base .
# For web-cookie providers (gemini-web, claude-web, claude-turnstile):
podman build --target runner-web -t omniroute:web .
# For CLI tool support:
podman build --target runner-cli -t omniroute:cli .
2. Copy Quadlet files to the systemd directory
mkdir -p ~/.config/containers/systemd/omniroute
cp contrib/podman/*.container ~/.config/containers/systemd/omniroute/
cp contrib/podman/*.network ~/.config/containers/systemd/omniroute/
cp contrib/podman/*.volume ~/.config/containers/systemd/omniroute/
3. Mount the project .env for secrets
Edit ~/.config/containers/systemd/omniroute/omniroute.container and
uncomment/replace the EnvironmentFile line with the absolute path to
your project .env:
EnvironmentFile=/home/USER/code/docker/OmniRoute/.env
Make sure CONTAINER_HOST=podman is set in that .env.
Alternatively, edit the env vars directly in the .container file.
4. Reload systemd and start
systemctl --user daemon-reload
systemctl --user start omniroute-redis
systemctl --user start omniroute
5. Verify
systemctl --user status omniroute
curl http://localhost:20128/v1/models
To follow logs:
journalctl --user -u omniroute -f
The checked-in Quadlet files already contain [Install] sections with
WantedBy=default.target. The Quadlet generator applies those sections during
systemctl --user daemon-reload. Generated Quadlet services are transient
systemd units and must not be enabled with systemctl enable.
Option B: podman compose (all supported host platforms)
The project's docker-compose.yml now works with both Docker and Podman.
Just set CONTAINER_HOST=podman in .env before starting.
1. Set the runtime in .env
Make sure .env contains:
CONTAINER_HOST=podman
2. Prepare the data directory
The Compose profiles bind-mount ./data at /app/data. Create the directory,
then use the permission guidance for your engine topology below.
mkdir -p data
3. Build and start
The application profiles use local image names such as omniroute:base; those
are build outputs, not published Docker Hub tags. On the first run, have Compose
build the selected profile:
podman compose --profile base up -d --build
Alternatively, build the matching target explicitly and tell Compose to reuse that local image:
podman build --target runner-base -t omniroute:base .
podman compose --profile base up -d --no-build
Profiles
Same profiles as docker compose:
| Profile | First-run command |
|---|---|
base (no CLIs) |
podman compose --profile base up -d --build |
web (+Chromium/Playwright) |
podman compose --profile web up -d --build |
cli (+CLI tools) |
podman compose --profile cli up -d --build |
host (host-mounted binaries) |
podman compose --profile host up -d --build |
cliproxyapi (sidecar) |
podman compose --profile cliproxyapi up -d |
Data directory permissions by topology
Linux with a local rootless Podman engine
Rootless Podman maps container UIDs into a subordinate range. If the container
cannot write to the bind-mounted ./data, run this on the Linux host whose
local engine will run the container:
podman unshare chown 1000:1000 ./data
Use this only when the CLI is connected to a local, non-remote Podman engine.
podman unshare is not available with a remote Podman client.
macOS or Windows with Podman Machine
Podman on macOS and Windows runs the engine inside a Linux VM. The host CLI is a
remote client, so do not run podman unshare on the macOS or Windows host.
Create ./data in the host directory shared with the machine and try the
Compose start command above; no ownership change is needed when that mount is
already writable.
If the bind mount is not writable and you do not need direct host access to the database files, use a Podman-managed named volume with the published image. Named volumes avoid host-directory UID translation:
podman volume create omniroute-data
podman run -d --name omniroute \
--env-file .env \
-e DATA_DIR=/app/data \
-p 20128:20128 \
-v omniroute-data:/app/data \
docker.io/diegosouzapw/omniroute:latest
For a bind mount that still fails, inspect or repair the shared path from the
Podman Machine side (for example with podman machine ssh) according to the
machine provider's mount configuration. A container cannot reliably determine
or repair that host/VM topology for you.
How it works
The docker-compose.yml uses fully-qualified image names
(docker.io/library/redis:7-alpine) and flat variable expansions so it
works with both Docker and Podman without a separate compose file.
The entrypoint script (check-permissions.sh) reads CONTAINER_HOST
from .env to choose the runtime guidance:
- docker:
sudo chown -R ... ./data - podman: a topology-neutral warning that points back to this guide, because the container cannot tell whether its engine is local or reached through Podman Machine.