Documentation · Quickstart · Examples · Benchmarks · Releases · Contributing
Serverless DuckDB on GCP. Submit SQL or your own code; DuckLess runs it in your project on a right-sized machine (Cloud Batch VM with local SSD for spill, or Cloud Run Jobs for small jobs), reads and writes GCS with the job's own credentials (no HMAC keys), then tears it down. Nothing runs between jobs. Jobs read Parquet, CSV, JSON, Excel and BigQuery tables, and write BigQuery tables; optional DuckLake tables on a Cloud SQL catalog, and Agent Skills for coding agents.
Status: v0.4, young and tested on real projects. Changes are listed in the GitHub Releases; the benchmarks have the measurements behind the design.
Quick start
uv tool install duckless # or: pip install duckless
duckless init --project my-project --region europe-west1
# prints the lines to put in .envrc (DUCKLESS_PROJECT, DUCKLESS_BUCKET, DUCKLESS_SA, DUCKLESS_IMAGE)
duckless preflight --machine n2-highmem-32 --spot
duckless run job.sql --machine n2-highmem-32 --spot
duckless exec --image <your-image> --machine n2-highmem-16 -- dbt build
duckless status <job-id>
duckless logs <job-id> --follow
duckless result <job-id>
duckless destroy --project my-project # removes what init created
duckless init is optional. It applies the Terraform module shipped with each release
(duckless/terraform) through Infrastructure Manager: handy to start in five
minutes. Teams that keep their infrastructure as code call the same module from their own Terraform
and CI/CD instead, with their deployment identity, and never run init:
module "duckless" {
source = "git::https://github.com/tosun-si/duckless.git//duckless/terraform?ref=v0.4.2"
project_id = "my-project"
region = "europe-west1"
runner_image_tag = "0.4.2"
}
The CLI only reads the settings the module outputs (envrc), however they were created.
Full guide: tosun-si.github.io/duckless, from the quickstart to the CLI reference.
Writing jobs
-
SQL (
.sql):${VAR}placeholders come from the job env (DUCKLESS_BUCKET,--env K=V). -
Python (
.py):from duckless_runtime import connectgives a DuckDB connection with GCS auth, spill on local SSD, memory and threads sized to the VM. -
Write to GCS in parallel:
COPY … TO 'gs://…' (FORMAT parquet, PER_THREAD_OUTPUT, FILE_SIZE_BYTES '256MB')is 7-8x faster than the single-writer default (~900 MB/s vs ~110 MB/s on 32 vCPU). -
Vectorize Python logic: a row-wise Python UDF runs at ~8k rows/s on one thread; use an Arrow UDF over numpy (
type="arrow") or SQL. -
Read BigQuery:
bigquery_scan('project.dataset.table', filter = '…')in SQL,read_bigquery(con, table, columns, where)in Python; only the columns and rows asked for leave BigQuery (duckless init --bigquery-dataset <dataset>first). -
Write BigQuery:
to_bigquery(con, query, "project.dataset.table", mode=...): Parquet, a free load job, thenreplace_partitions(deletes whole partitions for free, bills only the new rows) or amergethat reads only the partitions it touches (guide;duckless init --bigquery-write-dataset <dataset>first).
More in Writing jobs and Machines, Spot and spill.
Examples
Runnable pipelines in examples/, on data generated by the first one:
| Example | What it shows |
|---|---|
01-daily-marts |
raw Parquet to marts in plain SQL |
02-ducklake-incremental |
CSV loads and late JSON corrections in a DuckLake table, time travel |
03-raw-validation |
validating a raw CSV against YAML rules: 100 million lines in about a minute |
04-soda-contracts |
the same checks as a Soda v4 data contract, verified through DuckDB |
05-bigquery-to-python |
a BigQuery table, scored by vectorized NumPy logic, written to GCS |
Agent Skills
DuckLess ships a Claude Code plugin with five skills: setup, writing-jobs, sizing,
troubleshooting and ducklake. They carry what the docs and the CLI cannot decide for you
(which machine, why a job failed) and the rules measured while building DuckLess.
duckless skills install # into this project: .claude/skills + .agents/skills (--user: your home)
Or as a Claude Code plugin, kept up to date from this repository:
/plugin marketplace add tosun-si/duckless
/plugin install duckless@duckless
Layout
Hexagonal, kept light: a pure core, ports, adapters, and one wiring point.
| Path | What |
|---|---|
duckless/core/ |
Pure rules, no I/O: machine types and local SSD counts, job spec and planning, quotas, preflight |
duckless/ports.py |
What the service needs from outside: Executor, ArtifactStore, LogReader, QuotaReader, InfraBootstrap, InfraDeployer (Protocols) |
duckless/service.py |
Operations shared by the CLI, the SDK and later the SaaS control plane: functions taking ports as arguments |
duckless/adapters/ |
GCP implementations: Cloud Batch, Cloud Run Jobs, GCS, Cloud Logging, Compute quotas, Infrastructure Manager |
duckless/wiring.py |
Binds the service functions to the adapters (lazily) |
duckless/cli.py |
duckless command, a driving adapter |
runtime/ |
Runner image (duckless_runtime), published as ghcr.io/tosun-si/duckless-runner: DuckDB with the gcs, bigquery, ducklake, excel, avro and iceberg extensions, the Cloud SQL Auth Proxy, tuned for the machine |
examples/ |
Runnable pipelines, with the unit tests of their logic |
duckless/terraform/ |
The Terraform module (callable from your own Terraform): APIs, work bucket, least-privilege runner service account, Artifact Registry remote repository proxying the runner image, optional DuckLake catalog and BigQuery access |
duckless/plugin/ |
Claude Code plugin: the Agent Skills (duckless/plugin/skills/), listed by .claude-plugin/marketplace.json |
spike/ |
The spike that validated the approach, kept as a record |
Dependency rule: core imports nothing else from DuckLess, service only core and ports,
and only wiring imports adapters.
Contributing
Issues and pull requests are welcome: bug reports, real use cases, docs, code. See CONTRIBUTING.md for the setup, the conventions and how changes are tested.
uv sync
uv run pytest
uv run ruff check . && uv run ruff format --check .
License
DuckLess is open source under the Apache License 2.0. You can use it, modify it and ship it, commercially or not; keep the license and the notices.
Metadata
Release files for duckless 0.4.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| duckless-0.4.2.tar.gz | 55.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| duckless-0.4.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 127.2 kB
Release files / duckless-0.4.2.tar.gz
| Download URL | duckless-0.4.2.tar.gz |
|---|---|
| Size | 55.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
806272dd927ea27c215def151bcaab0b69221d095f2c352eff88bcc6f17e692b
|
|
BLAKE2b-256 checksum How to use checksums |
6c225f5fc37855ea5feb1872b751bf02b72927499e5f6d8eccf51cc75999966a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.
Transparency logRelease files / duckless-0.4.2-py3-none-any.whl
| Download URL | duckless-0.4.2-py3-none-any.whl |
|---|---|
| Size | 71.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1f4d58cb9bff4b2bab7e8ff1832d0e889603ef8794a0d2de67df18cb9dd92f58
|
|
BLAKE2b-256 checksum How to use checksums |
8ba40716f98a84e73baa6ca3686a33d6f93a9ef50a524f49b0f08c9b049bf572
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.
Transparency log