DuckDB
DuckDB is an embedded analytical database — often called
“SQLite for analytics”. It is a C++ columnar engine with vectorised execution
that queries Parquet, CSV, JSON and Arrow files directly, without a server
process. Think of it as a single-binary OLAP engine you point at your data:
SELECT * FROM read_parquet('s3://bucket/data.parquet') works out of the box
once the httpfs extension is loaded.
As a pgcli addon, DuckDB is a host-binary addon: there is no container and
no daemon. pg addon install duckdb downloads the official CLI zip from GitHub
releases (sha256-verified; a duckdb already on PATH is reused), installs
the httpfs extension (parquet and json are bundled in the binary), and
records the entry in pg.yaml.
Platform: Linux only, amd64 only (macOS and other architectures refuse with a clear error). DuckDB is a standalone CLI tool — it does not run as a background service, does not listen on a port, and does not propagate into running PG instance containers.
Why DuckDB alongside pgcli
DuckDB fills the analytical gap that PostgreSQL’s row-oriented engine does not
target: ad-hoc queries over large Parquet/CSV datasets, data-lake exploration,
ETL pre-processing before loading into PostgreSQL, or quick aggregations on
files that already live in S3. pgcli already manages S3 stores (MinIO, silo,
rustfs, garage, seaweedfs) — DuckDB queries their contents directly via
httpfs.
Usage
Once installed, launch the interactive shell or run one-shot queries:
Parameters
| Flag | Default | Description |
|---|---|---|
--name |
duckdb |
Instance name (config map key) |
--version |
1.5.6 |
Release to download (its sha256 is pinned in pgcli; 1.5.6 LTS and 2.0.0 supported) |
--data-dir |
<base>/addon/duckdb/<name> |
Default database directory for .duckdb files |
--install-dir |
/usr/local/bin |
Where the host binary goes (falls back to ~/.local/bin when not writable — keep it on PATH) |
--no-extensions |
off | Skip installing the httpfs extension (parquet and json are bundled in the binary) |
--force |
off | Rebuild the config entry from the flags |
Daily operations
remove never deletes the binary — it may be shared across multiple named
instances. Pass --clean-data to delete the data directory (database files);
without it, the directory is kept and reported.
Server/Client mode (v2.0.0+)
DuckDB v2.0.0 introduced the Quack protocol for native server/client operation. This allows DuckDB to serve its databases over HTTP, letting multiple clients query the same instance.
Starting a server
The server listens on localhost:9494 by default and prints an auto-generated
auth token. By default, it only accepts local connections — for remote access,
put it behind a TLS-terminating reverse proxy (see
DuckDB docs).
Cross-host access (0.0.0.0 binding)
To accept connections from any network interface, use quack_serve() with
explicit parameters instead of -serve:
Three parameters are needed:
listen_uri='quack:0.0.0.0:9494'— bind to all interfaces (not just localhost)allow_other_hostname=true— bypass the localhost-only restrictiondisable_ssl=true— use plain HTTP (default is HTTPS with auto-generated cert)
Clients connect using the server’s IP address:
Security warning: Binding to
0.0.0.0withdisable_ssl=trueexposes the database to the network without encryption. Use only on trusted networks or behind a firewall. For production, use a reverse proxy with TLS.
Connecting to a server
The -connect flag auto-discovers the token from the server’s output. For
manual connections, copy the token from the server’s startup message.
Key points
- stdin must stay open: the server exits when stdin closes (use
< /dev/zeroortail -f /dev/null |for background operation) - localhost only: remote access requires a reverse proxy with TLS termination
- Auth token: auto-generated, printed at startup, required for client connections
- Not managed by pgcli:
pg addon start/stop duckdbremain no-ops — the Quack server is a standalone process you manage yourself
Extensions
DuckDB’s extension system installs shared libraries on first use. pgcli
pre-installs httpfs at install time — it enables reading files over HTTP/S3.
parquet and json are statically linked into the DuckDB CLI binary, so they
are always available without a network download.
httpfs— read files over HTTP/S3 (read_parquet('s3://...'))parquet— Parquet file reader/writer (bundled)json— JSON file reader (read_json_auto(...)) (bundled)
Additional extensions can be installed later from the DuckDB shell:
Pass --no-extensions at install time if you prefer to manage extensions
manually, or if the host has no internet access at install time.
Accessing S3, Parquet, JSON and CSV data
DuckDB is built for analytical queries over files — no server, no COPY, no
import step. Point it at your data.
Parquet files
JSON files
CSV files
S3 access (httpfs extension)
The httpfs extension enables S3 access. Credentials and endpoint are configured
via CREATE SECRET (v2.0.0+) or legacy SET commands (v1.5.x).
DuckDB v2.0.0+ — CREATE SECRET
DuckDB v1.5.x — legacy SET commands
Connecting to pgcli-managed S3 stores
pgcli manages several S3-compatible stores (MinIO, silo, rustfs, garage,
seaweedfs). DuckDB queries them directly — the s3_endpoint is the store’s
HTTP address, and credentials come from pg addon password.
First, create an S3 store instance and a bucket. The setup is identical across
MinIO, silo (Pigsty’s MinIO fork), and rustfs (Rust reimplementation) — they
all share the same pg mc wrapper and the same DuckDB s3_url_style = 'path':
Then query it from DuckDB — note URL_STYLE 'path' is required for
MinIO, silo, rustfs and compatible stores (they do not support virtual-host style):
For a more permanent setup, create a DuckDB startup file (~/.duckdbrc) with
the CREATE SECRET (v2.0.0+) or SET (v1.5.x) commands — they run on every
session start:
Environment variables for S3
DuckDB reads endpoint and SSL settings from DUCKDB_S3_* environment variables,
but credentials must use standard AWS variables (AWS_ACCESS_KEY_ID,
AWS_SECRET_ACCESS_KEY) — DuckDB’s enable_global_s3_configuration auto-detects these.
v2.0.0 limitation:
URL_STYLEcannot be set via environment variables. It defaults to virtual-host, which breaks MinIO/silo/rustfs (they require path-style). For these stores, useCREATE SECRETwithURL_STYLE 'path'instead of env vars.
For MinIO/silo/rustfs, you still need s3_url_style = 'path' because these
stores don’t support virtual-host style URLs.