# sqlite3-ruby is the Ruby binding to SQLite, with fork and thread rules spelled out

> The sqlite3 gem gives Ruby programs access to the SQLite3 database engine through the SQLite3::Database, Statement and ResultSet classes, and its README spends more space on thread and fork safety than on the query API. That is the right priority for a binding, and the fork behaviour in particular is something an application inherits whether it planned for it or not.

**sparklemotion/sqlite3-ruby** — Ruby bindings for the SQLite3 embedded database

- Repository: https://github.com/sparklemotion/sqlite3-ruby
- Website: https://sparklemotion.github.io/sqlite3-ruby/
- Stars: 845 · Forks: 223
- Language: Ruby
- License: BSD-3-Clause
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/sparklemotion-sqlite3-ruby

## A binding to the engine, not a layer above it

This library allows Ruby programs to use the SQLite3 database engine. The source is at github.com/sparklemotion/sqlite3-ruby, the gem is published on rubygems.org as sqlite3, and the documentation lives at sparklemotion.github.io/sqlite3-ruby. The primary language of the repository is Ruby, and the C extension that wraps the engine sits in the ext directory beside the Ruby code in lib.

One compatibility note appears at the top of the README and should not be skipped: the module is only compatible with SQLite 3.6.16 or newer. That is the floor, not the target, but it matters for a binding because the native extension is compiled against a specific SQLite version and shipped with the platform gem.

The SQLite source code itself is distributed in the ruby platform gem, and the README notes that this code is public domain with a pointer to the SQLite copyright page. So the dependency chain is unusual in a useful way: the engine is not an external library you install, it arrives inside the platform gem, and the licence arrangement for the engine is separate from the BSD-3-Clause licence of the Ruby wrapper.

The audience is anyone who wants an embedded database in a Ruby program without running a database server, which in practice means tools, scripts, desktop applications and services that want durability without the operational cost of a server.

## Three classes carry the API

The README names the classes whose APIs are most often used, and links each to its generated documentation.

SQLite3::Database is the connection, and it is the object you open, execute against and close.

SQLite3::Statement is a prepared statement, and it is the object that matters for repeated execution with different parameters.

SQLite3::ResultSet is the cursor over the rows a statement produced.

The quick start shows the whole cycle in one script, opening a database, creating a table, inserting rows and reading them back:

```ruby
require "sqlite3"

# Open a database
db = SQLite3::Database.new "test.db"

# Create a table
rows = db.execute <<-SQL
  create table numbers (
    name varchar(30),
    val int
  );
SQL

# Execute a few inserts
{
  "one" => 1,
  "two" => 2,
}.each do |pair|
  db.execute "insert into numbers values ( ?, ? )", pair
end

# Find a few rows
db.execute( "select * from numbers" ) do |row|
  p row
end
# => ["one", 1]
#    ["two", 2]
```

The same script goes on to create a second table with several columns and insert a row using parameter markers, which is the shape to prefer over string interpolation for anything that contains user input. The block form of execute iterates the result rows as they arrive, and the rows come back as arrays.

For the full picture, the README points at the FAQ in the repository and the API documentation on the site, and both are worth reading before you settle on a pattern, because the convenience methods on the database are not the whole surface.

## Only the database handle is safe to share across threads

The thread safety section is the longest and most prescriptive part of the README, and its first move is to correct a common misreading.

When SQLite3.threadsafe? returns true, SQLite3 has been compiled to support running in a multithreaded environment. That does not mean all classes in the gem can be considered thread safe.

When threadsafe? is true, it is safe to share only SQLite3::Database instances among threads without providing your own locking mechanism. The README's first example opens an in-memory database, starts ten threads and lets a Queue release them together, with only the database instance shared.

Other instances can be shared, but they need your own locking. SQLite3::Statement objects, the prepared statements, are mutable, so applications must add appropriate locks to avoid data races. The second example in the README shows the corrected version: a statement is prepared once, a Mutex is created, and each thread enters stmt_lock.synchronize before binding values and walking the results, because binding and iterating mutate the statement and other threads would otherwise see each other's data.

The recommendation at the end is the one to take seriously: if applications want to share a database among threads, they should only share the database instance object. Other objects are fine to share, but may require manual locking for thread safety.

That is a different shape from most database bindings, where a connection pool is the norm, and it means the connection management in a Ruby application that uses this gem is a design decision rather than a configuration setting.

## After a fork, the gem closes your writable handles for you

The fork safety section describes a problem and then a mitigation, and the mitigation is the part that changes application behaviour.

The problem is upstream. SQLite is not fork safe, and the SQLite documentation instructs users not to carry an open writable database connection across a fork. Using an inherited connection in the child may corrupt your database, leak memory, or cause other undefined behavior.

The gem's response is to protect users from accidental corruption. Immediately after a fork, it closes any open writable databases in the child. Discarding writable connections in the child incurs a small one-time memory leak per connection, and the README's judgement is that this is preferable to potentially corrupting your database.

So the behaviour you inherit is that a child process no longer has a usable writable handle. If your application forks a worker pool after opening the database, the workers will find their handles closed unless the parent closed them before forking, which is the guidance the README gives: whenever possible, close writable connections in the parent before forking.

There is an escape hatch for the cases where you know what you are doing. If it is absolutely necessary, you may suppress the fork safety warnings by calling SQLite3::ForkSafety.suppress_warnings!.

The context and the reasoning behind the decision live in the repository's architecture decision record, adr/2024-09-fork-safety.md, and that file is worth reading if you are deploying a forking server, because it is where the trade-off between a small leak and a corrupted database was actually argued.

## Installing the gem and the native extension

The gem is published on rubygems.org, and the README's support section points installation and extension questions at INSTALLATION.md in the repository, which is the document to read when the build fails.

The reason a separate installation document exists is the native extension. The repository has an ext directory for the C code and a patches directory, a dependencies.yml file, and a sqlite3.gemspec, and the build goes through a Rakefile with a rakelib directory of build tasks. The gem also ships the SQLite source itself in the ruby platform gem, so the engine is compiled as part of the installation rather than being linked against a system copy.

The project uses a Gemfile for development, has a test directory driven by the test workflow in the CI configuration, and carries a .rubocop.yml for style, an .rdoc_options file for the generated documentation, and a .gemtest file. There is also a CHANGELOG.md, a SECURITY.md, a FAQ.md and a CONTRIBUTING.md.

The release history shows active work on the native side. Version 2.9.6 was published on 2026-08-11, version 2.10.0 on 2026-10-13 and version 2.10.1 on 2026-10-19, the same day as the last push to the main branch.

Help comes from two places. The mailing list at groups.google.com/group/sqlite3-ruby is where general questions go, and the GitHub discussions board has a Q and A category, while bugs belong in the issue tracker.

## The alternative, and the difference in approach

The other way to work with SQLite from a Ruby program is not to use this binding. You can shell out to the sqlite3 command line tool, or you can reach the same database file from another language's driver.

The difference in approach is where the engine lives. This gem embeds it in your process through a native extension, so queries are calls across the language boundary and the results are Ruby objects, and the whole thing is one artefact in your Gemfile. The command line route keeps the engine in a separate process, which means you pay for process startup, you exchange data through text, and the shape of what you can do is limited to what the tool exposes. Another language's driver gives you the same file and the same SQL, with the cost of a second language in your stack and the impedance of moving data across the boundary.

There is a third option that sits above both, and it is worth naming because it is what many Ruby applications actually use: an ORM, with SQLite as one of its backends. That gives you migrations, a query builder and model mapping, and it means the fork and thread rules apply to the connection the ORM holds rather than to something you opened yourself.

The trade is between directness and convenience. A binding gives you SQL and the engine's own behaviour, with no layer between your code and the database, and you take on the connection discipline the README describes. An ORM removes that discipline from your code and adds conventions you have to learn.

The binding is the right choice when the SQL is the point, and the wrong choice when you want a data layer rather than a database.

## Conclusion

Adopt sqlite3-ruby when your Ruby application needs an embedded database with direct SQL access and no server to run, and when you control how connections are opened and shared. Do not adopt it if you need a database with concurrent writers, a query builder, or migrations, since this is a binding to the engine rather than a layer above it. Before you rely on it, read the fork safety decision record in adr/2024-09-fork-safety.md, because the gem will close your writable handles in a child process, and decide whether sharing a single Database instance across threads, which the README calls the generally recommended approach, fits your process model.

## FAQ

### How do I install the sqlite3 Ruby gem?

The gem is published on rubygems.org as sqlite3, and the README directs installation and extension problems to INSTALLATION.md in the repository. The SQLite engine source itself is distributed in the ruby platform gem and is public domain, separate from the BSD-3-Clause licence of the Ruby wrapper.

### Which SQLite version does sqlite3-ruby require?

The README states that the module is only compatible with SQLite 3.6.16 or newer. The gem is published as sqlite3 and its documentation is at sparklemotion.github.io/sqlite3-ruby.

### Is it safe to share a SQLite3::Database between threads?

Yes, when SQLite3.threadsafe? returns true, and only the database instance should be shared. Prepared statements such as SQLite3::Statement are mutable, so the README's second example wraps their use in a Mutex to avoid data races between threads.

### What happens to my database connection after a fork?

Because SQLite is not fork safe, the gem closes any open writable databases in the child process after a fork, at the cost of a small one-time memory leak per connection. The README advises closing writable connections in the parent before forking, and the warnings can be suppressed with SQLite3::ForkSafety.suppress_warnings!.

## Sources

- [License: BSD-3-Clause](https://github.com/sparklemotion/sqlite3-ruby/blob/main/LICENSE)
- [Project website](https://sparklemotion.github.io/sqlite3-ruby/)
- [README](https://github.com/sparklemotion/sqlite3-ruby/blob/main/README.md)
- [Releases](https://github.com/sparklemotion/sqlite3-ruby/releases)
- [sparklemotion/sqlite3-ruby on GitHub](https://github.com/sparklemotion/sqlite3-ruby)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/sparklemotion-sqlite3-ruby
