Overview
Deploying DuckDB-Wasm means serving its components so that the browser can fetch and instantiate them. This page describes each component a deployment needs to serve, how the worker and WebAssembly variants relate, how extensions are served and mirrored, and the security considerations involved.
A DuckDB-Wasm deployment needs to access the following components:
- Main Library Component. Distributed as TypeScript and compiled to JavaScript code. For further details, see below.
- JS Worker Component. Compiled to JavaScript code, possibly instantiated multiple times for threaded environments. For further details, see below.
- Wasm Worker Component. Compiled as a WebAssembly file and instantiated by the browser. For further details, see below.
- DuckDB Extensions. Any relevant extensions the deployment loads. For further details, see below.
Main Library Component
This component is distributed as TypeScript or CommonJS JavaScript in the npm @duckdb/duckdb-wasm package. You can deploy it in one of three ways:
- Bundled together with your application
- Served from a same-origin (sub-)domain and included at runtime
- Served from a third-party CDN such as jsDelivr
It cannot be served as-is: it requires transpilation, because it needs to know the location of the follow-up files — the worker and WebAssembly components — in order to work. The exact steps depend on your setup; see the examples in the repository. For instance, the shell.duckdb.org deployment transpiles the main library together with the shell code (the first approach above), while the bare-browser example shows a minimal setup.
JS Worker Component
This is distributed as a JavaScript file in three different flavors, mvp, eh and coi, and needs to be served as-is. The main library component needs to be informed of the actual location.
The three variants target three different WebAssembly feature sets:
mvptargets the WebAssembly 1.0 (MVP) specehtargets WebAssembly with Wasm-level exception handling added, which improves performancecoitargets WebAssembly with exception handling and threading, which enables parallel query execution; it requires the deployment to be cross-origin isolated (see Cross-Origin Isolation)
You can serve all three and let the library feature-detect the best one with selectBundle, or serve a single variant and instruct the DuckDB-Wasm library on which one to use. The served artifacts are named after the flavor, for example duckdb-browser-coi.worker.js. The coi flavor corresponds to the wasm_threads extension platform.
Wasm Worker Component
This is the DuckDB engine itself, compiled to WebAssembly and instantiated by the browser. Like the JS Worker component, it comes in the same three flavors, each paired with the JS worker of the same flavor:
mvp— for exampleduckdb-mvp.wasm, loaded by themvpJS workereh— for exampleduckdb-eh.wasm, loaded by theehJS workercoi— for exampleduckdb-coi.wasm, loaded by thecoiJS worker
Each JS worker loads the WebAssembly module of its own flavor, so serve the module (or modules) that correspond to the JS workers you deploy. If you serve all three JS workers and let selectBundle feature-detect the best one, serve all three modules as well.
When serving these files, keep the following in mind:
- Serve them as-is. Unlike the main library component, they require no transpilation or bundling.
- Serve them with the
application/wasmcontent type so the browser can compile them efficiently. - Host them anywhere reachable from the main document — the same origin or an arbitrary [sub-]domain. The main library component is told each module's location through the bundle definition (the
mainModulepath), so the modules do not need to sit next to the JavaScript.
DuckDB Extensions
DuckDB extensions for DuckDB-Wasm, similar for the native cases, are served signed at the default extension endpoint: https://extensions.duckdb.org.
If you are deploying duckdb-wasm you can consider mirroring relevant extensions at a different endpoint, possibly allowing for air-tight deployments on internal networks.
SET custom_extension_repository = 'https://some.endpoint.org/path/to/repository';
Changes the default extension repository from the public https://extensions.duckdb.org to the one specified. Note that extensions are still signed, so the best path is downloading and serving the extensions with a similar structure to the original repository. See the additional notes on Creating a Custom Repository.
Community extensions are served at https://community-extensions.duckdb.org, and they are signed with a different key, so they can be disabled with a one way SQL statement such as:
SET allow_community_extensions = false;
This will allow loading only of core duckdb extensions. Note that the failure is at LOAD time, not at INSTALL time.
Please review the Extension Distribution page for general information about extensions.
Cross-Origin Isolation
The coi bundle runs multiple threads, which relies on SharedArrayBuffer. Browsers only expose SharedArrayBuffer to pages that are cross-origin isolated. To serve the threaded bundle, the top-level document must be delivered with the following HTTP headers:
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-origin
These headers isolate the document from other cross-origin documents and require any cross-origin resource it embeds to explicitly opt in. Because many third-party endpoints do not yet send the headers needed to be embedded under these policies, most deployments run on non-isolated pages, where DuckDB-Wasm falls back to the single-threaded mvp or eh bundle.
For more background on why these headers are required and how they unlock SharedArrayBuffer, see the following resources:
Security Considerations
Warning Deploying DuckDB-Wasm with access to your own data means whoever has access to SQL can access the data that DuckDB-Wasm can access. Also, DuckDB-Wasm in the default setting can access remote endpoints, so it can have a visible effect on the external world even from within the sandbox.
Further Reading
- Instantiate — how the library, worker, and WebAssembly components served here are wired together at runtime.
- Load Extensions — how extensions are fetched, signed, and served from a custom repository.
- Extension Distribution — general information about extension repositories and creating a custom one.
- DuckDB Wasm Client — the layered API and example deployments this page builds on.
- Troubleshoot — cross-origin isolation for threading and other deployment-related issues.