Open-source project
ncruces/go-sqlite3 avatar
ncruces/go-sqlite3

go-sqlite3: SQLite in Go with no cgo and no C toolchain

Go bindings to SQLite using wasm2go

1,129 stars37 forksGoMIT

At a glance

What is it?
The ncruces/go-sqlite3 module wraps a Wasm build of SQLite, translated to Go with wasm2go, so a Go program can embed SQLite without cgo and without a C compiler. It offers a database/sql driver, direct access to the C API, a pure Go VFS, and a GORM driver, at the cost of higher memory use.
Who is it for?
Adopt go-sqlite3 if you need to embed SQLite in a Go program and cannot or do not want to depend on cgo, such as a cross-compiled binary or a WebAssembly target. Do not adopt it if memory usage is your binding constraint, since each connection runs in a Wasm sandbox.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The cgo problem, and what Wasm solves

Go bindings to SQLite normally mean cgo, and cgo means a C toolchain in the build, a cross-compilation story that is painful on purpose, and a set of platforms where the resulting binary simply will not build. The ncruces/go-sqlite3 module is a cgo-free SQLite wrapper, and it gets there in a way that is worth understanding.

It wraps a Wasm build of SQLite and uses wasm2go to translate it to Go. The result is a Go module where SQLite is compiled to WebAssembly and then that WebAssembly is translated into Go source, so the whole thing compiles with the ordinary Go toolchain and produces a static binary with no C dependency.

The dependency list is the proof that it works. Go and x/sys are the only required dependencies, and the go.mod shows the rest. The SQLite build itself comes in as github.com/ncruces/go-sqlite3-wasm/v6 at version 6.3.35400, and three smaller modules come from the same author: ncruces/julianday, ncruces/sort, and ncruces/wbt. The optional extras are separated into their own require block: dchest/siphash and google/uuid for specific extensions, golang.org/x/crypto for the encryption VFSes, golang.org/x/text for Unicode support, and lukechampine.com/adiantum for the encryption at rest work.

The module also declares a Go version of 1.26.0, and it retracts two versions, v0.23.2 and v0.4.0, with the comment that they were tagged from the wrong branch. That is a small but reassuring detail, because a retracted version is one that the Go tooling will refuse to select.

The project is MIT licensed, the primary language is Go, the home page is the pkg.go.dev page for the module, and the last push to the repository was on 2026-10-16. The recent releases are v0.35.6 from 2026-09-23, v0.35.5 from 2026-09-16, and v0.35.4 from 2026-09-01.

Four packages, four levels of abstraction

The module is split into four packages, and the split is the API design.

The root package, github.com/ncruces/go-sqlite3, wraps the C SQLite API and gives direct access to most of it. This is the layer for when you need something the database/sql driver does not expose.

The driver package, github.com/ncruces/go-sqlite3/driver, provides a database/sql compatible driver. That is the layer most applications use, and the usage example is the shortest thing in the repository:

go
import "database/sql"
import _ "github.com/ncruces/go-sqlite3/driver"

var version string
db, _ := sql.Open("sqlite3", "file:demo.db")
db.QueryRow(`SELECT sqlite_version()`).Scan(&version)

The vfs package, github.com/ncruces/go-sqlite3/vfs, wraps the C SQLite VFS API and provides a pure Go implementation. This is the layer that replaces the operating system layer of SQLite, and it is where the interesting engineering lives.

The gormlite package, github.com/ncruces/go-sqlite3/gormlite, provides a GORM driver, for teams whose data access goes through GORM rather than raw SQL.

The root level files in the repository follow the same decomposition. There is blob.go for the incremental BLOB I/O, conn.go with a conn_127.go variant, stmt.go for statements, txn.go for transactions, func.go for custom functions, vtab.go for virtual tables, json.go with a json_v2.go variant, wrap.go and pointer.go for the type conversion layer, and quote.go, value.go, time.go, config.go, and error.go for the supporting pieces.

The 127 in conn_127.go is a hint about the internals. It suggests a file that only builds for a particular SQLite language version, which is the kind of thing you need when you are tracking a specific upstream release of a database engine.

The features beyond plain SQL

The advanced features list is long, and each entry links to the SQLite documentation for the feature and to a runnable example in the module, which is a good way to work out what is actually usable.

There is incremental BLOB I/O, which lets you read and write a blob in pieces rather than loading the whole value, and that matters for large media or document storage. There are nested transactions via savepoints, so a transaction can have a rollback point inside it. There are custom functions, where you register a Go function with SQLite and call it from SQL. There are virtual tables, with a module implementation, which is how you expose a Go data source to SQL as if it were a table.

There are custom VFSes, encryption at rest with a dedicated VFS directory, and online backup. The JSON support and the math functions are both listed, and the ext/ directory holds many more extensions, including Unicode support and statistics functions, each in its own subdirectory.

The custom VFS point deserves emphasis, because it is the feature that makes the pure Go approach viable rather than merely interesting. SQLite's VFS layer is the abstraction that lets the database talk to the operating system for files, locking, and shared memory. Replace it and you can put a database in memory, in an encrypted form, or over a network protocol, without SQLite knowing. The encryption at rest support is built exactly that way, with golang.org/x/crypto and the Adiantum implementation brought in as dependencies.

The VFS layer is also where the pure Go claim comes from. The vfs package wraps the C SQLite VFS API and provides a pure Go implementation, so the file system access does not go through a syscall wrapper that only exists for one platform.

The caveat, stated plainly

The Caveats section is short and it is the most important part of the README.

This module replaces the SQLite OS Interface, also known as the VFS, with a pure Go implementation, which has advantages and disadvantages, and the design is documented in the vfs README. Because each database connection executes within a Wasm sandboxed environment, memory usage will be higher than alternatives.

That is the trade, and it is worth understanding the shape of it. A Wasm sandbox per connection means the SQLite engine's own memory, plus the runtime overhead of the sandbox, plus the Go memory that the translation introduces. A process with a hundred database connections pays that overhead a hundred times. For an application with a handful of connections and modest data volumes, the overhead is invisible. For an application that opens a connection per request or holds thousands open, it is not.

The other side of the trade is what you get. No cgo means no C compiler at build time, no cross-compilation toolchain configuration, and a binary that is pure Go. It also means the SQLite engine runs in a sandbox, which is a genuine isolation property in a world where SQLite is often pointed at untrusted files, and it is why the VFS is a first-class extension point here rather than an afterthought.

The concurrency note is on the same lines. The module behaves similarly to SQLite in multi-thread mode: it is goroutine-safe, provided that no single database connection, or object derived from it, is used concurrently by multiple goroutines. The database/sql API is safe to use concurrently according to its own documentation, so the familiar rule applies: do not share a single connection between goroutines.

Testing, and the platform matrix

The testing story is the strongest part of the project, and it has two halves.

The first is the project aims for high test coverage, with a coverage badge and a test coverage report in the wiki, and it also benefits greatly from SQLite's own thorough testing. That second point is the clever one. The code is a translation of a well tested C program, and the SQLite test suite can be run against the translated version, so a bug in the translation shows up as a failure in a test that already existed.

The platform matrix is wide. Every commit is tested on Linux for amd64, arm64, 386, arm, riscv64, ppc64le, loong64, and s390x. On macOS for amd64 and arm64. On Windows for amd64 and arm64. On BSD for FreeBSD on amd64 and arm64, NetBSD on amd64 and arm64, DragonFly BSD on amd64, and OpenBSD on amd64. And on illumos and Solaris for amd64.

That list is the practical payoff of the no-cgo design. A cgo-free module can be tested on every platform Go supports, because there is no C toolchain to install on each of them, and that breadth is what makes the module usable on the less common ones.

Certain operating system and CPU combinations have some limitations, and the support matrix in the wiki has the complete overview. That caveat is worth checking against your own target, since the matrix is a wiki page and therefore changes more often than the release notes.

The VFS layer has its own testing story. The Go VFS is tested by running SQLite's mptest, which is the test harness SQLite uses for its own multi-platform testing, and running an upstream stress harness against a reimplementation is a much stronger check than writing your own tests would be.

Performance, and how the claims are checked

The performance section is short but it describes how the claims are substantiated, which matters more than the claims themselves.

Performance of the database/sql driver is competitive with alternatives, with a link to a third party benchmark repository. And the Wasm and VFS layers are benchmarked by running SQLite's own speedtest1, which is the reference benchmark from the SQLite test suite.

So the performance argument is not a marketing number, it is a comparison against other SQLite drivers using a shared benchmark, plus the upstream benchmark run against the Wasm and VFS layers specifically. The second part is the interesting one, because it isolates where the overhead is. A driver that loses ground can be traced to the translation layer or to the VFS replacement, rather than to the fact that it is doing the same work as the C implementation.

This is also where the memory caveat fits. The Wasm sandbox costs memory, and the speedtest1 comparison is what shows whether that cost shows up in throughput.

The alternative approach for anyone weighing this is the obvious one, which is to use a cgo-based driver such as the standard mattn one. It is the right choice when you are on a platform with a working C toolchain and a single process that opens few connections. The case for this module is the deployment story, the platform breadth, and the VFS extension point, and it should be made on those grounds rather than on speed.

Editorial conclusion

Adopt go-sqlite3 if you need to embed SQLite in a Go program and cannot or do not want to depend on cgo, such as a cross-compiled binary or a WebAssembly target. Do not adopt it if memory usage is your binding constraint, since each connection runs in a Wasm sandbox. Verify first that your target platform and CPU combination appears in the support matrix, because some combinations have limitations.

Frequently asked questions

What does go-sqlite3 do about cgo?

It avoids it. The module wraps a Wasm build of SQLite and uses wasm2go to translate it to Go, so Go and x/sys are the only required dependencies and the result compiles with the ordinary Go toolchain. The go.mod pulls the Wasm build in as go-sqlite3-wasm/v6.

What is the main drawback of go-sqlite3?

Memory usage. The module replaces the SQLite OS Interface, the VFS, with a pure Go implementation, and because each database connection executes within a Wasm sandboxed environment, memory usage will be higher than alternatives. The advantages and disadvantages of that design are documented in the vfs README.

Which packages does the go-sqlite3 module provide?

The root package wraps the C SQLite API, the driver package provides a database/sql compatible driver, the vfs package wraps the C SQLite VFS API and provides a pure Go implementation, and the gormlite package provides a GORM driver.

How is the go-sqlite3 VFS implementation tested?

By running SQLite's own mptest, the test harness SQLite uses for its own multi-platform testing. The project also aims for high test coverage of its own and benefits from SQLite's thorough testing, and the Wasm and VFS layers are benchmarked with SQLite's speedtest1.

Is go-sqlite3 safe to use from multiple goroutines?

The module behaves similarly to SQLite in multi-thread mode: it is goroutine-safe provided that no single database connection, or object derived from it, is used concurrently by multiple goroutines. The database/sql API is safe to use concurrently according to its own documentation.

Official sources

  1. License: MIT
  2. ncruces/go-sqlite3 on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/ncruces-go-sqlite3.svg)](https://hysenlabs.com/projects/ncruces-go-sqlite3)

Was this useful?