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 gateway, the gateway server only (webhooks and WebSockets). The gateway does not start channels, the heartbeat, or the scheduler; to get the full autonomous runtime, override the command to daemon (e.g. docker run ... ghcr.io/kumihoio/revka:latest daemon). 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, so the gateway is reachable from outside the container. Pairing is enabled by default (require_pairing = true), so first access requires completing the pairing flow.

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. Both published images leave this unset; the baked config value (openrouter) wins. Only the local-dev dev build target sets PROVIDER=ollama via ENV.
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.

The local-build Dockerfiles (Dockerfile and Dockerfile.debian) and the Compose file define this liveness probe. The published GHCR images (:latest and :debian) have no built-in HEALTHCHECK — the Compose-level healthcheck block (or docker run --health-cmd) is what provides liveness when using pulled images:

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 (.github/workflows/sync-marketplace-templates.yml) keeps the 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.

Currently only the Dokploy sync job is active. The Coolify job is disabled (if: false) because its upstream blocked the bot account (disabled 2026-05-08), and the EasyPanel job is also disabled (if: false) at the maintainer’s request (disabled 2026-05-01). Coolify is not being auto-PR’d. The Dokploy job rewrites the pinned image tag each release. The very first listing for each active platform must be submitted as a manual PR before auto-sync takes over.