ClickHouse and Elasticsearch overlap on logs but are built on different storage layouts: column files with a sparse index versus a Lucene inverted index. What that decides for aggregation cost, ranked search, vector retrieval and migration, with current (2026) facts on LogsDB, ClickHouse text and vector indexes, and licensing.
ClickHouse stores a table as one compressed file per column with a sparse index over sorted rows. Elasticsearch stores a document up to three times: in a Lucene inverted index, in stored fields, and in doc_values. Everything else in this comparison follows from that. If your queries are GROUP BY service, toStartOfMinute(ts) over billions of rows, ClickHouse will do it faster on less disk. If your queries are "find the ten documents that best match this phrase", Elasticsearch will do it and ClickHouse will not.
Both vendors have moved since 2024: Elastic shipped LogsDB index mode, ClickHouse shipped GA text and vector indexes. Neither flips the answer above, and the details of why are the useful part.
Two engines, two storage layouts
A ClickHouse MergeTree table is written as immutable parts. Inside each part, every column is its own compressed file, and a sparse primary index keeps one entry per granule of 8,192 rows, so the index for a multi-billion-row table fits comfortably in memory. Skip indexes (min-max, bloom filters, and now text and vector indexes) prune granules for filters outside the sort key. A query that reads 3 columns out of 100 reads roughly 3% of the bytes, and the execution engine processes those bytes as column blocks with SIMD instructions rather than row by row.
Elasticsearch is built on Apache Lucene, whose core structure is the inverted index: a map from every analyzed term to the postings list of documents containing it. Term lookup is what it does well. To also return the original document and to sort or aggregate, Lucene keeps two more representations: stored fields (the _source JSON) and doc_values, a columnar copy of each field. You can turn off index or doc_values per field, and synthetic _source can drop the stored copy by rebuilding it from doc_values at fetch time, but the default for a log index is all three.
Aggregations and log storage: where ClickHouse wins
The public migrations are consistent in direction and in the size of the gap. Cloudflare saw each Elasticsearch document of about 600 bytes shrink to 60 bytes per row in ClickHouse, which let them stop sampling and keep 100% of failure events (500K-800K per second out of 35-45 million HTTP requests per second). Uber cut the hardware cost of its logging platform by more than half compared with the ELK stack while serving more traffic; a single ClickHouse node ingested around 300K logs per second, roughly 10x what an Elasticsearch node handled. Uber also noted that over 80% of its production log queries were aggregations (terms, histogram, percentile), which is the workload the columnar layout is designed for. Contentsquare reports 11x lower cost, 10x faster queries, and retention extended from 1 to 13 months with 6x more data.
ClickHouse's own billion-row benchmark on the PyPI download dataset measured 12-19x less storage and at least 5x faster count(*) aggregations. Vendor-run, so discount it; the customer numbers above land in the same range anyway.
Elastic's response is LogsDB index mode, GA since 9.0. It sorts the index by host and timestamp, uses synthetic _source, and Elastic reports up to 60% smaller log indices for a 10-20% indexing cost. That closes part of the gap. A 60% reduction turns a 10x storage difference into roughly 4x, and synthetic _source requires a paid subscription, so the free tier still stores three copies.
Pre-aggregation and high cardinality
Dashboards over long retention are where ClickHouse pulls further ahead. An AggregatingMergeTree target fed by a materialized view stores partial aggregation states per insert block, so a p99-by-service panel over 90 days reads the rollup instead of the raw table. Projections do the same inside the base table, and the planner picks them without query changes.
High-cardinality GROUP BY is the other place the layouts diverge. Elasticsearch's terms aggregation returns the top 10 buckets by default, the whole response is capped by search.max_buckets at 65,536, and the per-shard top-N merge makes counts approximate when the field has millions of values. ClickHouse groups by user_id or URL over the full column, spilling to disk under max_bytes_before_external_group_by when the hash table outgrows memory.
Ranked search and hybrid retrieval: where Elasticsearch wins
Elasticsearch scores every hit with BM25 by default, and the query DSL is built around that score: match and match_phrase, fuzzy matching by edit distance, per-field boosts, function_score to fold business signals into the ranking. Text passes through analyzer chains at index time and again at query time, so "running" matches "runs" without the application doing anything.
Try to build that on ClickHouse and you hit a wall, though a different wall than two years ago. The text index is a real inverted index, GA since 26.2, with a choice of tokenizers (including n-gram and language-aware ones) that accelerates hasAllTokens, hasAnyTokens, hasPhrase, LIKE and match. For "show me log lines containing these tokens" it works and it is fast. But in ClickHouse's own words from the GA announcement, "it is not a relevance engine and does not implement scoring models such as TF IDF or BM25, nor does it store positional information for advanced phrase ranking." There is no query-time analysis, no synonyms, no highlighting, and no way to say "best match first" beyond an ORDER BY you compute yourself. The docs also flag the index as not supported in ClickHouse Cloud, which matters if that is where you run.
Vectors, hybrid retrieval, and geo
Elasticsearch has had HNSW approximate kNN in _search since 8.4, with weighted combination of a knn clause and a lexical query; 8.8 added Reciprocal Rank Fusion to merge the two without normalizing scores. Version 9.1 made BBQ quantization the default for vectors of 384 dimensions or more and added ACORN-1 for filtered kNN, with around 5x speedups on selective filters.
ClickHouse is no longer absent here. The vector_similarity index (HNSW via usearch) went GA in 25.8 with pre- and post-filtering, rescoring, and bf16, i8 and b1 quantization. Pure ANN over a table of embeddings is fine in ClickHouse. What it cannot do is hybrid retrieval: there is no BM25 score to fuse with the vector distance and no rank fusion primitive, so a RAG pipeline that wants lexical and semantic signals ranked together still belongs on Elasticsearch or OpenSearch.
Geo follows the same shape: Elasticsearch indexes geo_shape for intersects and within queries; ClickHouse has geoDistance, H3 and pointInPolygon but no shape index, so polygon containment is a scan.
| ClickHouse | Elasticsearch | |
|---|---|---|
| Storage layout | Column files, sparse index, immutable parts | Inverted index + stored fields + doc_values |
| Log storage per row | ~10x smaller in public migrations; 12-19x in ClickHouse's benchmark | LogsDB (9.0+) cuts up to 60%; synthetic _source needs paid tier |
| Aggregations | Vectorized, full-column, disk spill, pre-aggregation via MV/projections | terms top 10 default, search.max_buckets 65,536, approximate at high cardinality |
| Text search | text index GA 26.2: token filtering, no scoring, not in Cloud |
BM25, analyzers, fuzzy, phrase, highlighting |
| Vector search | HNSW vector_similarity GA 25.8, quantization, filtering |
HNSW since 8.4, RRF hybrid since 8.8, BBQ + ACORN-1 in 9.1 |
| Ingest model | Batch inserts or async_insert |
Single-document or _bulk |
| Memory | Native C++, no heap ceiling | JVM heap capped at ~26-30 GB per node |
| License | Apache 2.0 | AGPLv3 / SSPL / ELv2 (OpenSearch: Apache 2.0) |
Operating each one
ClickHouse runs as a single C++ binary. The operational rule that matters most is insert shape: each insert creates a part, and thousands of small inserts produce a "too many parts" error before background merges catch up. Batch on the client, or enable async_insert and let the server buffer until 100 MiB or 200 ms. Schema is explicit; ALTER TABLE ADD COLUMN is a metadata operation, and the JSON type covers fields you do not want to declare.
Elasticsearch's constraints sit in the JVM. Elastic's guidance is heap at no more than 50% of RAM and below the compressed-oops threshold, 26 GB safe on most systems and up to 30 GB on some, so useful memory per node has a ceiling regardless of machine size. GC pauses longer than the fault-detection timeout get a node ejected and its shards reallocated, and oversharding makes that more likely. Dynamic mapping is convenient until Kubernetes labels or trace attributes produce thousands of fields; Cloudflare named mapping explosion as one of its two reasons to leave.
Licensing changed in August 2024. Elasticsearch is triple-licensed under AGPLv3, SSPL and ELv2, so it is OSI open source again, though AGPL is copyleft and some legal teams treat it as they treated SSPL. ClickHouse is Apache 2.0, as is OpenSearch.
For observability, both sides now ship an OpenTelemetry-native stack: ClickStack (HyperDX UI, OTel collector, ClickHouse) with Lucene-style search syntax over SQL, and Elastic Observability with its EDOT collectors. The UI gap that used to favour Kibana is much smaller than it was.
Deciding, and migrating
Ask one question of the Elasticsearch cluster before anything else: does anyone run relevance-ranked queries against it? Pull a week of slow logs or proxy logs and look for match on analyzed text with the score used for ordering. If there are none and the workload is filters plus aggregations over time, the storage numbers above are your business case. If the cluster serves a search box, product catalogue, or a RAG retriever, stay, and put the log indices that share it on LogsDB.
When both exist for real, run both: fan out from Kafka, analytics to ClickHouse, search to Elasticsearch, two schemas to maintain. Do not plan on an API translation layer to bridge them. Quesma, the Elasticsearch-to-ClickHouse gateway the earlier version of this post recommended, was archived in November 2025 after its IP went to Hydrolix.
A migration in practice is three steps. Design the ClickHouse sort key from the actual filter patterns (usually (service, timestamp) or similar) before moving a byte, because it is the one thing you cannot change cheaply later. Dual-write for the retention window. Rebuild the Kibana panels in Grafana or HyperDX during that window, not after, since the panel rewrite is where teams discover the queries they forgot they had. For the wider set of options beyond these two, see our guide to Elasticsearch alternatives.
Frequently Asked Questions
Is ClickHouse faster than Elasticsearch?
For aggregations over large volumes, yes: public migrations report roughly 10x smaller storage per row and a single ClickHouse node ingesting around 300K logs per second, about 10x an Elasticsearch node, and ClickHouse's own benchmark shows at least 5x faster count aggregations. For relevance-ranked text search, Elasticsearch is the one that can do the job at all.
Can ClickHouse replace Elasticsearch for logs?
Often. Cloudflare shrank about 600 bytes per Elasticsearch document to 60 bytes per row in ClickHouse, and Uber cut logging hardware cost by more than half. The test is whether anyone runs relevance-ranked queries against the cluster; if the workload is filters plus aggregations over time, ClickHouse is the business case, and if it serves a search box or RAG retriever, stay on Elasticsearch.
Does ClickHouse support full-text search?
Partially. The ClickHouse text index, GA since version 26.2, is a real inverted index that accelerates token filters such as hasAllTokens, hasPhrase, LIKE and match. It does not implement BM25 or TF-IDF scoring, query-time analysis, synonyms or highlighting, and it is not supported in ClickHouse Cloud.
Is ClickHouse good for vector search?
For pure approximate nearest neighbor search, yes: the vector_similarity HNSW index went GA in 25.8 with filtering, rescoring and quantization. It cannot do hybrid retrieval because there is no BM25 score to fuse with vector distance, so a RAG pipeline that ranks lexical and semantic signals together belongs on Elasticsearch or OpenSearch.
Which uses less storage, ClickHouse or Elasticsearch?
ClickHouse, by roughly 10x in public migrations and 12-19x in ClickHouse's own benchmark. Elasticsearch's LogsDB index mode, GA since 9.0, cuts log indices by up to 60%, turning a 10x gap into roughly 4x, but synthetic _source requires a paid subscription.
What are the licensing differences between ClickHouse and Elasticsearch?
ClickHouse is Apache 2.0, as is OpenSearch. Since August 2024 Elasticsearch is triple-licensed under AGPLv3, SSPL and ELv2, so it is OSI open source again, though AGPL is copyleft and some legal teams treat it as they treated SSPL.