QuestDB

Open-source time-series database with SQL extensions (SAMPLE BY, ASOF JOIN), fast ingestion and Parquet; Enterprise adds HA and security.

Best for: Teams storing market data, sensor or telemetry streams who want fast SQL with time-series joins

Editor’s note: This QuestDB review is based on my hands-on test of QuestDB 10.1.0 (open source, released October 22, 2026) on my own laptop, from install to ingesting about 25 million rows and running time-series joins. I weighed pricing, ingestion, time-series SQL, storage and lifecycle, and security and availability.

Quick verdict: I recommend QuestDB for teams storing market data, sensor or telemetry streams who want SQL with time-series joins. Among real-time analytics databases it’s a time-series specialist. The trade-off: the open-source build is one instance without TLS, RBAC or failover, and Enterprise has no public price.

Key Takeaways:

  • I loaded 10M rows over ILP in 6.1 seconds; QWP handed off a 10M-row set with one column fewer in 1.7 seconds.
  • My ASOF JOIN of 10M trades against 5M quotes ran in 1.78 seconds.
  • Open source is Apache 2.0 and $0; Enterprise is quote-only.
  • There’s no DELETE statement: data leaves as whole partitions, through TTL or DROP PARTITION.
  • The 10.1.0 binaries need Java 25, and HTTP ships without a password.

In this review, I’ll take a closer look at QuestDB’s pricing, ingestion, time-series SQL and storage, so you can see exactly whether it beats TimescaleDB, InfluxDB or ClickHouse for your workload.

Pros and Cons

Pros

  • About 1.65M rows/s over ILP and 6.06M rows/s over QWP from one Python process in my test
  • ASOF JOIN of 10M trades against 5M quotes in 1.78 s; SAMPLE BY and LATEST ON built into SQL
  • DEDUP UPSERT KEYS turned 1,700 sent rows into 1,200, with late rows placed automatically
  • Apache 2.0 license with PostgreSQL wire on port 8812, REST and a Web Console
  • In-place Parquet conversion cut my partitions from 43.7 MiB to 27.7 MiB

Cons

  • Open source is a single instance with no replication, failover, TLS or RBAC
  • No public Enterprise price; the $2.283/hour AWS listing is a placeholder
  • No DELETE statement; data leaves only as whole partitions via TTL or DROP PARTITION
  • 10.1.0 binaries need Java 25, and HTTP ships with no password

How Much Does QuestDB Cost?

QuestDB has two editions, and only one has a published price: $0. Enterprise adds the production controls, but its price only comes from a sales quote, so I can’t put a monthly figure on it.

  • Open Source ($0): for self-hosted pilots and internal analytics on a single instance, under the Apache 2.0 license.
  • Enterprise (quote only): for production deployments that need replication, access control and SLA-backed support.
  • Enterprise BYOC (quote only): Enterprise in your own AWS or Azure account, run by QuestDB’s ops team. GCP is marked “coming soon”, and BYOC is a deployment option, not a third edition.
EditionPriceDeploymentHA and replicationSecurityBackupsSupport
Open Source$0, Apache 2.0Self-managed binaries or containersNone; single instanceBasic auth over unencrypted transmission; one admin plus one read-only userManual full backups to local storageCommunity, best effort
EnterpriseContact sales; free trial with unpublished termsSelf-managed (on-prem or cloud) or BYOCRead replicas; primary failover QuestDB states at under 100 msRBAC incl. column-level, TLS on all protocols, OAuth/OIDC/Entra ID SSO, user-activity logsAutomated incremental snapshots; point-in-time recoveryDedicated, response-time SLAs

The AWS Marketplace AMI shows $2.283/hour in software charges on an r6a.2xlarge, but the listing states Enterprise is sold only through private offers and that figure is a placeholder. AWS infrastructure is billed on top. I wouldn’t use that rate to estimate a production bill.

Is QuestDB Good Value for Money?

  • Open source includes the engine features I tested: time-series SQL, materialized views and manual Parquet conversion aren’t Enterprise upsells, and TTL retention ships in both editions.
  • Free software still needs operations: budget for your own backups and a TLS proxy. Neither one adds the replication or automatic failover open source lacks.
  • TimescaleDB offers a clearer entry budget: Tiger Data’s Performance plan starts at $30/month with a 30-day trial.
  • ClickHouse publishes usage-based cloud pricing: its $300 trial credit lets you measure real costs before you commit.
  • InfluxDB splits free and paid cleanly: InfluxDB 3 Core is free; Enterprise lists $0.198 per vCPU-hour on AWS Marketplace.

QuestDB is the only one of those four without a usable public production price. I’d start with Open Source for a pilot and request an Enterprise quote before any architecture decision that depends on failover.

Reviewer’s Notes: I recommend Open Source behind a reverse proxy for pilots and internal analytics that can tolerate a single-instance outage. Once you need failover or regulated access control, request an Enterprise quote and get the free trial’s length, restrictions and support terms in writing.

My Experience With QuestDB

Installing QuestDB 10.1.0

I downloaded the 34 MB no-JRE tarball from GitHub on release day and ran it on my Apple M1 Pro laptop with 32 GB RAM. The jar is compiled for Java 25, and the Java 8 already on my machine couldn’t run it.

I installed a Temurin 25.0.5 JRE and pointed JAVA_HOME at it. Homebrew’s formula was still on 10.0.1 that day. The Docker image and the Linux/Windows runtime tarballs bundle Java, so Docker users skip this step.

Running questdb.sh start -d <dir> brought the server up in about 3 seconds. It printed the Web Console URL and a ready-made ILP connection string.

The Default Setup Has No Password on HTTP

The REST /exec endpoint and the Web Console answered without authentication. The http.user and http.password settings were commented out in server.conf, and PostgreSQL wire on port 8812 used the default admin / quest login.

HTTP was open by default, not merely guarded by a weak password. I’d accept that on an isolated laptop, never on a reachable host.

Loading 10 Million Rows

pip install questdb installed client 6.0.0 on Python 3.14 with no build step. I sent 10M synthetic crypto trades from a pandas DataFrame in 1M-row batches over ILP/HTTP.

The load took 6.1 seconds, about 1.65M rows/s from one Python process, and every row was queryable at 6.1 seconds. QuestDB auto-created a WAL table partitioned by day, with the DataFrame’s nanosecond column as the designated timestamp. I never wrote a CREATE TABLE.

Next, I used questdb.connect() over QWP/WebSocket. It handed off 10M rows in 1.7 seconds, about 6.06M rows/s client side, but that set had one column fewer, so this isn’t a like-for-like protocol comparison.

A count right after the handoff showed 9,000,000 rows, and all 10M were visible at 2.2 seconds. Reading a 1,428,126-row day back into pandas over QWP took 107 ms.

The Python client printed two migration warnings:

  • Imports: questdb.ingress is deprecated in favor of importing from questdb or using questdb.connect().
  • Datetimes: naive datetimes are now read as UTC; client 4.x read them as local time.

Writes Show Up a Moment Later

A CREATE TABLE AS of 5M quotes returned in 779 ms, but a count a moment later showed 4,000,000 rows. It reached 5,000,000 shortly after.

DROP PARTITION also returned instantly while the row count fell later, from 10,000,000 to 8,571,265. WAL tables apply writes asynchronously, so I recommend polling wal_tables() or expected row counts before asserting results in automated tests.

The QWP handle had no wait() method even though its docstring mentions one. I polled count() instead.

Reviewer’s Notes: Java 25 was my only real install friction. After that, auto-created tables made the first load easy, but I’d build explicit visibility checks into any ingestion test rather than treating a successful write response as proof every row is queryable.

Time-Series SQL: SAMPLE BY, LATEST ON and ASOF JOIN

My full-week ASOF JOIN matched 10M trades against 5M quotes by symbol and averaged trade price minus mid-quote in 1.78 seconds, on both runs. For market-data work, that join matters more than raw ingest speed.

Query in my test (10M-row trades table)What it doesTime
SAMPLE BY 1hHourly OHLCV for one symbol105 ms cold; about 13 ms warm
SAMPLE BY 1d FILL(PREV)Daily aggregation across all symbols147 ms
LATEST ON timestamp PARTITION BY symbolLast trade per symbol6 ms
One-day interval filter with GROUP BYPer-symbol aggregation for one day16 ms
Full-week ASOF JOIN aggregateMatch each trade to the preceding quote1.78 s
SUBSAMPLE lttb(price, 500)Reduce about 143K rows to 500 original rows44 ms
One-day GROUP BY through psycopgSame kind of query over PostgreSQL wire11 ms

⏱️ SAMPLE BY and FILL

SAMPLE BY expressed hourly OHLCV in one clause, and FILL(PREV) carried previous values into empty buckets. TimescaleDB does bucketing with time_bucket inside PostgreSQL, so I’d choose between them on compatibility needs as much as aggregation speed.

My one-day interval filter finished in 16 ms because interval filters prune partitions. I’d put a time filter on every dashboard query and test full-history scans separately, since the week-long ASOF aggregate took 1.78 s.

🔗 ASOF, LT, SPLICE, WINDOW and HORIZON Joins

ASOF JOIN matches the latest right-hand row at or before each left-hand timestamp. On a one-day slice, my first 5 rows came back in 16 ms, each with the matched quote timestamp just before its trade.

TOLERANCE caps how stale a match can be, though months and years aren’t valid units. Inputs must be time-ordered: the timestamp() hint skips sorting but returns wrong results on unsorted data, so I’d only use it when order is guaranteed.

QuestDB also documents LT, SPLICE, WINDOW and HORIZON joins; I tested only ASOF. ClickHouse also supports ASOF JOIN, so the join alone isn’t unique to QuestDB.

📉 SUBSAMPLE, New in 10.1.0

My first query, lttb(500), failed with “lttb() requires at least 2 arguments: column and target points”; the release-notes example shows only method(args). With lttb(price, 500), QuestDB returned exactly 500 original rows from about 143K in 44 ms. The other methods are m4, minmax, uniform, cadence and sdt.

I like that SUBSAMPLE returns real rows rather than synthetic aggregates, which keeps chart points traceable. The same release adds percentile_disc and percentile_cont with arrays of percentiles.

🧮 Materialized Views

My hourly OHLCV materialized view returned in 27 ms with status “refreshing”, turned “valid” a few seconds later with 1,440 rows, and matched my ad-hoc SAMPLE BY output. Both editions include materialized views.

PostgreSQL habits don’t always carry over:

  • Index syntax differs: CREATE INDEX failed. ALTER TABLE ... ALTER COLUMN ... ADD INDEX worked, and only on SYMBOL columns.
  • Multi-statement execution failed: my psycopg BEGIN; INSERT ...; ROLLBACK; was parsed as one bad INSERT.
  • No scrollable cursors: drivers that rely on DECLARE CURSOR fail with “unexpected token: CURSOR”.
  • 10.1.0 has breaking changes: intervals must use one-letter units ('1min' used to be read silently as 1 nanosecond), INT arithmetic now wraps at 32 bits, and HORIZON JOIN output changed, so read the release notes before upgrading.

A subquery in WHERE did work over PGWire. I’d still run existing PostgreSQL application queries before treating PGWire as a drop-in replacement.

Ingestion, Deduplication and Out-of-Order Data

After loading 1,000 sensor rows, I resent 500 duplicate keys with a new temperature and added 200 rows backdated three hours. My DEDUP UPSERT KEYS(ts, sensor) table ended with 1,200 rows, not 1,700.

The last write won for the 500 duplicates, and the late rows landed in their own earlier hourly partition with no error and no manual merge. After ALTER TABLE ticks DEDUP DISABLE, resending 100 keys produced 1,300 rows, so dedup is a per-table switch I’d turn on before the first production load.

QuestDB documents four dedup limits that matter for backfills:

  • WAL tables only: dedup isn’t available on non-WAL tables.
  • Timestamp required: the designated timestamp must be in the upsert keys.
  • No retroactive cleanup: enabling dedup doesn’t remove existing duplicates.
  • Key choice affects cost: high-cardinality keys and many rows per timestamp add work.

Schema drift split on field versus type:

  • New field: sending humidity over ILP added a DOUBLE column automatically.
  • Wrong type: a string sent into the DOUBLE column temp failed the whole flush with “cast error from protocol type: STRING to column type: DOUBLE”.

One bad value cost me an entire flush, so I’d validate producer types before batching records together.

QuestDB’s ingestion and query paths:

  • QWP: the recommended client protocol; stable for Java, C/C++, Rust and Python, beta for .NET and Go, planned for Node.js.
  • InfluxDB Line Protocol: works with Telegraf, so existing InfluxDB agents can target QuestDB without a rewrite.
  • PGWire: mainly for queries from PostgreSQL clients.
  • REST: /exec, /imp and /exp for SQL, imports and exports.
  • Connectors: Kafka, Flink, Redpanda, Telegraf and Databento are documented; I didn’t test them.

My active hourly partition holding 1,000 rows showed 48 MiB on disk from preallocation, so small tables look big at first.

Storage, Parquet and Data Lifecycle

Converting two day partitions to Parquet in place cut each from 43.7 MiB to 27.7 MiB, about 37% smaller. My queries kept working over them: counts matched, and an average differed only in the 13th decimal place.

COPY trades TO 'trades_export' WITH FORMAT PARQUET ran asynchronously and wrote six per-partition files totaling 166 MB in about 0.55 seconds. Polars read one straight away, with timestamps as nanosecond UTC and symbols as strings.

read_parquet() only reads from the server’s import directory; a path outside it failed with “file does not exist”. From inside it, the function counted 1,430,155 rows with correct timestamps, so plan file placement before you build an import job.

QuestDB documents these Parquet limits:

  • Compression: the default codec is LZ4_RAW.
  • Active data: the active partition is never converted.
  • Mutations: UPDATE isn’t supported on Parquet partitions.
  • Query execution: some parallel queries aren’t yet optimized for Parquet.
  • Remote tiering: Enterprise adds storage policies and Parquet/Iceberg tiering to S3, Azure Blob and GCS. I didn’t test it.

There is no DELETE statement. My delete from trades ... returned “unexpected token [from]”. TimescaleDB, as PostgreSQL, supports row-level deletes; QuestDB removes data in whole partitions.

TTL drops a partition only once its full time range falls outside the window. Manual DROP PARTITION can’t remove the latest partition, and the partitioning scheme can’t change after table creation.

UPDATE worked when I zeroed a day of DOGE-USD trade amounts, but it can’t change the designated timestamp, and joins in UPDATE aren’t supported on WAL tables.

Choose partition boundaries before loading production data. Match PARTITION BY to your retention unit; GDPR-style removal of individual records means rewriting the table with CREATE TABLE AS SELECT. If selective erasure were routine for me, that workflow alone would push me to TimescaleDB.

Security, Availability and Enterprise Features

Open source has basic auth over unencrypted transmission, one admin plus one read-only user, and no replication or failover. My install didn’t require an HTTP password at all by default.

I didn’t have an Enterprise license, so these are documented capabilities, not features I tested:

  • Access control: RBAC down to columns, TLS on all protocols, OAuth/OIDC/Entra ID SSO and user-activity logs.
  • Availability: read replicas and primary failover, which QuestDB states takes under 100 ms; it also cites a 99.9% uptime figure without defining SLA terms.
  • Recovery: automated incremental snapshots and point-in-time recovery.
  • Compliance: QuestDB states SOC 2 Type 2 compliance.
  • Deployment: BYOC on AWS and Azure; multi-primary writes are “coming soon”.

A TLS reverse proxy covers transport encryption on open source, but it doesn’t replace RBAC or remove the single-instance failure risk.

The Web Console MCP server lets coding agents such as Claude Code, Codex, Cursor and Gemini CLI connect with None/Schema/Read/Write permission tiers, over loopback only, and needs Node.js. Version 10.0 also brought Web Console 2.0 with notebooks. I tested neither the MCP server nor the Web Console UI (I worked through REST, PGWire and the Python client), so neither shapes my verdict.

QuestDB recommends at least 8 GB RAM for basic workloads and 32 GB for heavier ones, NVMe storage, and raising ulimit -n and vm.max_map_count to 1048576; NFS can’t be used directly. My server sat at about 600 MB RSS after all my tests, with a 1.1 GB data directory for about 25M rows, but one laptop session isn’t capacity planning.

How Does QuestDB Compare to Competitors?

I’d shortlist QuestDB for time-oriented SQL, TimescaleDB for PostgreSQL semantics, and ClickHouse for broader event analytics. I didn’t run these rivals side by side, so this is a fit comparison, not a speed ranking.

  • Tiger Data (TimescaleDB): my pick when row-level DELETE, PostgreSQL extensions and existing application queries matter more than QuestDB’s SQL extensions. The Performance plan starts at $30/month with a 30-day trial, and HA replicas bill at the primary’s rate.
  • InfluxDB 3: the fit for metrics and observability stacks built around Telegraf and Line Protocol. Core is free but lacks HA, clustering, enterprise security and SLAs; Enterprise has a 30-day trial and an AWS Marketplace rate of $0.198 per vCPU-hour. QuestDB’s ILP endpoint lets you test a migration without replacing agents.
  • ClickHouse: my choice for wide-event analytics beyond time-series. Cloud starts with $300 in trial credit; published Enterprise compute on AWS us-east-1 is $0.39030 per compute-unit hour (one unit is 8 GiB RAM and 2 vCPU), with storage extra at $25.30/TB per month.
  • Apache Druid: streaming OLAP for user-facing dashboards on a distributed cluster, a heavier operational commitment than the single QuestDB process I ran.
  • kdb+ (KX): the commercial, quote-only incumbent for capital-markets tick data. QuestDB’s pitch against it is standard SQL and an Apache 2.0 license.

These entry prices aren’t equivalent configurations. QuestDB’s free engine, an InfluxDB vCPU-hour and a ClickHouse compute-unit hour buy different capabilities.

DatabaseOpen-source licenseEntry priceQuery languageHA in free tierBest for
QuestDBApache 2.0$0 OSS; Enterprise quote onlySQL with time-series extensionsNoMarket data and telemetry
TimescaleDBOpen-source coreTiger Cloud Performance from $30/monthPostgreSQL SQLSelf-managed onlyPostgreSQL-centric time-series
InfluxDB 3Open source (Core)$0 Core; Enterprise $0.198/vCPU-hour on AWS MarketplaceSQL and InfluxQLNoMetrics and observability
ClickHouseApache 2.0$0 self-hosted; usage-based CloudSQLSelf-managed onlyWide-event analytics
Apache DruidApache 2.0$0 self-hostedSQL and native queriesSelf-managed onlyStreaming dashboards
kdb+ProprietaryQuote onlyq and qSQLNot applicableCapital-markets tick data

How I Reviewed QuestDB

I installed QuestDB 10.1.0 on release day on my Apple M1 Pro laptop with 32 GB RAM and loaded about 25M synthetic rows across four tables through ILP and QWP with the Python client.

I ran SAMPLE BY, LATEST ON, ASOF JOIN, SUBSAMPLE and a materialized view, tested dedup, out-of-order writes and schema drift, converted and exported Parquet, and queried over PostgreSQL wire with psycopg. I weighed edition features and pricing against TimescaleDB, InfluxDB 3 and ClickHouse.

Enterprise and BYOC were untested, as were the MCP server, the Web Console UI, Grafana and Kafka Connect. The timings come from one laptop with synthetic data, not a controlled benchmark or a sustained production run.

Prices current as of October 2026.

Should You Use QuestDB for Time-Series Data?

I recommend QuestDB for SQL-driven time-series workloads: my 10M-row ILP load finished in 6.1 seconds, and the full-week trade/quote ASOF aggregate took 1.78 seconds. Decide on deletion, security and availability needs, since ingest speed won’t be the problem.

Use QuestDB if:

  • You store market data or telemetry and want time-aware joins without application-side matching.
  • Your retention fits whole partitions, and dedup can absorb replayed events.
  • You can run a protected single instance, or budget for an Enterprise quote.

Skip it if:

  • You need routine row deletes or full PostgreSQL semantics. I’d choose TimescaleDB.
  • You need supported HA with a public price and no sales call.
  • Your main workload is broad, wide-event OLAP. I’d shortlist ClickHouse first.

For the next step, run the Docker image pinned to 10.1.0 with ports kept local, load a day of your own data, and test your three heaviest queries before you talk to sales.

QuestDB FAQ

Is QuestDB free?

QuestDB Open Source is free under Apache 2.0, with no license fee for the engine. Enterprise needs a sales quote and adds availability and security controls, and its free trial has unpublished terms. Even on the free edition, budget for hosting, backups and a TLS proxy.

Does QuestDB support DELETE?

QuestDB has no row-level DELETE statement; my delete from query failed with a syntax error. TTL and DROP PARTITION remove whole partitions instead. UPDATE works with limits, including no changes to the designated timestamp. If selective deletion is a core requirement, I’d choose TimescaleDB.

Is QuestDB compatible with PostgreSQL?

QuestDB speaks the PostgreSQL wire protocol on port 8812, but it isn’t fully PostgreSQL-compatible. My psycopg queries worked, including a one-day aggregation in 11 ms and returned 59,442 rows in 97 ms. Scrollable cursors aren’t supported, and index syntax differs, so test your application queries first.

Does open-source QuestDB have high availability?

No. Open-source QuestDB runs as a single instance without replication or automatic failover; those features are Enterprise-only. I didn’t test Enterprise failover, so I’d ask for a recovery demonstration and written availability terms before relying on QuestDB’s stated under-100 ms figure.

Is QuestDB faster than TimescaleDB?

I didn’t run TimescaleDB alongside QuestDB, so my test doesn’t settle which is faster. QuestDB loaded 10M synthetic rows over ILP in 6.1 seconds on my laptop and ran a 10M-by-5M ASOF JOIN in 1.78 seconds. Compare both on your own schema and heaviest queries before choosing on performance.

Spotted a wrong price or a missing integration? Send a correction. A human reads every one.

Similar tools

Other tools in the same category, with the same card and the same honest pricing.

Oxla

OLAP engine

C++ MPP OLAP engine, now Redpanda SQL: read-only Postgres-dialect queries over Redpanda topics and Iceberg history in BYOC.

Visit site

Timeplus

Streaming database

Streaming SQL database with a free Apache 2.0 engine (Proton), a $199/month Cloud plan and self-hosted Enterprise clusters.

Visit site

OpenSearch

Search-analytics

Apache 2.0 search, log-analytics and vector engine forked from Elasticsearch, with security, alerting, SQL/PPL and k-NN included free.

Visit site

InfluxDB

Time-series

Time-series database for metrics, IoT and observability: SQL and InfluxQL, Python plugins; free Core caps queries, Enterprise compacts.

Visit site
Free self-hosted; cloud from $30/month

Tiger Data

Time-series

Tiger Data sells TimescaleDB, a PostgreSQL extension for time-series data, and Tiger Cloud, its managed Postgres service on AWS and Azure.

Visit site

Kinetica

OLAP engine

GPU- or CPU-accelerated SQL database for real-time geospatial, time-series, graph, vector and Kafka streaming analytics.

Visit site

Kyligence

OLAP engine

Commercial OLAP engine built on Apache Kylin, with MDX and cell-level security, plus the Zen metrics platform and Copilot.

Visit site

Hydrolix

OLAP engine

Streaming data lake for high-volume log and event data on object storage, queried with ClickHouse SQL and billed per GB ingested.

Visit site

Aiven

OLAP engine

Managed open source data platform; Aiven for ClickHouse runs ClickHouse on fixed hourly plans on AWS, GCP or Azure with Kafka and PostgreSQL integrations.

Visit site