Running ClickHouse on servers with less than 16 GB of RAM

ClickHouse can run on servers under 16 GB of RAM if you lower its caches, parallelism and logging and size the workload to fit. This page explains each setting and its trade-offs.

On this page

Running ClickHouse on servers with less than 16 GB of RAM: when small hosts make sense

A single ClickHouse server under 16 GB of RAM is a reasonable choice when the workload is modest, and the vendor's guidance for that class of host is a set of reduced defaults rather than a different product. Consider a team with a few hundred gigabytes of event data, a handful of dashboards refreshed by a few analysts, and a small server budget. That team can run ClickHouse on one host if ingestion is light, concurrency is limited, and the site is non-production or an edge location that collects data for later analysis. Development and staging environments that mirror production schemas but not production load also fit well. Workloads with heavy concurrent queries, large continuous inserts or many materialized views are a poor match for small hosts, and the reader should size those for more memory from the start.

The settings that make a small host stable are covered in the sections below, but the decision comes first: whether the workload fits at all. Confirm the expected peak concurrency, the insert rate and the retention period with the people who own the data, then test the chosen settings against representative queries before committing. The vendor's OSS usage recommendations describe the reduced defaults for this class of server, and they should be read as a starting point for your own measurements rather than as a guarantee of headroom.

This page covers single-server tuning only. It does not address replica placement, Keeper quorum or how data survives a host failure. If the analytics data must remain available during a node outage, the replication design is a separate decision, and it changes the memory budget because Keeper nodes and replicas each need their own resources. Plan that design before you size the host, not after.

Which low-memory settings to change and when each fits

This table lists each setting ClickHouse suggests for servers under 16 GB of RAM, what it gives up, and when the default should stay. Read each row against your workload: a shared analytics box with many concurrent users needs different trade-offs than a single-team ingestion node.

SettingWhat it trades awayKeep the default when
Mark cache size, lowered toward 500 MBFewer index marks held in memory, so queries that touch many parts read more index data from disk and run slowerThe server has enough free RAM for the working set, or queries repeatedly scan the same key ranges
One query processing threadLower per-query parallelism, so large scans and aggregations take longer to finishFew concurrent queries are expected and you need faster single-query latency on big tables
max_block_size of 8192Smaller blocks mean more per-block overhead, which can reduce throughput on wide scansQueries are small or selective, or memory pressure from large blocks is not a concern
Parallel parsing and formatting disabledSlower bulk ingestion and large result exports, since parsing no longer uses multiple threadsIngestion volume is modest or inserts are already limited by the network or the disk, not CPU
metric_log disabledLoss of per-interval profile event history used to investigate past performanceYou rely on Prometheus metrics or the built-in dashboard for historical performance data
trace_log disabledLoss of query and stack trace sampling for diagnosing slow or failing queriesYou are actively debugging query behavior or need trace data for an incident review
text_log disabledLoss of the server's internal text log, which slows root-cause analysis after errorsYour logging pipeline already captures server logs elsewhere and you have confirmed that
asynchronous_metric_log disabledLoss of history for asynchronous server metrics such as memory and loadHost-level monitoring already tracks memory and load over time with a retention you have checked

Where ClickHouse memory goes on a small server

The memory a small ClickHouse server uses is not one pool. Four consumers account for most of the pressure, and each of the settings recommended for servers under 16 GB of RAM targets one of them. The first is the mark cache, which holds index marks so that queries can locate the right granules in MergeTree data without rereading the primary index from disk. Its size is a trade-off: a larger cache avoids repeated index reads, while a smaller one leaves more memory for queries. The second consumer is per-query working memory, which grows with the number of query processing threads, because each thread builds its own intermediate state for joins, aggregations and sorts.

The third consumer is the block buffer, sized by max_block_size. ClickHouse reads and processes data in blocks, and each in-flight block occupies memory across every stage of a query pipeline. Lowering the block size reduces the peak size of those buffers but adds per-block overhead, so the value should be tested against your own query shapes rather than changed blindly. The fourth consumer is the parsing and formatting buffers used when parallel parsing and formatting are enabled, since each parser thread holds its own chunk of input or output. Before changing any of these values, confirm the current settings in config.xml and users.xml and record the baseline memory use under your real workload, so that you can tell which consumer actually dominates on your hardware.

Why S3 and Kafka integrations need more memory

The low-memory settings trade throughput for headroom, and that trade holds only while the workload stays small. S3-backed storage, Kafka consumers, large merges and concurrent queries each add memory demand that the reduced settings do not account for. Measure each case in a pre-production environment before you fix a server size.

S3-backed table functions and storage

Reading Parquet or CSV files through S3 table functions, or storing table data on S3-compatible disks, pulls data through memory buffers and caches on the server. A single large object read with one query thread can still hold several blocks at once. Check peak memory for your largest object and your most common read pattern, and confirm the buffer and cache settings with your ClickHouse version's documentation.

Kafka table engine consumers

Each Kafka table engine consumer parses messages into blocks and holds them until a flush condition is met. More topics, partitions and consumers multiply that working set, and a slow downstream materialized view lets it grow. Measure consumer lag, batch size and resident memory together under your real message rate and message size before you settle on a server size.

Large merges

Background merges combine data parts and need memory in proportion to the size of the parts being merged. Large parts from wide tables or big partitions can exceed the memory left after the mark cache and query threads are limited. Watch merge activity and memory during a backfill or a large insert run, and confirm that the merge memory limits you set match the largest part you expect to produce.

Concurrent queries

The single query thread setting protects one query, not the server. Several dashboards or API calls running at once each take their own memory for sorting, aggregation and joins, and a few heavy queries can exhaust the server together. Load test your real concurrency with representative queries, and use settings profiles and quotas to bound what each user or application can consume.

Pre-production sizing check

The low-memory values are a starting point, not a guarantee. Build a pre-production environment with representative data, the same ingestion paths and the same query mix, then record peak memory under the heaviest combination you expect. Use those results to decide whether a server under 16 GB of RAM is enough or whether the integration needs a larger host.

How to configure a small ClickHouse server step by step

This sequence takes a ClickHouse server with less than 16 GB of RAM from default settings to a tuned configuration that is backed up, documented and verified under a representative workload. Each step closes with a check so the operator knows the change took effect before moving on.

  1. Confirm server memory and workload profile

    Record the total RAM, CPU cores, disk type and free disk space on the host, then describe the workload: concurrent queries, typical query size, insert rate and whether the server also runs Kafka or S3 integrations. The operator on the server or the platform team does this, and the output is a short written profile that the later settings refer to. It is done when the profile lists the memory figure from the operating system, not from the vendor's minimum guidance, and names every integration that will run on the node. Those integrations determine how much memory is left for queries.

  2. Back up existing configuration and data

    Copy the current config.xml, the config.d and users.d directories, and the server's data directory or a recent backup before changing anything. The operator on the server does this and stores the copy outside the ClickHouse data path. It is done when the copies exist, can be listed, and a test restore of the configuration files into a scratch directory succeeds. Keep the copy so a rollback is a file restore rather than a rebuild.

  3. Apply reduced mark cache and thread settings

    Create a file under config.d, such as a dedicated low-memory override, and lower the mark cache size. ClickHouse's OSS usage recommendations suggest going as low as 500 MB on servers under 16 GB, and set the query processing thread count to one. Keep these in a single override file so the change is easy to find and revert. The check is that the file parses on a configuration validation run, and that the server log shows the new values after restart, which is verified in a later step.

  4. Set block size and disable parallel parsing

    In the same or a second config.d file, set max_block_size to 8192 and disable parallel parsing and formatting, as the same recommendations describe for servers under 16 GB. These settings are query-level or profile-level, so place them in a settings profile in users.d if the server runs mixed workloads. The check is that the setting appears in the system.settings table with the changed value for the profile used by your application.

  5. Disable the system log tables

    Turn off metric_log, trace_log, text_log and asynchronous_metric_log in a config.d file so these background tables stop consuming disk and memory. Ask whether you need these logs for diagnosis, because disabling them removes data you may want during an incident. Ship the server's own logs to your log platform instead. The check is that the tables no longer receive new rows after a restart, which you confirm by querying the table row counts in the system database a few minutes after the restart.

  6. Restart and confirm the new configuration loaded

    Restart the ClickHouse service during a planned window, using your service manager or the container orchestrator, and confirm the server started without errors in its log. The platform engineer then queries system.server_settings and system.settings to confirm the mark cache, thread and block values match the files. The step is complete when every value in the step records is visible on the running server, and any mismatch is traced to a file that was not loaded.

  7. Verify memory use under a representative load test

    Replay representative queries and ingestion from the workload profile against the tuned server, ideally on a pre-production copy with representative data. Watch peak memory, query duration and errors, using the system.metrics and system.query_log tables. The step is done when the replay completes without memory-limit errors or restarts, and the peak memory stays inside the headroom you planned for the operating system and any Kafka or S3 integration. If integrations push memory higher, record that finding, because it changes the sizing for that node.

Security and operational risks of trimming diagnostics

A smaller memory footprint often means fewer system logs and looser defaults at the same time. The cards below cover the evidence you lose when diagnostic tables are disabled, the configuration and access controls that must stay in place, and the change control that keeps the reduced profile from drifting.

Losing incident evidence

Disabling trace_log, text_log and metric_log removes the records you would normally use to reconstruct a slow query, a failed merge or an unexpected restart. Before you disable them, decide which questions must still be answered during an incident. Then confirm that another path, such as Prometheus metrics scraped from the server, server logs written to disk, or query logs from your client tooling, covers each question.

Monitoring that replaces the system tables

If the internal log tables are off, alerting has to come from outside the database. Make sure disk space, memory pressure, replication lag and failed queries are visible through a monitoring system that runs independently of the ClickHouse node. Test the alerts on a staging server with the reduced profile so you know they fire before a production incident depends on them.

Configuration file access

Settings for a small server live in config.xml, users.xml and files under config.d and users.d. Anyone who can edit these files can change the memory limits, authentication methods or network listeners. Restrict file permissions to the service account and administrators, keep the files in version control, and confirm that the reduced profile is not overwritten by a package upgrade.

Restricting the default user

The default user starts with all permissions, so a small server with an open default account is easy to misuse. Create named administrative users, assign roles that match each job, and disable or restrict the default user before the server accepts application traffic. Check that the reduced memory profile does not depend on the default user's settings, since that dependency makes later hardening harder.

Settings profiles and roles

Memory and thread limits are easiest to keep consistent when they live in settings profiles assigned to roles, not in ad hoc user settings. Use the SQL-driven access model to attach the low-memory limits to the roles that run analytics, and give administrators a separate profile. Review grants whenever a new application or team starts querying the server.

Change control for the reduced profile

A reduced profile tends to drift when someone raises a limit during an incident and forgets to revert it. Track every change to memory, thread and logging settings in version control, require a review, and note the reason in the change record. Re-test the profile after each ClickHouse upgrade, because default values and log behavior can change between releases.

Questions teams ask before running ClickHouse on a small server

What does it cost to tune ClickHouse on a small server?

Cost depends mainly on how much measurement and testing the work needs, and on who does it. Most of the spend goes to people: someone must profile your real queries, adjust settings such as the mark cache size, query threads and block size, and then prove the server stays stable under load. The cost is largely one-time, although the settings you choose will need review when you upgrade ClickHouse or change workloads. Costs rise with the number of data sources, such as Kafka streams or S3 tables, because each one adds memory pressure and more testing. Ask any quote to state the workload assumptions, the test environment, the number of integrations in scope and who is responsible for each deliverable, and to name the deliverables in writing before work starts.

How long does tuning a small ClickHouse server take?

Duration is set by how quickly you can produce representative test conditions and how many workloads must be validated. A server running one ingestion path and a few dashboard queries is simpler than one that also reads from object storage or consumes Kafka topics. Expect time for baseline measurement, a settings change, a load test, and a second round after the results come in. Preparing a pre-production host with realistic data often takes longer than the tuning itself. Ask for a written schedule with milestones tied to specific tests, and make sure it states what happens if the first round of results does not meet the agreed criteria.

What must our team provide before tuning starts?

You need to provide three things: representative data, representative queries and a pre-production host. Representative data should match the shape, volume and cardinality of production, because a sample that is too small hides memory problems. Representative queries should come from your dashboards, alerts and scheduled jobs, with their frequency and concurrency recorded. The pre-production host should match the production memory, CPU and disk profile closely enough that results carry over. Without these inputs, any settings change is a guess. The ClickHouse production guidance also recommends a pre-production environment before any production change, and reading the changelog before an upgrade, so plan for both from the start.

Can our internal team tune a small ClickHouse server alone?

Yes, if the team already runs Linux servers, understands ClickHouse table engines and can run load tests. The main risks for an internal team are time and missing experience with memory behavior under concurrency. Settings such as the mark cache, query threads and parallel parsing interact, and a change that helps one query can hurt another. Internal teams also tend to skip the restore and upgrade checks that keep a tuned server supported over time. Decide based on who will own the settings after go-live, whether there is a pre-production host, and whether someone can run the tests on a schedule. If any of these are missing, a partner can cover the gap, and the split of work should be written into the scope.

Can small ClickHouse servers be replicated for availability?

Yes. ClickHouse replicates data with ReplicatedMergeTree tables coordinated by ClickHouse Keeper, which replaces ZooKeeper. A recovered replica automatically syncs missing data from a healthy replica without manual intervention. The replication guide recommends at least three Keeper nodes, on dedicated hosts in production, with about 4 GB of RAM each. That Keeper memory sits on top of what the data servers need, so a small data node plus three Keepers changes the memory plan. Replication also does not protect against human error or corruption, so it should be paired with tested backups. Confirm the Keeper placement and memory budget with your team before you size the data nodes.

Why do S3 and Kafka integrations need more memory?

Object storage and streaming integrations keep extra buffers, metadata and connections in memory, and they add concurrent work that a plain local table does not. The low-memory settings suggest lowering the mark cache and using one query thread, which leaves less headroom for these features. For that reason, a server running S3-backed tables or Kafka consumers should be treated as needing more memory than the base recommendation, and its settings tested with those integrations switched on. Ask the team to measure peak memory with every integration active, not just with local tables, before they choose a server size.

Is it safe to disable ClickHouse log tables on a small server?

ClickHouse suggests disabling metric_log, trace_log, text_log and asynchronous_metric_log on servers with under 16 GB of RAM to free memory and disk. The trade-off is that you lose the built-in diagnostics those tables provide when something goes wrong. Before disabling them, decide where you will get the same signals, for example from Prometheus metrics or a log shipper, and confirm that your on-call team can still troubleshoot a slow query. Keep the change in your configuration files so it survives restarts and is visible in review.

When to move off a small server instead of tuning harder

Tuning a small server stops being the right answer once the workload itself has outgrown one machine. The settings in this topic (a smaller mark cache, one query thread, an 8192 max_block_size, disabled parallel parsing, and turned-off system log tables) trade throughput for stability. If you find yourself relaxing them further to keep a service running, the problem is capacity or availability, not configuration. Watch for three signals: a Kafka or S3 integration moving into production, query concurrency that keeps climbing after the tuning is done, and a requirement that the data stay queryable when one server fails.

Integrations are the first signal to take seriously because they add memory and CPU demand that the single-thread settings do not account for. The topic's guidance already notes that Kafka and S3 integrations need more memory than a small server offers. Before go-live, confirm the expected message rate, the number of consumers, the object sizes read from storage, and the memory headroom left after the mark cache and query threads are set. Run the integration against representative volume in a pre-production environment. If the numbers only work with the cache at its floor and no concurrent queries, plan the larger host now rather than after the first incident.

Concurrency and availability are the second and third signals. When several analysts or services query at once, a single query thread becomes a queue, and the settings that protected stability start limiting usefulness. A single node also cannot provide availability: a failed host means lost query access until it returns. ClickHouse provides replication through ReplicatedMergeTree tables coordinated by ClickHouse Keeper, which needs its own dedicated nodes in production. Replication is not a backup, so the backup plan still applies. The next steps are the sibling topics on ClickHouse hardware sizing for self-managed clusters, which turns your concurrency and retention figures into CPU, memory and replica counts, and on ClickHouse high availability and replication, which covers shards, Keeper and replica recovery.

How DeployOpen scopes and delivers low-memory ClickHouse servers

This sequence turns a memory-constrained ClickHouse server into a written, tested configuration that your team can operate, with each step producing a record you can review before the next one starts.

  1. Agree the written scope

    DeployOpen and your team agree a written scope before any work starts. It names the servers in scope, the ClickHouse version and deployment method, which integrations are in or out (for example, S3 or Kafka table engines), the environments that will be changed, and the acceptance checks. The scope is the basis for the quote. The step is complete when both parties have signed off on the document and the change window and access model are recorded.

  2. Collect hardware and workload facts

    Your team provides the current memory, CPU count, disk layout and operating system for each node, along with a description of the workload: which tables are loaded, how often they are queried, peak concurrency, and whether any queries join large tables or read from external storage. DeployOpen checks these facts against the vendor's guidance for servers under 16 GB. The step closes with a short inventory that lists what is known, what is unknown, and what must be confirmed in your environment.

  3. Review memory consumers and integrations

    DeployOpen maps every component that draws on memory: the mark cache, query threads, block sizes, background merges, system log tables, and any S3 or Kafka integrations. Integrations that need more memory are flagged, and the review states whether they fit on the target hardware or need a larger server, a dedicated node, or a deferred integration. The output is a memory budget table that shows each consumer and the setting that controls it, reviewed with your team.

  4. Draft the configuration changes

    DeployOpen drafts the server configuration as files your team can review in version control. Changes cover the mark cache size, the number of query processing threads, max_block_size, parallel parsing and formatting, and disabling the metric_log, trace_log, text_log and asynchronous_metric_log tables where your monitoring does not depend on them. Each change is paired with the reason and the setting it replaces. The step closes when the draft has been reviewed against the memory budget.

  5. Apply changes in your infrastructure

    Your team applies the approved changes in the environments you control, using your change process and access controls. DeployOpen works through a runbook covering the order of restarts, rollback steps, and the checks to run after each node restarts. The step is complete when every node reports the expected settings and the server starts cleanly under normal load, with the results recorded in the change ticket.

  6. Validate with representative queries

    DeployOpen runs your representative queries and ingest jobs against a pre-production copy of the configuration with data that resembles production. The tests measure memory use, error rates, and whether queries finish with the thread and cache settings applied. Results are compared with the baseline, and any query that fails or exhausts memory is documented with a proposed fix. The step closes with a signed validation report that lists the queries tested, the outcomes, and any open items.

  7. Hand over documentation and monitoring checks

    DeployOpen delivers the final configuration, the memory budget, the runbook, and a short list of monitoring checks to watch after go-live. Because disabled system log tables reduce what operators can inspect, the handover states which signals remain available and which must come from elsewhere. Your team confirms it can read and apply the configuration without DeployOpen present. The step is complete when the handover package is accepted in writing.

  8. Plan optional retainer support

    If you want continued help, DeployOpen can provide a retainer covering version upgrades, configuration reviews when workloads change, and re-validation after memory-related incidents. Upgrades follow the vendor's release and support policy, and the changelog is read before each move. The retainer scope, cadence and fees are set in a separate written agreement, and the step closes when that agreement is signed or when your team confirms it will run the configuration in-house.

Talk to our team.

Tell us what you're working on, whether it's a deployment, an audit, a security test or a cyber range. You'll speak with an engineer who can help you scope it.

  • 30-minute call: free, with no obligation.
  • NDA on request: we can sign before you share details.
  • Clear next steps: a scope and plan after the call.