Files
OmniRoute/contrib/podman
Diego Rodrigues de Sa e Souza c06ac9aafa fix(security): remove literal secrets from podman manifest, block CHANGEME remote login (#13679) (#13812)
Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
2026-09-16 06:09:29 -03:00
..
2026-06-04 20:05:38 -03:00
2026-06-04 20:05:38 -03:00
2026-06-04 20:05:38 -03:00

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 systemctl and podman unshare do 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. Generate secrets before first start

omniroute.container no longer ships JWT_SECRET / API_KEY_SECRET / INITIAL_PASSWORD values — earlier versions shipped copy-pasteable placeholders (change-me-to-a-random-base64-string, change-me-to-a-random-hex-string) that an operator could forget to rotate, leaving the deployment with a public, guessable secret/password (#13679). Generate real ones and put them in your project .env:

echo "JWT_SECRET=$(openssl rand -base64 48)" >> .env
echo "API_KEY_SECRET=$(openssl rand -hex 32)" >> .env
echo "INITIAL_PASSWORD=$(openssl rand -hex 24)" >> .env

If you skip this: JWT_SECRET/API_KEY_SECRET are auto-generated and persisted on first boot, and the dashboard requires setup from localhost before it accepts any password — safer than a literal default, but a real INITIAL_PASSWORD is still recommended so a non-interactive first boot has a known credential to log in with.

4. 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.

5. Reload systemd and start

systemctl --user daemon-reload
systemctl --user start omniroute-redis
systemctl --user start omniroute

6. 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.