Open-source project
nutsdb/nutsdb avatar
nutsdb/nutsdb

NutsDB: an embeddable Go key/value store with transactions and real data structures

A simple, fast, embeddable, persistent key/value store written in pure Go. It supports fully serializable transactions and many data structures such as list, set, sorted set.

3,581 stars344 forksGoApache-2.0

At a glance

What is it?
NutsDB is a pure Go embedded key/value store that puts every operation inside a serializable transaction and ships list, set and sorted set types alongside plain keys. This review covers how it stores data, how to install it, and where the design starts to bite.
Who is it for?
Adopt NutsDB when your Go process needs an embedded store with serializable transactions and list, set or sorted set operations, and you accept that v1.0.0 broke on-disk compatibility with earlier versions. Skip it if you need to open an existing v0.x data directory with a current release, or if you want a query language rather than bucket-and-key access.
Can I use it commercially?
Yes. Apache-2.0 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 received new commits within the last day.
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 1, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What NutsDB solves, and the Go programs it fits

NutsDB targets the case where a Go process wants durable storage inside its own address space rather than a database server on the other end of a socket. The README describes it as "a simple, fast, embeddable and persistent key/value store written in pure Go." Pure Go matters here: the module in go.mod depends on Go 1.24.0 and pulls in libraries such as github.com/tidwall/btree, github.com/edsrzf/mmap-go and github.com/gofrs/flock, with no cgo binding to a C storage engine. That keeps cross-compilation straightforward.

The second half of the pitch is the data model. Most embedded stores give you a byte key and a byte value. NutsDB also exposes list, set and sorted set types, so a program that would otherwise layer its own encoding on top of a plain key/value store can call the structure directly. The README states that all operations happen inside a Tx, and that a Tx can be read-only or read-write. Read-only transactions read a value for a given bucket and key, or iterate over key-value pairs; read-write transactions read, update and delete keys.

The intended user is a Go developer building a service, CLI or agent that needs local persistence with more than one shape of data. It is not aimed at teams who want a standalone server with a wire protocol, and the repository does not present it as one.

Transactions, buckets and the on-disk layout

The unit of work is the transaction. A read-only Tx reads a single key in a bucket or iterates a set of key-value pairs, while a read-write Tx can read, update and delete keys, and the README describes the isolation level as fully serializable. Buckets are the namespace above keys, and since v1.0.0 they are not implicit: the README says the current Bucket needs to be created manually and points to docs/user_guides/use-buckets.md.

On disk, data lives in segment files, with a default segment size controlled by DefaultOptions. The repository layout shows the machinery: datafile.go, file_manager.go, merge.go, merge_v2.go, merge_manifest.go, merge_recovery.go, hintfile.go and hint_collector.go sit alongside index.go and recovery_reader.go. Merge V2 is the compaction path described in the README. It runs in four phases: preparation, which enumerates files and validates merge state; a rewrite phase that processes data files and rewrites valid entries into new merge files; a commit phase that updates in-memory indexes and writes hint files; and finalization, which persists data and cleans up old files. The README claims memory use during merge drops from roughly 145 bytes per entry to roughly 50 bytes per entry, and that writes may proceed concurrently during a merge.

Merge V2 uses negative file IDs for merge files, starting from math.MinInt64, while normal data files use positive IDs starting from 0. During index rebuild, merge files are processed before normal files. That ordering rule is the kind of detail worth knowing if you ever inspect a data directory by hand.

Installing NutsDB and opening your first database

NutsDB is a Go module, so installation is a module fetch rather than a binary install. The README requires Go 1.24 or newer and gives this command.

bash
go get -u github.com/nutsdb/nutsdb

Opening a database uses nutsdb.Open with an options value and functional options. The README states that Dir, EntryIdxMode and SegmentSize must be specified by the client. The example below sets the directory and leaves the rest of DefaultOptions in place; it will create /tmp/nutsdb if the path does not exist.

go
package main

import (
    "log"

    "github.com/nutsdb/nutsdb"
)

func main() {
    db, err := nutsdb.Open(
        nutsdb.DefaultOptions,
        nutsdb.WithDir("/tmp/nutsdb"),
    )
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()
}

After Open returns, the README's next step is a bucket: since v1.0.0 buckets must be created manually before use, and docs/user_guides/use-buckets.md is the reference for that call. The examples/ directory in the repository is the practical starting point, with separate programs for k-v, list, set, sortedSet, iterator, batch, bucket, emptybucket, http and watcher. If you want to see a working call sequence before writing your own, examples/k-v is the shortest path. Note the README's warning about segment size: the default in DefaultOptions changed from 8MB to 256MB at v0.9.0, and it says the original data will not be parsed unless the value is changed back manually.

Where NutsDB is the wrong choice

The compatibility statement is the first thing to read before adopting. The README says that after nutsdb v1.0.0, because of changes in the underlying data storage protocol, data from the old version is not compatible, and asks users to rewrite it before using the new version. There is no documented in-place migration path in the README. If you have a v0.x data directory in production, upgrading the library is not a drop-in operation; you export and rewrite.

The segment size change carries the same risk in a quieter form. A database written under the old 8MB default will not be parsed by a process running the new 256MB default unless the setting is put back by hand. That is a configuration detail that can look like corruption.

The second limitation is scope. NutsDB has no query language and no server. Access is bucket plus key, with iteration for ranges, so anything resembling a join, a secondary index or an ad hoc query has to be built in application code. Teams that want SQL, replication or a network protocol should be looking at a different class of system entirely. And because the store is embedded, the process that opens the directory owns it; there is no built-in story in the README for multiple writers on one data directory across machines.

NutsDB against bbolt, BadgerDB and Pebble

The closest comparison is bbolt, the maintained fork of BoltDB. Both are embedded Go key/value stores, but the data models diverge: bbolt presents nested buckets of byte keys and byte values, while NutsDB adds list, set and sorted set types and wraps every operation in a Tx. If your data is genuinely structured, NutsDB saves you an encoding layer; if it is opaque bytes, that advantage disappears.

BadgerDB takes a different storage approach, using a log-structured merge tree with value separation rather than the segment-file design visible in NutsDB's datafile.go and merge_v2.go. LSM stores generally trade read amplification for write throughput, and BadgerDB has its own compaction and garbage collection story. NutsDB's Merge V2 is the counterpart on that axis, and the README's stated goal for it is memory efficiency during compaction rather than peak write throughput.

Pebble is the other name that comes up, a Go LSM key/value store derived from the RocksDB lineage. It is closer to RocksDB in design than NutsDB is, and RocksDB itself is a C++ engine that typically arrives through bindings. Choosing between them is mostly a question of whether you want an LSM with a large tuning surface or a smaller segment-based store whose API already speaks in sets and sorted sets. NutsDB is the smaller of the two by design, and the README does not claim otherwise.

Maintenance, licensing and what an upgrade costs

The repository is not archived, and the last push was on 2026-09-18. Releases are spaced rather than continuous: v2.0.0 on 2026-02-08, v1.1.0 on 2025-12-17, and v1.0.4 on 2024-02-25. The gap between v1.0.4 and v1.1.0 is close to two years, so anyone planning to track the project should plan around infrequent releases rather than a steady stream.

Licensing is Apache-2.0, per the LICENSE file and the badge in the README, which permits use in closed-source products subject to the licence's notice and attribution conditions. That is a permissive licence, but the obligations are real; read the LICENSE text rather than relying on the badge. This is not legal advice.

The upgrade cost is dominated by the storage format. Because the README states that v1.0.0 changed the underlying data storage protocol and that old data is not compatible, a major version bump is not something you absorb by changing a version string in go.mod. Budget for an export and reimport, and check the CHANGELOG.md and the release notes for v2.0.0 before moving. The go.mod file requires Go 1.24.0, which sets a floor on your toolchain as well.

Editorial conclusion

Adopt NutsDB when your Go process needs an embedded store with serializable transactions and list, set or sorted set operations, and you accept that v1.0.0 broke on-disk compatibility with earlier versions. Skip it if you need to open an existing v0.x data directory with a current release, or if you want a query language rather than bucket-and-key access. Before committing, open a database with nutsdb.DefaultOptions and nutsdb.WithDir, create a bucket explicitly, and confirm that the segment size you configure matches the files you already have, because the default changed from 8MB to 256MB at v0.9.0 and the README states old data will not parse under the new value.

Frequently asked questions

How do I install NutsDB?

It is a Go module, so you add it with go get -u github.com/nutsdb/nutsdb after installing Go 1.24 or newer, as the README states. There is no separate server or binary to download.

Does NutsDB support transactions?

Yes. The README says all operations happen inside a Tx, which can be read-only or read-write, and describes the transactions as fully serializable.

What data structures does NutsDB support besides key/value?

The README lists list, set and sorted set alongside plain key/value access, and the repository's examples directory has a separate program for each of those types.

Is NutsDB compatible with data written by older versions?

No. The README states that after nutsdb v1.0.0, because of changes in the underlying data storage protocol, data from the old version is not compatible and must be rewritten before using the new version.

Official sources

  1. License: Apache-2.0
  2. nutsdb/nutsdb 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/nutsdb-nutsdb.svg)](https://hysenlabs.com/projects/nutsdb-nutsdb)