Deployment guide

Hosting ClickHouse on a VPS

Deploy on a VPS Cloud →

Tutorial

Databases

Hosting ClickHouse on a VPS

ClickHouse is a column-oriented analytical database capable of aggregating billions of rows in a few milliseconds. It's the reference tool for reporting, log analysis, and real-time dashboards, and it self-hosts very well on a properly sized VPS. Here's how to deploy it with Docker, secure it, and configure it for optimal performance.

9 min read6 steps
Contents· Why self-host ClickHouse on a VPS1/10
  1. 01Why self-host ClickHouse on a VPS
  2. 02Concrete benefits of self-hosting ClickHouse
  3. 03Hardware and software requirements
  4. 04Deploy ClickHouse step by step
  5. 05Post-installation configuration: users, database, and compression
  6. 06Concrete use cases: application logs and time-series metrics
  7. 07Performance: batch inserts and parts monitoring
  8. 08Troubleshooting common errors
  9. 09Install it in one click from the ServOrbit Marketplace
  10. 10ClickHouse vs Elasticsearch for logs: which database to choose?

Why self-host ClickHouse on a VPS

ClickHouse excels at analytical queries (OLAP): thanks to its column storage and aggressive compression, it can scan terabytes on modest hardware where a classic relational database would collapse. Self-hosting on a VPS saves you the per-query costs of managed offerings (ClickHouse Cloud, BigQuery) and gives you full control over schema, table engines, and retention. It is especially relevant for ingesting your own application logs, product metrics, or analytics events without sending your data to a third party. Since ClickHouse often compresses data by a factor of 5 to 10, a VPS with a reasonable SSD can store hundreds of millions of events. You also keep control over latency: your dashboards query an instance close to your users rather than a remote service.

Concrete benefits of self-hosting ClickHouse

  • Extremely fast analytical queries: aggregations over hundreds of millions of rows in under a second.
  • Column compression of 5x to 10x that drastically reduces the storage cost of your logs and events.
  • Massive ingestion: hundreds of thousands of rows per second on a single well-tuned node.
  • Compatible with SQL and many connectors (Grafana, Metabase, HTTP and native clients).
  • Analytical sovereignty: your logs and product metrics stay on your infrastructure.
  • No per-query billing: a fixed VPS cost for high analytical volume.

Hardware and software requirements

ClickHouse loves RAM and fast storage. The absolute minimum is 2 GB of RAM for a test or development environment, but 4 GB is the recommended floor for light production workloads, and 8 GB provides a much healthier margin for large aggregations and mark cache. Below that threshold, you will encounter OOM errors as soon as you query several million rows. On the CPU side, 2 vCPU allow ClickHouse to exploit parallelism on simple queries; 4 vCPU become necessary for aggregations or joins on dense tables. Disk is the most critical factor: an NVMe SSD is mandatory — ClickHouse generates intense I/O during partition merges and scan queries; a spinning HDD immediately caps performance even on small volumes. Plan for at least 50 GB to start, and scale according to your retention knowing that compression reduces the actual footprint by a factor of 5 to 10. Two ports matter: port 8123 for the HTTP interface (used by Grafana, Metabase, curl) and port 9000 for the native TCP protocol (used by Go, Python, or clickhouse-client). Never expose port 9000 directly to the internet without network filtering.

Deploy ClickHouse step by step

  1. Prepare the VPS

    Install Docker and raise system limits (ulimit -n to 262144) because ClickHouse opens many file descriptors. Disable excessive swap and verify the disk is NVMe SSD.

  2. Launch ClickHouse with Docker

    Use the official clickhouse/clickhouse-server image. Mount three persistent volumes: /var/lib/clickhouse (data), /var/log/clickhouse-server (logs), and a config folder /etc/clickhouse-server/config.d. Add ulimits: nofile: 262144 to the compose file.

  3. Create a user and password

    Never leave the default user without a password. Create a users.d/custom.xml file with a dedicated user, a hashed password (echo -n password | sha256sum) and quotas. Restrict <networks> to authorized IPs.

  4. Tune memory and profile

    In config.d, set max_server_memory_usage_to_ram_ratio to 0.8 and define a per-query max_memory_usage suited to your VPS to avoid OOM. Enable partition retention with a TTL on your tables (TTL date + INTERVAL 90 DAY).

  5. Expose the HTTP interface behind a reverse proxy

    ClickHouse exposes an HTTP API on port 8123. Place Nginx or Caddy in front for TLS and additional authentication, and do not open the native port 9000 to the public. Reserve native access to the internal network or via SSH tunnel.

  6. Connect a visualization tool

    Connect Grafana or Metabase to the HTTPS endpoint to build your dashboards. Create tables with the MergeTree engine, choose a relevant ORDER BY, and partition by month for efficient queries and purging.

Post-installation configuration: users, database, and compression

Once ClickHouse is running, three configuration steps consolidate security and performance. First, create a dedicated database via CREATE DATABASE analytics; in the SQL console or via the HTTP API. Avoid writing directly to the default database — a clean namespace simplifies backups, quotas, and permission management. Next, define your users in a users.d/production.xml file rather than in the main users.xml: this split allows you to reload users without restarting the server (SYSTEM RELOAD USERS). For each user, specify a profile, a quota, and a network list. Finally, enable LZ4 compression in config.d/compression.xml: it is ClickHouse's default codec for columns, but defining it explicitly ensures it applies to all tables including those created before the change. LZ4 offers a good balance between compression ratio and decompression speed at read time — for even more compact storage at the cost of slightly higher CPU usage, ZSTD (levels 1 to 3) is a viable alternative for rarely queried columns.

Concrete use cases: application logs and time-series metrics

ClickHouse is a natural alternative to Elasticsearch for analytical log pipelines. Where Elasticsearch indexes every field of every JSON document and requires significant JVM memory, ClickHouse stores each column separately and compresses repetitive values (log level, hostname, service) extremely efficiently. For a log pipeline, create a logs table with a MergeTree engine partitioned by day (PARTITION BY toDate(timestamp)) and sorted by (service, timestamp). Ingest via the HTTP API in batch INSERT (see the performance section below) or via Vector.dev, Fluent Bit, or Filebeat, all of which have a native ClickHouse output. For time-series metrics, the MergeTree engine with an ORDER BY (metric_name, timestamp) is suitable for moderate volumes. At larger scale, the AggregatingMergeTree engine pre-aggregates data during merges, further reducing the footprint and accelerating trend queries. A typical query over 500 million rows filtered by a time range and grouped by service runs in 2 to 8 seconds on a 4 vCPU / 8 GB VPS depending on the index — often 10 to significantly faster than OLTP databases for analytics than a standard relational database.

Performance: batch inserts and parts monitoring

ClickHouse's write behavior is fundamentally different from OLTP databases. Each INSERT creates a new part on disk, and ClickHouse merges these parts in the background. If you insert row by row or in small batches (fewer than 1,000 rows), you flood the system with small parts — merges can't keep up, performance degrades, and you get the Too many parts error (beyond 300 active parts per partition). The golden rule is to insert in batches of 10,000 to 100,000 rows minimum, and space insertions at least one second apart if inserting continuously from multiple sources. From a Python script, use clickhouse-driver or clickhouse-connect with insert_block_size to control block size. From Node.js, the official @clickhouse/client exposes a streaming API that builds batches automatically. Monitor part health in real time with SELECT table, count() as parts, sum(rows) FROM system.parts WHERE active GROUP BY table ORDER BY parts DESC — if a table exceeds 100 active parts, that is a warning sign.

Troubleshooting common errors

Three errors come up regularly when deploying ClickHouse on a VPS. Too many parts: you are inserting in batches that are too small; group your inserts, add an application-level buffer, or use the Buffer engine as an intermediate table that automatically flushes to the main table. OOM error (Killed, Memory limit exceeded): the query consumed more RAM than max_memory_usage allows. Increase this parameter in the user profile, add a LIMIT clause to exploratory queries, or move to a VPS with more RAM. A query without a filter on a table with billions of rows can consume several GB in one second. Permission denied on /var/lib/clickhouse: the clickhouse system user (UID 101 in the official Docker image) does not have write rights on the mounted volume. Before the first start, run chown -R 101:101 /your/path/clickhouse-data on the host. If ClickHouse has started but is not responding on port 8123, check logs with docker logs <container-name> — the permission error is explicitly shown there.

Install it in one click from the ServOrbit Marketplace

Everything above can be done manually, and it is useful to know how. If you prefer not to, ClickHouse is listed on the ServOrbit Marketplace and installs on your VPS in one click. The template deploys the pinned 26.3 LTS version, mounts persistent volumes for data and logs, raises the file descriptor limit to 262144 as ClickHouse requires, and creates an analytics database on first boot. Most importantly, it closes the most common trap of manual deployment: the upstream image ships a default account with no password, and the installation removes it at startup and replaces it with an admin account whose password is generated for your server and shown in your client area. Only port 8123 is published, and only on the loopback interface: attach a domain and the built-in SQL console responds at https://<your-domain>/play with its TLS certificate; without a domain, use a tunnel ssh -L 8123:127.0.0.1:8123 root@<vps-ip> then http://localhost:8123/play.

ClickHouse vs Elasticsearch for logs: which database to choose?

Scroll the table

Backendabverdict
Storage per GB of logsClickHouseElasticsearchClickHouse (5-10x compression vs 1-2x)
Analytical queries (aggregates, trends)Seconds on 500M rowsMinutes or degradationClickHouse
Full-text search in messagesLimited (LIKE, token BF)Native and performantElasticsearch
RAM required to start2 GB minimum4-8 GB minimum (JVM)ClickHouse
Real-time ingestion (streaming)Batch recommendedNative and optimizedElasticsearch
Grafana / BI connectorsNative ClickHouse pluginElasticsearch pluginEquivalent

The choice of the sort key (ORDER BY) for the MergeTree engine is the decisive performance factor: put the most filtered columns in your queries first (often a date and an identifier). Use Buffer tables or batch inserts of several thousand rows rather than row-by-row insertions, which create a multitude of small partitions and trigger costly merges. Monitor system.merges and system.parts to detect excessive fragmentation.

Deploy ClickHouse in one click on a ServOrbit VPS

Pinned 26.3 LTS release, persistent volumes, generated administrator account: an analytics warehouse ready to take your events.