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-runner is built from the workspace in Cargo.toml.
  • The binary starts in crates/duckle-gpu/src/lib.rs (no main.rs in the crate, the workspace exposes it as a binary).
  • From there, duckle-gpu calls duckdb-engine::compile_pipeline defined in crates/duckdb-engine/src/lib.rs.
  • duckdb-engine parses the pipeline definition, loads connectors via connectors::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 in apps/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-engine is the core hub; changes here propagate to every pipeline run.
  • connectors is 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/ and crates/connectors/src/.
  • 109 Markdown and documentation files in docs/ and README.md.
  • CI defined in .gitlab-ci.yml and several GitHub Actions workflows.
  • Dual license (MIT & Apache‑2.0) declared in LICENSE-APACHE and LICENSE-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.