system.data.sqlite-binaries: How Duplicati Built Its Own SQLite Supply Chain
A deep look at the repo that cross-compiles native SQLite for the platforms upstream does not fully cover, then packages it into the exact .NET-compatible shape Duplicati needs.
This project contains builds for additional architectures and systems on `System.Data.SQLite.Core` NuGet Package.
- This repository is a dependency-ownership play, because Duplicati needs to control how SQLite ships across operating systems and chips.
- The core trick is not wrapping SQLite, but reassembling it into a controlled native binary with feature flags, interop glue, and extension code baked in.
- Docker and cross-compilers turn one source tree into a multi-arch output matrix, which is the real product this repo manufactures.
- Compared with general-purpose SQLite providers, this repo exists to solve a narrower but harder problem: predictable native delivery for one application.
Most people think of SQLite in .NET as an API choice. This repo argues the harder problem is elsewhere: native distribution. Duplicati cannot rely on upstream packaging to cover every architecture it cares about, so it manufactures the binaries itself.
That decision turns a quiet infrastructure repo into a strategic asset. It gives Duplicati control over feature flags, build targets, and platform-specific quirks that would otherwise sit outside its reach.
The hidden database factory behind Duplicati
The repository exists because Duplicati depends on SQLite as more than storage. Its local database tracks remote backup state, blocks, file hashes, and indexes, which lets the app avoid repeated reads from object storage. That is why the native layer matters so much: if SQLite is unstable or missing on a target platform, the whole backup workflow weakens.
In Duplicati's own framing, SQLite is the local map of the remote world. The repository that ships its binaries is therefore part of the product's core plumbing, not a sidecar dependency.
Why a backup app needs its own SQLite binaries
The important point is not that Duplicati uses SQLite. Plenty of .NET apps do. The important point is that Duplicati needs SQLite to behave consistently across a messy support matrix that includes Linux distributions, macOS, Windows, and multiple CPU families.
That is where project-specific binary ownership becomes valuable. Instead of waiting for a package matrix to line up, Duplicati can ship the exact native surface area it expects.
The repo does not wrap SQLite. It reassembles it
The source tree makes the intent plain. The heart of the project lives under `netfx-src/SQLite.Interop`, where the C interop layer pulls in `sqlite3.c` directly rather than linking against some external shared object. That is the opposite of a thin wrapper. It is a controlled amalgamation.
This matters because it gives the build pipeline ownership over what gets compiled in. Features can be turned on or off at build time, extensions can be bundled, and the final native artifact is shaped to fit the application rather than a generic package.
That reassembly strategy is what makes the repo interesting. It is not trying to abstract SQLite away. It is trying to own the exact compilation boundary where SQLite becomes a Duplicati-compatible binary.
#include "../core/sqlite3.c"
#if defined(INTEROP_EXTENSION_FUNCTIONS)
#include "../contrib/extension-functions.c"
#endif
#if defined(_WIN32)
#define INTEROP_EXPORT __declspec(dllexport)
#else
#define INTEROP_EXPORT __attribute__((visibility("default")))
#endif
Interoperability is where the real engineering lives
The portability work is not decorative. `interop.c` has to satisfy .NET expectations on both Windows and Unix-like systems, which means handling export symbols, atomic operations, and the little compatibility edges that let managed code talk cleanly to native code.
That is also why the file carries its own compile-time reporting and defensive platform logic. The repository is not just making a binary. It is documenting, in code, exactly what that binary contains and how it should behave.
#if !defined(_WIN32)
static long InterlockedIncrement(long *value) {
return __sync_add_and_fetch(value, 1);
}
static long InterlockedDecrement(long *value) {
return __sync_sub_and_fetch(value, 1);
}
#endif
This is the kind of work that disappears when it succeeds. That invisibility is the point: if the interop layer is doing its job, the rest of Duplicati can pretend SQLite is just there.
How Docker turns one workstation into many build targets
The build scripts turn a single development machine into a small cross-compilation factory. Docker is not just a container here. It is the orchestration layer that lets the project produce native outputs for `386`, `amd64`, `arm/v7`, and `arm64/v8` without asking the developer to own every environment manually.
That matters because native distribution has a long tail of failure modes. A build that works on one host can miss a library, a compiler flag, or an architecture-specific assumption on another. The containerized pipeline reduces that chaos by standardizing the build surface.
| Layer | What it owns | What it optimizes for | Typical user |
|---|---|---|---|
| System.Data.SQLite | Official provider and upstream packaging | General ADO.NET compatibility | Apps that want the standard package |
| Microsoft.Data.Sqlite | Modern .NET provider | Lightweight managed integration | Teams on current .NET stacks |
| SQLitePCLRaw | Native loading and raw access | Cross-platform abstraction | Libraries that want portability |
| duplicati/system.data.sqlite-binaries | Project-specific native builds | Exact platform coverage and feature control | Duplicati and its target matrix |
This is the repo's strategic niche. It does not compete with the mainstream providers on breadth of audience. It fills the gap where a specific application needs control over native delivery more than it needs another abstraction layer.
The extension pack makes SQLite feel like a different database
One of the more interesting details is how the repository bakes in extension code. Files like `extension-functions.c` add math and string functions, plus aggregates such as `median` and `mode`. That shifts the project from binary packaging toward feature composition.
At that point, the binary is not just SQLite. It is a curated SQLite flavor with a sharper SQL dialect and a deliberately chosen surface area.
#if defined(INTEROP_EXTENSION_FUNCTIONS)
/* math, string, and aggregate helpers are compiled into the binary */
#endif
That matters because feature choices made at compile time are easier to control than feature choices left to deployment-time dependency resolution. The repo treats the binary as a product boundary.
What this repo is compared with official SQLite providers
The cleanest way to understand the repo is as a division of labor. The official provider is the baseline. `Microsoft.Data.Sqlite` is the modern managed path. `SQLitePCLRaw` is the portability layer. Duplicati's repo is the manufacturing layer that makes the whole stack fit a specific deployment reality.
| Project | Native binary ownership | Cross-platform breadth | Feature tuning | Main advantage | Main limitation |
|---|---|---|---|---|---|
| System.Data.SQLite | Partial, upstream-led | Broad but package-driven | Moderate | Established ADO.NET provider | Not every target is packaged the way Duplicati needs |
| Microsoft.Data.Sqlite | Delegated through loading | Strong on modern .NET | Low to moderate | Simple modern API | Relies on other layers for native delivery |
| SQLitePCLRaw | Managed over raw native loading | Very broad | Low | Portable abstraction | Not a project-specific build pipeline |
| duplicati/system.data.sqlite-binaries | Fully owned by Duplicati | Focused on Duplicati targets | High | Exact binary and feature control | Narrow purpose |
That distinction is the whole article. This repo exists because Duplicati refuses to treat the native layer as somebody else's problem.
Why this kind of infrastructure repo matters
Infrastructure repos like this rarely get attention because they do not ship a user-facing feature. But they decide whether the user-facing feature works on real hardware, in real packaging environments, across the awkward long tail of architectures that large ecosystems often ignore.
The lesson is broader than SQLite. Modern cross-platform software still depends on old-school control over C code, compiler flags, and binary distribution. The projects that own that layer gain resilience. The ones that outsource it inherit someone else's schedule and someone else's gaps.
Duplicati's SQLite supply chain is a reminder that in software, control over the native layer is often control over the product itself.