One Binary, One File, No Daemon: The SQLite and nginx Pattern as a Product Strategy
Compare two quickstarts. The first says to install Docker, bring up a compose file with a database, a cache and a worker, create an account, and paste a token into an env file. The second says to download one file and run it. Both tools might solve the same problem. Only one of them survives an evaluation that lasts ten minutes.
The second quickstart is a product decision, made in the architecture. SQLite and nginx made it decades ago, and Caddy, DuckDB, ripgrep, jq and Hugo have made it again in their own corners. Small surface area, few dependencies and one artifact to copy work as an adoption strategy. For a one-person or AI-assisted project they also work as the only scope control that holds up. That’s the claim, and the rest of this post tries to break it.
What the Pattern Removes
Time to first success comes first. SQLite ships as a single C file, the amalgamation, and in Python import sqlite3 is the whole installation. Caddy is one binary with automatic HTTPS. ripgrep, jq and Hugo ship as single binaries you copy to a machine and run. Every dependency in a quickstart is a place where an evaluation can die. Think of the account that needs email confirmation, or the database that needs a password. Remove them and the first success arrives before the reader’s attention leaves.
Next is the cost of running the thing afterward. An embedded database has no port to expose and no process to monitor. The file is the unit you back up (use the backup API or VACUUM INTO while writers are active). When you outgrow that, the next step arrives as another small tool: Litestream replicates the file to object storage without the application changing. nginx is a daemon, since a web server has to be one, but it’s one process tree driven by plain-text configuration that reloads without dropping connections.
Then trust. SQLite is in the public domain, and the project says it intends to support its file format through the year 2050. That’s an unusual promise to make to someone building on you. A file you can copy, open with a standard tool and keep for decades needs no vendor to stay alive. A stable format also turns other people’s tools into your distribution: anything that can open the file is a front end you didn’t have to write. DuckDB brings the same trick to analytics: it runs inside your process and queries CSV, JSON and Parquet files where they sit. This site is built with Hugo, which turns a folder of Markdown into a folder of HTML, so there’s no server to patch (the case for files over servers is made at length elsewhere).
Why It Suits a Solo or AI-Assisted Project
The constraints that make a tool easy to adopt make it feasible to build alone. My rule of thumb is a codebase of 10,000 to 30,000 lines in C, Rust, Zig or Go. One person can hold that in their head. A model can reason about it more reliably than about forty services, because the bugs in a distributed system live in the contracts between the pieces, and no single file contains those. A failure in one binary reproduces by running it. A change touches one program, and one test suite covers the change.
AI assistance adds a twist. Code is cheap to produce now, and a model will add the forty-first service the moment you ask. Scope becomes the scarce thing, so the pattern works as a budget you set before the first prompt. Writing 20,000 lines was never the hard part; vibe coding works until you have to read the code. Deciding what the program refuses stays a human job, and architectural coherence is the discipline a model can’t substitute for.
Where the Pattern Breaks
The pattern doesn’t delete complexity. SQLite contains a query planner, crash-safe transactions and a file format with compatibility promises. That weight moves into one codebase that one team maintains, instead of being re-solved in every deployment. The author pays once so every user pays little, which is why the pattern is hard to copy as a slogan. It also means a single binary isn’t automatically simple: it can hold forty subsystems and a plugin API, and the shape of the artifact says nothing about the size of its surface.
The constraints only hold if you refuse features, and refusing is a recurring cost. Every “could it also do X” is a vote to break the pattern, and the answer is usually no.
A stable file format is a promise that costs effort forever. Every field you add is a migration you owe to files you’ll never see, so version the header on day one and keep old files in the test suite.
Distribution is real work: signing so the operating system doesn’t warn people off, packages for the managers they use, and a way to deliver a security fix to someone who downloaded one file two years ago. “Download this one file” assumes the file runs on their machine.
There’s a ceiling, too. No daemon means no shared state across machines. SQLite allows one writer at a time, and its locking is unreliable on network filesystems, so many writers on many hosts sits outside the pattern. That’s fine if you know where the line is.
And a free file still needs an answer to who pays, which no architecture supplies. The four questions in Screening App Ideas Before You Write Code start there.
A Checklist, and Who Has Tried It
If I were choosing what to build next, I’d impose this list up front and make every exception argue for itself:
- One binary or one library.
- No mandatory daemon where embedding works.
- No external database, no Redis, no Docker requirement.
- Human-readable configuration.
- A stable on-disk format, wherever there’s a disk format.
- A C API if it’s a library, with bindings later.
- A README where the complete, useful example fits on the first screen.
The last item is the cheapest to check and the one most often skipped. If the example doesn’t fit, the tool is doing too much or explaining too little. The C API line is there for reach: nearly every language can call C, so a small library with a plain C interface gets its bindings later, from you or from strangers. That’s how SQLite became available from nearly every language, Python’s standard library included.
Several other posts here apply the pattern to other pieces of infrastructure: a job queue in one SQLite file, Redis semantics without Redis and a log database you hand to someone as a file. The cleanest first project of the lot is recording an agent’s MCP traffic once and replaying it in CI, and its whole README could fit on one screen (the tool is a proposal, so these commands are illustrative):
$ curl -LO https://example.com/dl/tool-linux-amd64 && chmod +x tool-linux-amd64
$ ./tool-linux-amd64 record -o run.cassette -- ./my-agent --task fix-tests
recorded 212 requests to run.cassette (1.8 MB)
$ ./tool-linux-amd64 replay run.cassette -- ./my-agent --task fix-tests
replay ok: 212/212 matched, 0 network calls
A thesis post on apicoding.com about reducing data near the source makes the same argument about data.
Three projects covered on this site are early attempts. BareProxy is a small Go web server and reverse proxy with a core design budget under 5,000 lines and no dependencies outside the Go project. Its routing has no regular expressions or scripting, which is what lets bareproxy explain say which rule matched a URL and why; caching, compression, rate limits and auth are optional modules. AltSql is a hybrid of key-value and SQL whose core compiles to about 15 KB of code for a microcontroller, and the gateway database stores the same records byte for byte. It’s alpha, with the hardware simulated so far. Precomputing compiles a short policy into plain SQLite triggers, so the answers live in a file any SQLite tool can open; it’s version 0.1, tested on simulated data. All three are early. They show what holding to the budget looks like in practice.
Write the one-line quickstart first, then build only what makes it true.