Skip to content

Docker, Compose & one-click PaaS

Distroless/Debian images, Compose deployment, health checks, Podman, and the Coolify/Dokploy/EasyPanel templates.

Revka ships as a single self-contained binary, which makes it a natural fit for containers. This page covers running Revka in Docker — the two published images, a production docker-compose.yml, the container environment reference, the HEALTHCHECK probe, and Podman differences — and then the three one-click PaaS templates (Coolify, Dokploy, EasyPanel) plus the workflow that keeps them in sync with each release.

Use this page when you want Revka running as a long-lived gateway on a server or PaaS host. If you instead want Revka managed by your OS init system on the host directly, see Run as a background service. For how the in-container runtime.kind sandbox differs from the host runtime, see Runtime modes, adapters & resource limits.

Two multi-arch images are published to GitHub Container Registry. They share the same build but differ in their runtime base.

ImageBaseShellUse it when
ghcr.io/kumihoio/revka:latestgcr.io/distroless/cc-debian13:nonrootNoneYou want the smallest attack surface and the agent does not need shell tools.
ghcr.io/kumihoio/revka:debiandebian:bookworm-slim (ships bash, git, curl, ca-certificates)YesThe agent needs shell-based tools (ls, git, curl, etc.).

Both images use the same entrypoint — they run revka daemon, the full autonomous runtime (gateway, channels, heartbeat, and scheduler under one supervised process). Both run as the unprivileged nobody user (UID 65534), expose port 42617, and set HOME=/revka-data with the workspace at /revka-data/workspace.

Provide an API key at runtime, persist /revka-data in a named volume, and publish the gateway port:

Terminal window
docker run -d \
--name revka \
-e API_KEY=sk-or-... \
-e PROVIDER=openrouter \
-v revka-data:/revka-data \
-p 42617:42617 \
ghcr.io/kumihoio/revka:latest

Then open the dashboard at http://localhost:42617/. The image bakes a default config with allow_public_bind = true and require_pairing = false so the gateway is reachable from outside the container immediately.

Both Dockerfiles accept a single build argument controlling which Cargo feature flags are compiled in:

Build ARGDefaultMeaning
REVKA_CARGO_FEATURESchannel-lark,whatsapp-webComma-separated Cargo feature list passed to cargo build --release --locked --features. Set to empty to build the default feature set only.
Terminal window
# Build the distroless image with a custom feature set
docker build --build-arg REVKA_CARGO_FEATURES="channel-lark" -t revka:custom .
# Build the Debian (shell-equipped) variant
docker build -f Dockerfile.debian -t revka:debian .

The build is a multi-stage pipeline: a web-builder stage (Node 22 Alpine) compiles the dashboard, a builder stage (Rust 1.95) compiles the binary with registry/target build caches, and the final stage copies the stripped binary into the distroless or Debian runtime. The build fails fast if the resulting binary is under 1 MB, which catches dummy/cache-only build artifacts. See Cargo feature flags & ADRs for the full feature list.

The image bakes a working config.toml into /revka-data/.revka/config.toml. You normally only need to supply an API key; everything else has a container-appropriate default. Environment variables override the baked config at runtime.

VariableDefaultMeaning
API_KEY / REVKA_API_KEYemptyLLM provider API key. Required — must be supplied at runtime.
PROVIDERopenrouterLLM provider id. The :latest image leaves this unset so config-file edits win; the Debian/dev image defaults it.
REVKA_MODELprovider defaultModel override (e.g. anthropic/claude-sonnet-4-20250514).
REVKA_ALLOW_PUBLIC_BINDtrue (set in Compose/templates)Required for container networking — the gateway refuses non-loopback binds otherwise.
REVKA_GATEWAY_PORT42617Gateway listen port inside the container.
LANGC.UTF-8UTF-8 locale so CJK / multibyte input is handled correctly.
HOME/revka-dataHome directory; config lives at /revka-data/.revka/.
REVKA_WORKSPACE/revka-data/workspaceAgent workspace path.

Provider-specific key variables (OPENROUTER_API_KEY, ANTHROPIC_API_KEY, OPENAI_API_KEY, and others) also work and take precedence over the generic API_KEY. For the full environment-override list, see Configuration overview.

The repository ships a reference docker-compose.yml tuned for production: restart: unless-stopped, resource limits, a named volume, and the health check wired in. Supply the API key on the command line or via a .env file.

services:
revka:
image: ghcr.io/kumihoio/revka:latest
# For ARM64 where distroless exits immediately, switch to:
# image: ghcr.io/kumihoio/revka:debian
container_name: revka
restart: unless-stopped
environment:
- API_KEY=${API_KEY:-}
- PROVIDER=${PROVIDER:-openrouter}
- REVKA_ALLOW_PUBLIC_BIND=true
- REVKA_GATEWAY_PORT=${REVKA_GATEWAY_PORT:-42617}
volumes:
- revka-data:/revka-data
ports:
- "${HOST_PORT:-42617}:${REVKA_GATEWAY_PORT:-42617}"
deploy:
resources:
limits:
cpus: "2"
memory: 512M
reservations:
cpus: "0.5"
memory: 32M
healthcheck:
test: ["CMD", "revka", "status", "--format=exit-code"]
interval: 60s
timeout: 10s
retries: 3
start_period: 10s
volumes:
revka-data:

Lifecycle commands:

Terminal window
# Start in the background (inline key)
API_KEY=sk-or-... docker compose up -d
# Stop and remove the container (the named volume is kept)
docker compose down
# Follow logs
docker compose logs -f revka
# Publish on a different host port (e.g. 42617 is already taken)
HOST_PORT=8080 docker compose up -d

Or keep settings in a .env file next to the Compose file:

API_KEY=sk-or-...
PROVIDER=openrouter
HOST_PORT=42617
REVKA_GATEWAY_PORT=42617
VariableDefaultMeaning
API_KEYemptyProvider API key (required).
PROVIDERopenrouterProvider id.
HOST_PORT42617Host-side port in the ports mapping.
REVKA_GATEWAY_PORT42617Container-side gateway port.

The committed resource limits cap the container at 2 CPUs / 512 MB and reserve 0.5 CPU / 32 MB. Revka’s footprint is small, so these are comfortable defaults for a single instance; raise the limits if you run many channels or heavy MCP sidecars.

Both Dockerfiles and the Compose file use the same liveness probe:

healthcheck:
test: ["CMD", "revka", "status", "--format=exit-code"]
interval: 60s
timeout: 10s
retries: 3
start_period: 10s

revka status --format=exit-code is the canonical container liveness check. Instead of printing the human-readable status report, it performs a lightweight GET /health against the configured gateway and exits 0 when the gateway is healthy and 1 otherwise. The probe normalizes a bind host of [::] or 0.0.0.0 to 127.0.0.1, uses the configured gateway.port, and applies a 5-second timeout. The start_period gives the daemon 10 seconds to bind before failures count against the retry budget.

Run it manually against a running container:

Terminal window
docker exec revka revka status --format=exit-code; echo "exit=$?"

The images and Compose file run unchanged under Podman, with two adjustments for rootless Podman’s user-namespace and SELinux handling:

  • Add --userns keep-id so the container’s UID 65534 maps cleanly to your host user and the volume stays writable.
  • Append the :Z suffix to the volume mount so SELinux relabels it for the container.
Terminal window
podman run -d \
--name revka \
--userns keep-id \
-e API_KEY=sk-or-... \
-e PROVIDER=openrouter \
-v revka-data:/revka-data:Z \
-p 42617:42617 \
ghcr.io/kumihoio/revka:latest

podman compose reads the same docker-compose.yml. Everything else — environment variables, the health check, the published port — is identical to Docker.

Revka has draft templates for three self-hosted PaaS platforms. Each provisions the same container, a revka-data volume, port 42617, and the standard health check; they differ only in how the platform expects images to be pinned and how it generates the API-key secret.

The Coolify template (marketplace/coolify/revka.yaml) is a Compose service that targets the ghcr.io/kumihoio/revka:latest tag, so Coolify users pick up new versions on redeploy. Coolify generates a random API key for you via its ${SERVICE_PASSWORD_APIKEY} helper.

  1. In Coolify, create a New Service → Compose (or use the community template once merged).
  2. Set the PROVIDER environment variable (default openrouter) and supply or accept the auto-generated API_KEY.
  3. Deploy. Port 42617 is exposed; resources default to 0.5–2 CPU and 32M–512M memory.

The Dokploy blueprint (marketplace/dokploy/blueprints/revka/) requires a pinned image — Dokploy does not allow the latest tag — so it references a CalVer tag such as ghcr.io/kumihoio/revka:2026.4.21. The blueprint ships a docker-compose.yml and a template.toml that generates a domain and a 64-character API key.

[variables]
main_domain = "${domain}"
api_key = "${password:64}"
[config]
env = [
"API_KEY=${api_key}",
"PROVIDER=openrouter",
"REVKA_ALLOW_PUBLIC_BIND=true",
"REVKA_GATEWAY_PORT=42617"
]
[[config.domains]]
serviceName = "revka"
port = 42617
host = "${main_domain}"
  1. In Dokploy, open Templates and search for “Revka” (once the listing is merged).
  2. Set API_KEY (or accept the generated one) and PROVIDER.
  3. Deploy. Dokploy assigns the generated domain to the service on port 42617.

The EasyPanel template (marketplace/easypanel/) also requires a pinned image and exposes a schema-driven form. Its meta.yaml defines four fields:

FieldDefaultMeaning
appServiceNamerevkaService name within EasyPanel.
appServiceImageghcr.io/kumihoio/revka:2026.4.21Pinned image tag.
apiKeyemptyLLM provider API key.
provideropenrouterOne of openrouter, openai, anthropic, ollama.
  1. In EasyPanel, go to Apps → Browse Templates → “Revka”.
  2. Fill in the API key and select a provider.
  3. Deploy. After deployment, the gateway is reachable on port 42617.

A GitHub Actions workflow (marketplace/sync-marketplace-templates.yml) keeps the three listings current. After each stable release — it runs once the Docker images are built and pushed to GHCR — it opens pull requests to the Coolify, Dokploy, and EasyPanel upstream repositories with the new image tag.

  • Input: release_tag — the CalVer tag (e.g. 2026.4.21).
  • Secret: MARKETPLACE_PAT — a GitHub token with repo + workflow scopes that can push to the KumihoIO forks of the three upstream repos and open PRs.
  • Trigger: automatic from the stable-release pipeline after the Docker job; manual dispatch is also available.

The version handling differs per platform: Coolify uses :latest, so it gets updates automatically on redeploy and the workflow does not need to bump its tag, while Dokploy and EasyPanel require pinned tags, so the workflow rewrites their image tag (and EasyPanel’s changelog) each release. The very first listing for each platform must be submitted as a manual PR before auto-sync takes over.