Skip to Content
Palui Agent
Self-hosting

Self-hosting

Run Palui with Docker Compose as two services from one image, sharing one SQLite volume, configured by three keys in .env.

Palui runs as two Compose services built from one image. Both share a single data volume.

Messages arrive from three channels: WhatsApp, Telegram and the dashboard web chat. They flow into the runtime container, which holds the router, agent turns, the scheduler, the outbox and the Saklar supervisor. Next to it, the app container serves the dashboard, the REST API, authentication, the /health endpoint and database migrations. Both containers share one SQLite database on a single volume. Model providers, the recall memory service and MCP servers sit outside both containers and are reached over the network.

ServiceWhat it runs
appThe dashboard, the REST API, auth and /health. It's the only service that runs database migrations.
runtimeA plain Node process: channel sockets, agent turns, the scheduler, the outbox and the Saklar supervisor.

Long-lived sockets (WhatsApp, Telegram polling) live in the runtime so the app stays a normal web server. Both restart on a deploy. The durable inbox and outbox are what keep messages safe through that, see Upgrades.

compose.yaml

A trimmed example. The repository's compose.yaml is the real one, with the exact healthcheck commands.

services:
  app:
    build: .
    image: palui:latest
    env_file: .env
    environment:
      DATA_DIR: /data
      RUNTIME_URL: http://runtime:4891
    ports:
      - "127.0.0.1:4802:3000" # loopback only, your tunnel connects here
    volumes:
      - palui-data:/data
    stop_grace_period: 60s

  runtime:
    image: palui:latest
    pull_policy: never
    env_file: .env
    environment:
      DATA_DIR: /data
    volumes:
      - palui-data:/data
    depends_on:
      app:
        condition: service_healthy
    stop_grace_period: 60s
    # no ports: the control API stays on the compose network

volumes:
  palui-data:

The rules behind it:

  • Only app has build:. runtime uses the same palui:latest image with pull_policy: never, so Compose builds once and never tries to pull it from a registry.
  • runtime waits for app to be healthy, which means migrations have run. It refuses to start on a schema it doesn't know.
  • stop_grace_period: 60s on both. On SIGTERM the runtime drains: it stops taking new turns, finishes the current step and releases its outbox claims.
  • The runtime healthcheck is liveness only: the event loop responds, the database is open and the schema version is known. Connector state goes in the /health body, never in the exit code, so a WhatsApp outage can't fail a deploy.
  • app is bound to 127.0.0.1. The runtime's control port is never published.

Volume

One volume, mounted at /data:

PathWhat it holds
/data/palui.dbThe SQLite database (WAL mode): connectors, encrypted secrets, chats, reminders, jobs, everything
/data/wa-auth/The WhatsApp linked-device session
/data/backups/Pre-deploy snapshots

There are no JSON state files. Every write goes through a database transaction. Retention: tool call arguments and results are kept 30 days, media 14 days, events 90 days.

The three keys

.env holds exactly three keys. Every other credential is entered in the dashboard and stored encrypted in the database.

KeyWhat it is
MASTER_KEYEncrypts every connector secret (AES-256-GCM). Generate with openssl rand -base64 32.
BETTER_AUTH_SECRETSigns dashboard sessions. Generate the same way.
APP_URLThe public origin of the dashboard, like https://palui.example.com. Used for cookies, the passkey domain and invite links.

Keep a copy of MASTER_KEY

Store MASTER_KEY in your password manager. Every connector secret is encrypted with it. If you lose it, a restored backup can't decrypt any API key or bot token, and you'll have to enter them all again.

Logs

docker compose logs app
docker compose logs runtime

Check both when something's wrong. Channel, turn and scheduler problems show up in runtime. Logs are JSON lines tagged with run, chat and connector ids, with secrets redacted.

Next: Backups, Upgrades, Tunnel.

On this page