Deployment

Deployment

This guide covers production deployment of ClickLens for operators and DevOps teams.

1. Environment Variables

ClickLens is configured entirely through environment variables. Here's the complete reference:

1.1. Required Variables

VariableDescriptionExample
CLICKHOUSE_HOSTClickHouse server hostnameclickhouse.example.com
LENS_USERService account username for metadata querieslensuser
LENS_PASSWORD""Password for the Lens service account
SESSION_SECRETEncryption key for session cookies (≥32 chars)your-32-character-secret-key...

1.2. Optional Variables

VariableDefaultDescription
CLICKHOUSE_PORT8123 (HTTP) or 8443 (HTTPS)ClickHouse HTTP interface port
CLICKHOUSE_SECUREfalseSet to true to use HTTPS
CLICKHOUSE_VERIFYtrueSet to false to skip SSL certificate verification (for self-signed certs)
NODE_ENVdevelopmentSet to production for production deployments
DISABLE_SECURE_COOKIESfalseSet to true if running behind a non-HTTPS proxy
NEXT_PUBLIC_APP_VERSIONFrom Git (tag + commit hash)Override the displayed app version. By default, falls back to Git metadata.
CLICKLENS_BASE_PATH(empty)Sub-path the app is served under (e.g. /clicklens for https://host/clicklens/…). Must be set at build time (see callout below). A trailing / is stripped.
CLICKLENS_TRAILING_SLASH(empty)Set to true or 1 to use trailing slashes on app routes (Next.js trailingSlash (opens in a new tab)). Any other value (or unset) is treated as false.

Sub-path (CLICKLENS_BASE_PATH) is a build-time setting. Set it in the same environment you use for next build, your Docker docker build, or your CI image build. It is compiled into the client as NEXT_PUBLIC_BASE_PATH so the browser can load /api/… routes and static assets under the same prefix. Your reverse proxy (or Kubernetes ingress) path must match this value (e.g. both use /clicklens). The production sample below keeps the app at the site root; for a sub-path, see §5.2 below.

⚠️

Security: The SESSION_SECRET must be at least 32 characters. In production, generate a strong secret with: bash openssl rand -base64 32

2. ClickHouse Server Requirements

2.1. Version Compatibility

ClickLens is tested with ClickHouse version 21.8 and later. Some features require newer versions:

FeatureMinimum VersionNotes
Core functionality21.8+Basic queries, schema browsing
Query cache stats23.1+system.query_cache table
Time-series charts21.8+Requires system.metric_log enabled
Cluster monitoring21.8+Uses clusterAllReplicas() function

2.2. Required Server Configuration

Ensure the ClickHouse HTTP interface is enabled in your config.xml:

<clickhouse>
    <!-- Required: Enable HTTP interface -->
    <http_port>8123</http_port>
    <!-- Or for HTTPS -->
    <https_port>8443</https_port>
 
    <!-- Recommended: Enable access management for RBAC -->
    <access_control_improvements>
        <users_without_row_policies_can_read_rows>true</users_without_row_policies_can_read_rows>
    </access_control_improvements>
</clickhouse>

For user management features, ensure SQL-driven access control is enabled in users.xml:

<clickhouse>
    <users>
        <default>
            <access_management>1</access_management>
        </default>
    </users>
</clickhouse>

2.3. Cluster Configuration

For cluster monitoring, ClickLens uses the clusterAllReplicas() function to aggregate data across nodes. Ensure your cluster is properly configured in config.xml:

<clickhouse>
    <remote_servers>
        <my_cluster>
            <shard>
                <replica>
                    <host>node1.example.com</host>
                    <port>9000</port>
                </replica>
                <replica>
                    <host>node2.example.com</host>
                    <port>9000</port>
                </replica>
            </shard>
        </my_cluster>
    </remote_servers>
</clickhouse>

ClickLens automatically detects clusters from system.clusters and displays cluster-aware metrics when available.

2.4. Sample .env.local File

# ClickHouse Server Connection (required)
CLICKHOUSE_HOST=localhost
CLICKHOUSE_PORT=8123
CLICKHOUSE_SECURE=false
CLICKHOUSE_VERIFY=true
 
# Lens Service User (for metadata queries)
LENS_USER=lensuser
LENS_PASSWORD=your-password
 
# Session Secret (required for production)
SESSION_SECRET=complex_password_at_least_32_characters_long_for_security
 
# Optional — Next.js public URL (build-time: use when running `next build` / custom Docker build)
# CLICKLENS_BASE_PATH=/clicklens
# CLICKLENS_TRAILING_SLASH=false

3. Docker Deployment

3.1. Quick Start

docker run -d \
  --name clicklens \
  -p 3000:3000 \
  -e CLICKHOUSE_HOST=your-clickhouse-host \
  -e CLICKHOUSE_PORT=8123 \
  -e LENS_USER=lensuser \
  -e LENS_PASSWORD=your-password \
  -e SESSION_SECRET=your-32-character-secret-key-here \
  -e NODE_ENV=production \
  ghcr.io/ntk148v/clicklens:latest

3.2. Docker Compose

version: "3.8"
 
services:
  clicklens:
    image: ghcr.io/ntk148v/clicklens:latest
    ports:
      - "3000:3000"
    environment:
      - CLICKHOUSE_HOST=clickhouse
      - CLICKHOUSE_PORT=8123
      - LENS_USER=lensuser
      - LENS_PASSWORD=your-password
      - SESSION_SECRET=your-32-character-secret-key-here
      - NODE_ENV=production
      - DISABLE_SECURE_COOKIES=true
    restart: unless-stopped
    depends_on:
      - clickhouse
 
  clickhouse:
    image: clickhouse/clickhouse-server:latest
    ports:
      - "8123:8123"
      - "9000:9000"
    volumes:
      - clickhouse-data:/var/lib/clickhouse
 
volumes:
  clickhouse-data:

3.3. Docker Image Details

  • Base Image: node:20-alpine (runtime)
  • Build Image: oven/bun:latest (build stage)
  • Exposed Port: 3000
  • Output: Next.js standalone build (optimized for containers)

3.4. Building Custom Image

docker build \
  --build-arg APP_VERSION=1.0.0 \
  -t clicklens:custom \
  .

CLICKLENS_BASE_PATH and CLICKLENS_TRAILING_SLASH are read when next build runs inside the image. The stock Dockerfile in this repo only wires APP_VERSION; to bake a sub-path or trailing-slash setting into a custom image, add ARG / ENV lines before RUN bun run build in the builder stage, then pass them at docker build time, for example:

# In Dockerfile — builder stage, before `RUN bun run build`:
ARG CLICKLENS_BASE_PATH=
ARG CLICKLENS_TRAILING_SLASH=
ENV CLICKLENS_BASE_PATH=$CLICKLENS_BASE_PATH
ENV CLICKLENS_TRAILING_SLASH=$CLICKLENS_TRAILING_SLASH
docker build \
  -t clicklens:subpath \
  --build-arg APP_VERSION=1.0.0 \
  --build-arg CLICKLENS_BASE_PATH=/clicklens \
  --build-arg CLICKLENS_TRAILING_SLASH=true \
  .

Pre-built images on the registry are normally built for the site root (/). Use a sub-path only when you build the image (or the static export) yourself with these variables set.

4. RBAC Configuration

ClickLens uses ClickHouse's native RBAC. To enable all features for a user, you can use the predefined Feature Roles.

4.1. Feature Roles

These are ClickHouse roles that ClickLens expects. Create them to simplify user permission management:

RolePurposeRequired Grants
clicklens_table_explorerBrowse databases, tables, partsSHOW DATABASES, SHOW TABLES, SHOW COLUMNS, SELECT ON system.{tables,columns,parts,replicas}
clicklens_query_monitorView/kill queries, analyze performanceKILL QUERY, SELECT ON system.{processes,query_log,query_cache}
clicklens_cluster_monitorView cluster health and metricsSELECT ON system.{clusters,replicas,metrics,events,disks,settings}
clicklens_user_adminFull user and role managementACCESS MANAGEMENT ON *.*
clicklens_table_adminDDL operations (TRUNCATE, OPTIMIZE)TRUNCATE, OPTIMIZE ON *.*
clicklens_settings_adminView system settingsSELECT ON system.{settings,server_settings}

4.2. Creating Feature Roles

-- Table Explorer Role
CREATE ROLE IF NOT EXISTS clicklens_table_explorer;
GRANT SHOW DATABASES ON *.* TO clicklens_table_explorer;
GRANT SHOW TABLES ON *.* TO clicklens_table_explorer;
GRANT SHOW COLUMNS ON *.* TO clicklens_table_explorer;
GRANT SELECT ON system.tables TO clicklens_table_explorer;
GRANT SELECT ON system.columns TO clicklens_table_explorer;
GRANT SELECT ON system.databases TO clicklens_table_explorer;
GRANT SELECT ON system.parts TO clicklens_table_explorer;
GRANT SELECT ON system.replicas TO clicklens_table_explorer;
GRANT SELECT ON system.mutations TO clicklens_table_explorer;
GRANT SELECT ON system.merges TO clicklens_table_explorer;
 
-- Query Monitor Role
CREATE ROLE IF NOT EXISTS clicklens_query_monitor;
GRANT KILL QUERY ON *.* TO clicklens_query_monitor;
GRANT SELECT ON system.processes TO clicklens_query_monitor;
GRANT SELECT ON system.query_log TO clicklens_query_monitor;
GRANT SELECT ON system.query_cache TO clicklens_query_monitor;
 
-- Cluster Monitor Role
CREATE ROLE IF NOT EXISTS clicklens_cluster_monitor;
GRANT SELECT ON system.clusters TO clicklens_cluster_monitor;
GRANT SELECT ON system.replicas TO clicklens_cluster_monitor;
GRANT SELECT ON system.replication_queue TO clicklens_cluster_monitor;
GRANT SELECT ON system.metrics TO clicklens_cluster_monitor;
GRANT SELECT ON system.events TO clicklens_cluster_monitor;
GRANT SELECT ON system.asynchronous_metrics TO clicklens_cluster_monitor;
GRANT SELECT ON system.settings TO clicklens_cluster_monitor;
GRANT SELECT ON system.disks TO clicklens_cluster_monitor;
GRANT SELECT ON system.parts TO clicklens_cluster_monitor;
 
-- User Admin Role
CREATE ROLE IF NOT EXISTS clicklens_user_admin;
GRANT ACCESS MANAGEMENT ON *.* TO clicklens_user_admin;
 
-- Settings Viewer Role
CREATE ROLE IF NOT EXISTS clicklens_settings_admin;
GRANT SELECT ON system.settings TO clicklens_settings_admin;
GRANT SELECT ON system.server_settings TO clicklens_settings_admin;

4.3. Creating the Lens Service User

The Lens user needs minimal read access for metadata queries:

CREATE USER IF NOT EXISTS lensuser IDENTIFIED BY 'your-password';
 
-- Grant read access to system tables
GRANT SELECT ON system.* TO lensuser;
 
-- Grant schema introspection
GRANT SHOW DATABASES ON *.* TO lensuser;
GRANT SHOW TABLES ON *.* TO lensuser;
GRANT SHOW COLUMNS ON *.* TO lensuser;
GRANT SHOW DICTIONARIES ON *.* TO lensuser;

4.4. Creating Application Users

Example: Create a developer user with full ClickLens access:

CREATE USER IF NOT EXISTS developer IDENTIFIED BY 'password';
 
-- Grant all feature roles
GRANT clicklens_table_explorer TO developer;
GRANT clicklens_query_monitor TO developer;
GRANT clicklens_cluster_monitor TO developer;
GRANT clicklens_settings_admin TO developer;
 
-- Grant data access
GRANT SELECT ON mydb.* TO developer;
GRANT INSERT ON mydb.* TO developer;

Example: Create a read-only analyst user:

CREATE USER IF NOT EXISTS analyst IDENTIFIED BY 'password';
 
-- Limited feature access
GRANT clicklens_table_explorer TO analyst;
GRANT clicklens_cluster_monitor TO analyst;
 
-- Read-only data access
GRANT SELECT ON analytics.* TO analyst;

5. Reverse Proxy Configuration

5.1. Nginx

server {
    listen 80;
    server_name clicklens.example.com;
 
    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        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;
        proxy_cache_bypass $http_upgrade;
    }
}

If your proxy terminates SSL (HTTPS), set DISABLE_SECURE_COOKIES=true so session cookies work over the HTTP connection between proxy and app.

5.2. Nginx: serving ClickLens under a sub-path

If the UI is not at the site root, set CLICKLENS_BASE_PATH to the same prefix when you build the app (e.g. /clicklens), and route that prefix to the container. Example: app listens on localhost:3000 with CLICKLENS_BASE_PATH=/clicklens:

server {
    listen 80;
    server_name clicklens.example.com;
 
    location /clicklens/ {
        proxy_pass http://127.0.0.1:3000;
        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;
    }
}

The Next.js server will serve pages and /_next assets under /clicklens/… when CLICKLENS_BASE_PATH is set; do not strip the prefix in Nginx in a way that differs from that value.

6. Health Checks

ClickLens provides a health check endpoint.

At the site root (no CLICKLENS_BASE_PATH):

curl http://localhost:3000/api/clickhouse/ping

With CLICKLENS_BASE_PATH (e.g. /clicklens), prefix the same path on the public URL the app is mounted at:

curl http://localhost:3000/clicklens/api/clickhouse/ping

Response (healthy):

{ "success": true }

Use this endpoint for:

  • Kubernetes liveness/readiness probes
  • Load balancer health checks
  • Monitoring systems

7. Troubleshooting

7.1. Common Issues

"Session expired" errors:

  • Ensure SESSION_SECRET is consistent across restarts
  • Check cookie settings if behind a proxy (DISABLE_SECURE_COOKIES)

"Permission denied" for features:

  • Check the user's ClickHouse grants with SHOW GRANTS FOR user
  • Ensure the required system tables are accessible

Connection refused to ClickHouse:

  • Verify CLICKHOUSE_HOST and CLICKHOUSE_PORT are correct
  • Ensure the HTTP interface is enabled (not just native protocol)
  • Check firewall rules

SSL certificate errors:

  • Set CLICKHOUSE_VERIFY=false for self-signed certificates
  • Or install the CA certificate in the container

404 on pages, assets, or /api/… when using a sub-path:

  • CLICKLENS_BASE_PATH must be set when you build the app, and the same prefix must be used in your reverse proxy (see §5.2).
  • Health check URL must include the prefix, e.g. …/clicklens/api/clickhouse/ping (see §6).