chmonitor
Deployment

Self-host (Node / standalone)

Run chmonitor on any server using Node.js or Bun — bare VMs, dedicated servers, or any environment where you manage the process directly.

Run chmonitor on any server using Node.js or Bun — best for bare VMs, dedicated servers, or any environment where you manage the process directly.

Prerequisites

  • Bun installed on the server.
  • A reachable ClickHouse endpoint with a monitoring user.
  • A process supervisor (systemd) and, optionally, a reverse proxy (nginx).

Setup

Clone and install

git clone https://github.com/chmonitor/chmonitor.git
cd clickhouse-monitoring
bun install

Set credentials

export CLICKHOUSE_HOST='https://clickhouse.example.com:8443'
export CLICKHOUSE_USER='monitoring'
export CLICKHOUSE_PASSWORD='change-me'

Build and start

bun run build
bun run start

Open http://localhost:3000.

Using .env

Put env vars in .env.local in the project root (Next.js) or .env for the TanStack app. Never commit credentials.

cat <<'EOF' > .env.local
CLICKHOUSE_HOST=https://clickhouse.example.com:8443
CLICKHOUSE_USER=monitoring
CLICKHOUSE_PASSWORD=change-me
CLICKHOUSE_NAME=prod
CLICKHOUSE_MAX_EXECUTION_TIME=30
CLICKHOUSE_TZ=UTC
EOF

bun run start

Verify

curl -sf http://localhost:3000/api/healthz && echo OK

Configure

ClickHouse connection

VariableDefaultDescription
CLICKHOUSE_HOST— (required)ClickHouse URL(s), comma-separated
CLICKHOUSE_USERdefaultUsername(s), same count as HOST
CLICKHOUSE_PASSWORD""Password(s), same count as HOST
CLICKHOUSE_NAMEFriendly label(s) for host switcher

Multiple hosts

CLICKHOUSE_HOST defines the host count. CLICKHOUSE_USER and CLICKHOUSE_PASSWORD may be a single value (applied to all hosts) or one value per host position. CLICKHOUSE_NAME is optional:

export CLICKHOUSE_HOST='https://ch1:8443,https://ch2:8443'
export CLICKHOUSE_USER='monitoring,monitoring'
export CLICKHOUSE_PASSWORD='pass1,pass2'
export CLICKHOUSE_NAME='shard-1,shard-2'

Query / pool tuning

VariableDefaultDescription
CLICKHOUSE_MAX_EXECUTION_TIME60Query timeout in seconds
CLICKHOUSE_TZTimezone for queries
CLICKHOUSE_DATABASEsystemDefault database
CLICKHOUSE_POOL_SIZE10Connection pool size
CLICKHOUSE_POOL_TIMEOUT300000Pool acquire timeout (ms)
CLICKHOUSE_POOL_CLEANUP_INTERVAL60000Pool cleanup interval (ms)

Feature permissions

export CHM_DISABLED_FEATURES='peerdb,actions'
export CHM_AUTH_REQUIRED_FEATURES='agent,settings,mcp'
export CHM_FEATURE_AGENT_ACCESS='authenticated'
export CHM_FEATURE_SETTINGS_ENABLED='false'
export CHM_CONFIG_FILE='/etc/chmonitor/config.toml'
[features.agent]
access = "authenticated"

[features.settings]
enabled = false

[features.mcp]
access = "authenticated"

Feature ids: overview, agent, insights, health, queries, tables, metrics, dashboard, security, logs, settings, cluster, operations, actions, mcp, docs, about.

Authentication

export CHM_AUTH_PROVIDER='none'
export CHM_API_KEY_SECRET='a-long-random-secret'
# Build with the canonical names (the client VITE_* derive from them):
# CHM_AUTH_PROVIDER=clerk CHM_CLERK_PUBLISHABLE_KEY=pk_live_... bun run build
export CHM_AUTH_PROVIDER='clerk'   # also needed at runtime
export CLERK_SECRET_KEY='sk_live_...'

Set CHM_AUTH_PROVIDER / CHM_CLERK_PUBLISHABLE_KEY before bun run build (the client values are inlined at build time), then keep CHM_AUTH_PROVIDER and the CLERK_SECRET_KEY secret in the runtime env.

Cloudflare Access:

export CHM_AUTH_PROVIDER='proxy'
export CHM_CF_ACCESS_TEAM_DOMAIN='https://yourteam.cloudflareaccess.com'
export CHM_CF_ACCESS_AUD='<audience-tag>'

nginx. Put chmonitor behind nginx. Configure nginx to set an auth header and pass the secret.

export CHM_AUTH_PROVIDER='proxy'
export CHM_PROXY_AUTH_HEADER='X-Forwarded-User'
export CHM_PROXY_AUTH_SECRET='a-long-random-secret'

Without CHM_PROXY_AUTH_SECRET, the trusted-header provider is disabled. See Authentication.

Example nginx config:

upstream chmonitor {
    server 127.0.0.1:3000;
}

server {
    listen 443 ssl;
    server_name monitor.example.com;

    # Set after your auth module populates $remote_user
    proxy_set_header X-Forwarded-User $remote_user;
    proxy_set_header X-Chm-Proxy-Secret "$chm_proxy_secret";  # inject via env/secrets manager, not hardcoded

    location / {
        proxy_pass http://chmonitor;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

AI agent

export LLM_API_KEY='sk-...'
export LLM_API_BASE='https://openrouter.ai/api/v1'
export LLM_MODEL='openrouter/free'
export AGENT_API_TOKEN='bearer-token-for-agent-api'
export AGENT_ENABLE_CONTROL_TOOLS='false'

Keep LLM_API_KEY server-side — never set it as a VITE_* variable.

Conversation store

Default: browser localStorage. No server config needed.

Server-side persistence requires CHM_FEATURE_CONVERSATION_DB=true set at build time (its client VITE_FEATURE_CONVERSATION_DB is derived at build). Build with this flag set. At runtime, select the backend:

export CONVERSATION_STORE_BACKEND='postgres'
export DATABASE_URL='postgresql://user:pass@host:5432/dbname'
export CONVERSATION_STORE_BACKEND='agentstate'
export AGENTSTATE_API_KEY='as_live_...'

D1 and Durable Object stores are Cloudflare-only.

Health alerting

The health sweep runs at GET /api/cron/health-sweep. Call it from a cron job:

export HEALTH_ALERT_ENABLED='true'
export HEALTH_ALERT_WEBHOOK_URL='https://hooks.slack.com/services/...'
export HEALTH_ALERT_MIN_SEVERITY='warning'
export CRON_SECRET='a-random-secret'

Add a crontab entry:

*/5 * * * * curl -sf -H "Authorization: Bearer <CRON_SECRET>" http://localhost:3000/api/cron/health-sweep

Branding

Branding vars are inlined at build time:

VITE_TITLE_SHORT=MyCluster VITE_LOGO=/logo.png bun run build

These are inlined at build time — set them before running bun run build.

systemd unit

[Unit]
Description=chmonitor ClickHouse dashboard
After=network.target

[Service]
Type=simple
User=chmonitor
WorkingDirectory=/opt/chmonitor
EnvironmentFile=/etc/chmonitor/env
ExecStart=/usr/local/bin/bun run start
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

Create /etc/chmonitor/env:

CLICKHOUSE_HOST=https://clickhouse.example.com:8443
CLICKHOUSE_USER=monitoring
CLICKHOUSE_PASSWORD=change-me
CHM_AUTH_PROVIDER=none

Enable and start:

systemctl enable --now chmonitor

Upgrading

cd /opt/chmonitor
git pull
bun install
bun run build
systemctl restart chmonitor

For breaking changes between major versions, see Migrating to v0.3.

On this page