The Problem
Teams that build ETL pipelines often lock themselves into vendor clouds or pay per‑row fees. They need a way to author pipelines locally, keep the code in source control, and run them on their own infrastructure without vendor lock‑in or hidden costs.
What This Does
duckle is an end‑to‑end ETL/ELT platform that compiles a single pipeline file into SQL that runs inside DuckDB. The engine is written in Rust (see crates/duckdb-engine/), while connectors for CSV, Parquet, PostgreSQL, etc. live in crates/connectors/. A lightweight web console (Dockerfile.web) and a cross‑platform desktop UI (apps/desktop/) provide visual authoring and monitoring. All pipelines are one file in git and can be deployed with duckle-runner serve, which starts the same binary on any machine.
How It Is Wired
Entry points
duckle-runneris built from the workspace inCargo.toml.- The binary starts in
crates/duckle-gpu/src/lib.rs(nomain.rsin the crate, the workspace exposes it as a binary). - From there,
duckle-gpucallsduckdb-engine::compile_pipelinedefined incrates/duckdb-engine/src/lib.rs. duckdb-engineparses the pipeline definition, loads connectors viaconnectors::load(crates/connectors/src/lib.rs), and emits a DuckDB SQL string.- The generated SQL is executed against a local DuckDB instance (
duckdb_engine::execute_sql).
Side effects
- The web console (built with React in
frontend/) talks to the runner over a local HTTP endpoint defined inapps/desktop/src/engine_manager.rs. - The desktop UI (
apps/desktop/src/main.rs) initializes Tauri, loads the web view, and hooks into the runner’s audit trail stored in a local SQLite database.
Blast radius
duckdb-engineis the core hub; changes here propagate to every pipeline run.connectorsis another hot spot; adding a new data source requires edits in this crate and a re‑build of the runner.- The UI crates are isolated – modifications affect only the user interface.
How To Use It
Setup
git clone https://github.com/moses-y/duckle
cd duckle
cargo build --release
Configuration
- Pipeline files live in JSON/YAML (see
benchmarks/pg-to-parquet/pipelines/duckle.json.tpl). - Optional secrets are read from
apps/desktop/src/secrets.rs; the file is currently empty but can be populated with environment variables (export DB_PASSWORD=…).
Running
./target/release/duckle-runner serve --config path/to/duckle.json
The CLI will start a local DuckDB instance, compile the pipeline, and expose a web console at http://localhost:8000. For the desktop UI: cargo run -p duckle-desktop (or build the Tauri target).
Real‑World Use
A data engineer can author a daily ingest from PostgreSQL to Parquet:
{
"name": "pg-to-parquet",
"source": { "type": "postgres", "connection": { "host": "db.local", "user": "app", "password": "${DB_PASSWORD}" } },
"destination": { "type": "parquet", "path": "/data/output.parquet" },
"schedule": "0 2 * * *"
}
Place this file in a Git repo, commit, and deploy with duckle-runner serve. The pipeline runs on schedule without any cloud provider.
Code Health & Issues
- Low/Security – Secret‑shaped paths discovered in
apps/desktop/src/secrets.rs(no secrets committed, but the file exists). - 35 test files located under
crates/duckdb-engine/src/andcrates/connectors/src/. - 109 Markdown and documentation files in
docs/andREADME.md. - CI defined in
.gitlab-ci.ymland several GitHub Actions workflows. - Dual license (MIT & Apache‑2.0) declared in
LICENSE-APACHEandLICENSE-MIT.
The Bottom Line
duckle delivers a fully self‑hosted ETL stack that compiles pipelines to native DuckDB SQL, avoiding vendor lock‑in and per‑row costs. It is production‑ready for teams that already use Rust or need a lightweight, open‑source solution. The codebase is well‑structured, with a clear separation between engine, connectors, and UI, but adding new connectors requires touching the core duckdb-engine crate, which can be a maintenance consideration. Overall, it is a solid choice for data engineers who want control over their pipelines and infrastructure.