CH-UICH-UI

Deployment

Production deployment for CH-UI — self-hosted server or cloud with connectors

CH-UI can be deployed in two ways:

  • Cloud (managed) — use CH-UI Cloud. No server to deploy. You only run ch-ui connect next to each ClickHouse instance and manage everything from the Console.
  • Self-hosted — deploy the full CH-UI server yourself (this guide).

Cloud Deployment

With CH-UI Cloud, the server infrastructure is managed for you:

  1. Sign up at console.ch-ui.com
  2. Create an organization
  3. Add connections from the Console UI
  4. Run ch-ui connect on each ClickHouse host with the provided token
  5. Access your workspace at your-org.ch-ui.com

You still need to install the ch-ui binary on ClickHouse hosts for the connector agent. See Installation and Console for details.

Self-Hosted Deployment

This section covers the recommended self-hosted production topology:

  • VM2: ch-ui server (web app/API/gateway)
  • VM1..N: ch-ui connect next to each ClickHouse instance

Topology

Browser

  • SQL workspace
  • Governance
  • Brain UI
HTTPS

CH-UI Server

  • :3488 API + UI
  • Tunnel gateway
  • Scheduler + syncers
WSS

Connector Host

  • ch-ui connect
  • Outbound only

SQLite

  • Sessions, queries
  • Governance, chats, audit

ClickHouse

  • Your data
  • HTTP :8123

1) Start CH-UI Server (VM2)

Binary install:

ch-ui server --port 3488 --detach
ch-ui server status

Docker install (official image):

docker run -d \
  --name ch-ui-server \
  --restart unless-stopped \
  -p 3488:3488 \
  -v ch-ui-data:/app/data \
  -e APP_URL=https://ch-ui.yourcompany.com \
  -e APP_SECRET_KEY='replace-with-a-strong-secret' \
  ghcr.io/caioricciuti/ch-ui:latest

curl http://localhost:3488/health

Server lifecycle:

ch-ui server start --detach
ch-ui server status
ch-ui server restart
ch-ui server stop

Single instance: CH-UI stores state in a local SQLite database and keeps tunnel state in memory. Run one instance — do not scale it horizontally. Use a persistent volume for database_path and front it with a load balancer only for TLS/routing, not for multiple replicas.

Docker Compose (with ClickHouse)

The repository ships a docker-compose.yml that runs CH-UI plus a local ClickHouse:

docker compose up -d
# open http://localhost:3488

Set APP_SECRET_KEY to a strong random value in production.

Kubernetes (Helm)

A Helm chart is provided under deploy/helm/ch-ui:

helm install ch-ui ./deploy/helm/ch-ui \
  --set ingress.enabled=true \
  --set ingress.hosts[0].host=ch-ui.yourcompany.com

The chart runs a single replica with the Recreate strategy (never two pods on one database), a PersistentVolumeClaim for state, liveness/readiness probes against /health, and a generated APP_SECRET_KEY that stays stable across upgrades. See deploy/helm/ch-ui/values.yaml for all options.

Native TLS (no proxy)

To serve HTTPS without a reverse proxy, point CH-UI at a cert and key:

ch-ui server --port 3488 \
  # via env or server.yaml:
  # tls_cert_file: /etc/ch-ui/tls/server.crt
  # tls_key_file:  /etc/ch-ui/tls/server.key

2) Create Tunnel Connection + Token (VM2)

Use CLI on the server host (the machine where ch-ui.db lives):

ch-ui tunnel create --name "vm1-clickhouse" --url wss://your-ch-ui-domain/connect
ch-ui tunnel list
ch-ui tunnel show <connection-id>

If the server runs in Docker, run tunnel commands through the container:

docker exec -it ch-ui-server ch-ui tunnel create --name "vm1-clickhouse" --url wss://your-ch-ui-domain/connect
docker exec -it ch-ui-server ch-ui tunnel show <connection-id>

Copy the generated token (cht_...).

Optional UI path (Pro/Admin): create/manage additional connections in Admin.

3) Start Connector (VM1)

ch-ui connect \
  --url wss://your-ch-ui-domain/connect \
  --key cht_your_token \
  --clickhouse-url http://127.0.0.1:8123

If a stale session exists:

ch-ui connect --url wss://your-ch-ui-domain/connect --key cht_your_token --takeover

4) Run Connector As Service

ch-ui service install \
  --url wss://your-ch-ui-domain/connect \
  --key cht_your_token \
  --clickhouse-url http://127.0.0.1:8123

ch-ui service status
ch-ui service start

5) Reverse Proxy (Nginx)

Basic TLS proxy for UI/API and WebSocket tunnel:

server {
    listen 443 ssl http2;
    server_name ch-ui.yourcompany.com;

    location / {
        proxy_pass http://127.0.0.1:3488;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /connect {
        proxy_pass http://127.0.0.1:3488/connect;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_read_timeout 3600;
    }
}

6) Hardening Checklist

  • Set strong APP_SECRET_KEY (sourced from env or server.yaml, not the DB dir).
  • Set APP_URL to public HTTPS URL; terminate TLS (native or proxy).
  • Restrict ALLOWED_ORIGINS.
  • Keep database_path on persistent disk; run one instance only.
  • Run daily backups of ch-ui.db and back up APP_SECRET_KEY (without it, the encrypted credentials in the DB cannot be decrypted).
  • Enable service supervision for server and connect.
  • Scrape /metrics and forward audit events — see Monitoring & SIEM.

Backup and Restore

CH-UI state is stored in SQLite (database_path). Stop the server for a consistent copy, then back up the DB file and the secret key:

ch-ui server stop
cp /var/lib/ch-ui/ch-ui.db /var/backups/ch-ui-$(date +%F).db
# also preserve APP_SECRET_KEY (e.g. from /etc/ch-ui/server.yaml)
ch-ui server start --detach

Restore by replacing the DB file while the server is stopped, using the same APP_SECRET_KEY the backup was created with.

Troubleshooting

Address already in use

status says port in use but no PID: stop the legacy process once, then restart using the current binary.

ch-ui server status
ch-ui server stop

Connector invalid token

  • Regenerate token with ch-ui tunnel rotate <id> (or via Admin if Pro-enabled).
  • Restart connector with the new token.

Connector timeouts behind proxy

  • Validate /connect proxy upgrade headers (Upgrade, Connection: upgrade).
  • Set long read/send timeouts (e.g. proxy_read_timeout 3600).
  • Check firewall egress from the connector host.

Health check

curl http://localhost:3488/health

ClickHouse behind a load balancer

If your ClickHouse runs as multiple pods behind chproxy, HAProxy, or a Kubernetes ingress, CH-UI sends X-CH-UI-Session on every request — configure your LB to hash on that header for sticky routing. See ClickHouse Clusters & Load Balancers for chproxy/HAProxy/nginx examples.

On this page