DuckDB 2.0 Becomes a Server This Autumn. Ask Whether It Deletes the Postgres Box You Keep Out of Habit.
DuckDB has been an in-process database since day one, and that constraint shaped how everyone used it. You reached for it inside a script, not behind a service. The v2.0 preview published on 17 August ends that: any DuckDB process can serve its databases over the network, and any other DuckDB can attach and route queries there.
Release is targeted for this autumn. There are more than 10,000 commits since v1.5 in March. The feature list is long, and most of it is not what matters if you run a one-person product.
What the server mode looks like
The quack extension implements DuckDB's native wire protocol. It shipped as a preview in May, and it graduates to stable in v2.0. On the server:
CALL quack_serve(token = 'my_token');
On the client, the new CONNECT statement:
ATTACH 'quack:server.example.com' AS qk (TOKEN 'my_token'); CONNECT qk; SELECT count(*) FROM events; DISCONNECT;
The query runs on the server and results stream back. CONNECT is not limited to Quack either. It points a session at any remote database that supports it, and a new remote pushdown optimiser ships SQL directly to PostgreSQL and MySQL rather than dragging tables across the network:
CONNECT 'postgres://localhost/mydb'; SELECT count(*) FROM orders; -- runs on the PostgreSQL server DISCONNECT;
DuckDB's team makes a point I had wrong: DuckDB has had full MVCC and transaction isolation since the beginning, and they claim it is fast enough to compete with general-purpose databases on a fair number of transactional workloads. Most people never noticed because a single-user in-process database gives you no reason to care.
The numbers worth remembering
DuckDB published a microbenchmark you can run on a laptop, single-source reachability over a graph with one million edges, written as a plain recursive CTE. v1.5.4 takes 4.90 seconds. The v2.0 preview takes 0.12 seconds. That is roughly 40x, and it comes from a rewritten recursive CTE engine rather than from tuning.
Two more that will affect ordinary queries without you asking. Partial aggregates now get pushed below joins, and redundant aggregations get reused. Aggregations spill to disk when they outgrow memory, which removes a class of out-of-memory failure that previously just killed the process.
The ICU dependency is gone entirely. Timezones, calendars, and collations are now implemented in the icu extension itself, with IANA timezone data compressed to around 45 kB. Converting 25 million timestamps to a timezone went from 0.24 s to 0.11 s, and filtering 5 million strings with a German collation went from 0.15 s to 0.06 s. Smaller binary, faster, and one less vendored library to think about.
Triggers, and what they are actually for
v2.0 ships triggers in full: BEFORE and AFTER, FOR EACH ROW and FOR EACH STATEMENT, transition tables via REFERENCING OLD/NEW TABLE, multiple triggers per event, RETURNING on triggered tables, and DROP TRIGGER.
The canonical use is audit tables, and that is a genuinely useful thing to get for free when you are the only person who will ever debug your data. But triggers in a database you previously treated as a computation step are a different proposition from triggers in a long-running service. If DuckDB is the thing your script opens, computes with, and closes, a trigger is a way to make behaviour invisible to the person reading the script. That person is you in four months.
The stack question
Here is the version of this that matters for a small product. Most solo operators running DuckDB have a shape like this: analytics and reporting in DuckDB against Parquet on object storage, and a separate Postgres instance that exists because something needs to accept a connection from a web process.
v2.0 makes it technically possible to collapse that. It does not make it advisable yet, and DuckDB's own post is careful about this. "Can serve multiple clients" and "has been operated as a multi-tenant production service by a lot of people for several years" are different claims, and only the first one is being made. The post explicitly flags that running DuckDB long-term brings new challenges and that v2.0 pushes on metrics, logs, and observability for exactly that reason. Observability work being in progress is the tell.
My read: if the service you would retire is a managed Postgres costing you $15 a month and holding data you cannot lose, keep it. If it is a box you run yourself, holding derived data you could rebuild from source files in an afternoon, v2.0 is worth a serious look when it ships. The second case is more common than people admit, because that box usually got provisioned during a phase when it was doing more.
The extension change that will matter in a year
Extensions currently build against the unstable C++ API, which means authors must rebuild and republish for every DuckDB release even when nothing in the extension changed. Not rebuilding means your extension cannot be installed on the current version.
v2.0 ships a revamped C API with a versioned specification in YAML, code generation tooling, and lifecycle and stability tags per symbol. A large part of it will be marked stable and frozen, giving a stable ABI across versions, with a thin C++ layer on top that compiles into your extension and talks only to that ABI. Rust bindings are in progress.
You will also be able to register your own signed extension repositories, pinning RSA public keys at CREATE time and comparing SHA-256 fingerprints against something published out of band:
CREATE EXTENSION REPOSITORY my_repo FROM 's3://my-bucket/extensions'
USING PUBLIC KEY '-----BEGIN PUBLIC KEY----- ...';
That is currently marked work in progress. Given the week the Rust ecosystem just had with a malicious crate running a build-time payload, a signed, pinned, self-hosted extension channel is a more interesting feature than it would have looked a month ago.
What I would actually do
Do not migrate anything today. The preview builds exist and have most of these features, and the correct use of them is measurement rather than adoption.
Run the recursive CTE benchmark from DuckDB's post against a preview build on your own hardware. It takes two minutes and it tells you whether the 40x figure is real on your machine, which is worth more than believing it.
Then write down the honest answer to one question: what is the actual reason you run a separate database process? If the answer is "it accepts connections," v2.0 is aimed directly at you and you should plan to test it in the autumn. If the answer involves durability guarantees, a managed backup you did not have to build, or a client library ecosystem you depend on, none of this changes anything and you can skip the release.
Also note the breaking changes before you get excited. v2.0 bumps the default storage format to v2.0.0 and completes the lambda syntax transition. DuckDB says the full list comes with the release announcement, which means it is not published yet.
Where this could be wrong
The main risk in everything above is that I am treating a preview post as a shipped release. Features move, get cut, or land differently. DuckDB says explicitly that details may shift before the autumn release, and the extension repository feature is flagged as work in progress right now.
The other honest counter: "delete a tier from your stack" is the most seductive and most frequently wrong idea in infrastructure. The Postgres box you are thinking about retiring is boring, and boring is the entire feature. Replacing a database that has been running untouched for two years with a v2.0 release of something that just changed its storage format is a trade where the upside is one fewer thing to pay for and the downside is your data. Most of the time, keep the boring thing.
Author
Lukas
@lukcombinator