Nginx SQLite Module (ngx_http_sqlite_module) is a standalone dynamic module for building configured HTTP APIs directly inside Nginx. Instead of letting clients submit arbitrary SQL, every route owns its SQL in Nginx configuration and accepts only the bound parameters that operation explicitly declares.
Version 1.0 turns that compact idea into a production-hardened runtime. HTTP database work runs on Nginx thread pools by default, independent connection lanes keep slow queries from blocking a worker, and bounded execution protects the server with query deadlines, cancellation, and row, body, and response limits.
It is useful when an application needs lightweight local persistence, status endpoints, feature flags, device metadata, authentication data, or small admin APIs without adding a separate app server for every read/write path.
Example Uses
- HTTP auth: Store users, API keys, session metadata, or hashed credentials locally and verify submitted secrets with configured SQLite operations.
- Hit tracking: Count requests, record route access, track downloads, or log lightweight event data without shipping every small metric to an external service.
- Simple stats: Expose counters, rollups, recent activity, health rows, feature-flag state, or operational snapshots as JSON endpoints.
- Basic dashboards: Back small internal status pages, device panels, admin tools, or self-hosted control surfaces directly from SQLite.
Key Features
- Nonblocking HTTP execution: SQLite work runs on an Nginx thread pool by default, with configurable per-worker connection lanes and an explicit synchronous compatibility mode.
- Config-owned SQL operations: A
locationbecomes a named SQLite operation with a specific HTTP method, SQL statement or atomic batch, response mode, and parameter list. - Strict, typed bindings: Startup validation checks every named SQL binding. Declared inputs can come from query args, JSON bodies, headers, cookies, or Nginx variables and can be validated or transformed as text, integers, real numbers, UUIDs, booleans, native JSON scalars, BLOBs, or Argon2id hashes.
- Multiple bounded response formats: Routes can return a JSON object, JSON array, NDJSON or CSV output, or a status-only response. BLOBs are encoded as base64, raw JSON columns are supported, and invalid UTF-8 is rejected instead of producing malformed output.
- Execution guardrails: Query deadlines, client-disconnect cancellation, busy timeouts, strict warmup, and row, request-body, and response-size limits keep database work bounded.
- Hardened database policy: Read-only SQL validation and a SQLite authorizer block attachment, arbitrary PRAGMAs, extension loading, and file helpers by default. Bearer tokens can be loaded from a file, with anonymous access enabled only per route.
- Stable HTTP behavior and observability: Constraint, busy, timeout, empty-result, and oversized-response cases map to predictable HTTP statuses and JSON errors, while
$sqlite_*variables and optional metadata headers expose operation results. - Batch, migration, backup, and benchmark tooling: The repository includes atomic multi-statement operations, a concurrency-safe ordered migration runner, SQLite online backup support, a Litestream replication and recovery guide, and portable local or remote load tests.
- Versioned third-party C hooks: Same-worker Nginx modules can execute named, explicitly hookable operations and receive structured rows without raw SQL access or direct
sqlite3handles. Version 1.0 advances this public API to v2. - Self-contained reference environment: Docker Compose builds the module and hook sample, applies migrations, provisions example databases, serves an interactive OpenAPI reference, and runs the feature, error, smoke-test, and benchmark suites.
How It Works
- Build or install the dynamic module, then load
ngx_http_sqlite_module.sofrom Nginx config. - Declare shared defaults such as
sqlite_db, authentication, execution mode, connection count, response format, timeouts, limits, and warmup behavior atserverorlocationscope. - Add a route with
sqlite;,sqlite_operation,sqlite_method,sqlite_sqlorsqlite_batch, and one or moresqlite_paramdeclarations. - At startup, the module warms its connection lanes and validates the configured statements and named bindings. On each request, it validates method, authentication, content type, and limits before dispatching SQLite work to the configured thread pool.
- The worker binds only declared values, enforces the query deadline and cancellation state, then renders the selected bounded response format with stable status and error mappings.
1 | sqlite_db "/var/lib/app/app.sqlite"; |
The Linux installer supports apk, apt-get, dnf, and yum hosts and builds the dynamic module against the installed Nginx version. Because the 1.0 default is thread_pool, Nginx must be built with --with-threads; a location can opt into sqlite_execution sync; as a compatibility fallback.
Pre-1.0 users should review the repository’s migration guide before upgrading. Version 1.0 also enables stricter defaults for JSON content types, foreign keys, bind validation, warmup, the SQLite authorizer, atomic batches, query limits, and the v2 hook API.
Why This Project Matters
This project sits in a practical middle ground: it is not trying to turn Nginx into a full application framework, and it is not a raw database proxy. The useful part is the constraint. Operators define the SQL, accepted inputs, response shape, limits, and access rules in configuration, while clients can only provide values for declared bindings.
The 1.0 execution model preserves that simplicity without making the Nginx event loop wait on SQLite. It is a good fit for self-contained services, embedded dashboards, internal tooling, local-first APIs, or small infrastructure endpoints where running another service would be more moving parts than the job needs.
The hook API also makes the module useful as local persistence infrastructure for other Nginx modules. A third-party module can call an explicitly enabled named operation in the same worker process, get structured rows or a rendered response back, and still avoid owning raw SQL execution itself.
Stack
- C and the Nginx module API for the dynamic HTTP module
- Nginx thread pools for nonblocking HTTP database execution
- SQLite for local embedded storage, prepared statements, WAL, and online backups
- Argon2id for password or API-key hashing helpers
- Make + POSIX shell for Linux install, build, status, and uninstall flows
- Docker Compose for the reference server, OpenAPI explorer, feature tests, and portable benchmarks
- Litestream guidance for off-host backup, read replicas, recovery testing, and manual failover
If you need a compact HTTP surface over SQLite and want SQL, inputs, policy, and resource limits to stay firmly under operator control, Nginx SQLite Module 1.0 is the production-ready version of that idea.