Skip to content

ClickHouse

events:
driver: clickhouse
dsn: clickhouse://reader:${CLICKHOUSE_PASSWORD}@clickhouse:9440/events?secure=true&compress=lz4
allowed_users: [analysts]
timeout: 15s
ParameterMeaning
secureenable TLS (default port 9440 when secure, 9000 when not)
compresslz4 (default), zstd, none
dial_timeoutconnection timeout
read_timeoutper-query read timeout
CREATE USER plotpress_reader IDENTIFIED WITH sha256_password BY '...';
GRANT SELECT ON events.* TO plotpress_reader;

For row-policy or quota-aware setups, layer those on the user before granting.

ClickHouse’s regular VIEWs, MATERIALIZED VIEWs with a target table, and ordinary MergeTree tables all work. PlotPress runs:

SELECT * FROM monthly_revenue WHERE year = {year:UInt16}

Notice the {name:Type} placeholder syntax — that’s ClickHouse’s native parameter form. PlotPress rewrites :name into it at execution time, so authors continue using :year.

-- queries/monthly_revenue.sql
-- @param year int default 2026
select
toStartOfMonth(invoice_date) as month,
sum(amount) as revenue,
currency
from events.invoices
where toYear(invoice_date) = :year
group by 1, 3
order by 1
settings max_execution_time = 10;
  • JSON columns. ClickHouse JSON columns expose subcolumns via dot paths (payload.user.id) and .:Type for typed access. Bracket subscripts (payload['user']) do not work in views; build the path in the view itself.
  • Nullable types stringify nulls as 'ᴺᵁᴸᴸ' if you toString(null) on the SQL side. Cast/coalesce in the view, not in the dashboard.
  • Wide tables. Plot blocks see whatever the view returns; project the columns you need in the view rather than SELECT * from a 200-column table.
  • Settings. Use SETTINGS max_execution_time = ... inside the view/query as a backstop in addition to the connection’s timeout.