Skip to main content
This guide is for developers who want analytical queries without running a database server. A volume holds Parquet files, DuckDB reads them inside a function, and an agent calls that function as a tool. Prerequisites: CYCLS_API_KEY, and Docker if you want to run locally first.

Architecture

One writer, many readers. That split matters, because a volume is shared files with no locking: concurrent writes to the same file are last write wins.
Do not put a DuckDB database file (.duckdb) on a shared volume and write to it from more than one deployment. DuckDB expects exclusive access to a database file, and the volume gives no locking. Write immutable Parquet files instead, one per partition, and let readers open them read-only.

1. Create the volume and load a file

2. Write the ingest function

warehouse.py
Run it against one day:
COPY ... TO takes a literal path, not a bound parameter, so the date is validated before it reaches the SQL string. Writing one file per day makes a rerun idempotent: the same input overwrites the same partition, which is what makes at-least-once scheduling safe.

3. Backfill with a fan-out

map runs one call per item across autoscaled instances and returns results in input order.

4. Query it

Two safeguards are worth keeping. lake.read_only() means this deployment cannot modify the warehouse even if the SQL tries. SET enable_external_access = false stops DuckDB from reading URLs or local paths outside what is already attached.
query runs arbitrary SQL. Only expose it to callers you trust, or to an agent whose output you are willing to treat as user input. The read-only mount limits the damage to reads, and the external access setting blocks file and network reads from inside SQL.

5. Schedule the ingest

A scheduled function is called with no arguments, so nightly computes the day itself and delegates to ingest. A rerun overwrites the same partition, which is what makes at-least-once delivery harmless here.

6. Give an agent access

Deploy query as a named endpoint, then call it from a tool handler.
analyst.py
The agent image contains no data and no DuckDB. It calls the deployed query endpoint, which keeps the chat container small and the warehouse in one place.

7. Serve a dashboard from the same data

dashboard.py
Three deployments now share one volume: ingest writes, query and dashboard read. Deleting any of them leaves the data untouched.

Operational notes

Next

Scheduled reports

Turn a nightly query into a file users can open.