hashicorp/raft: consensus machinery without a database attached
Golang implementation of the Raft consensus protocol
At a glance
- What is it?
- A Go library that replicates a log and hands committed entries to a finite state machine you write. Storage, transport and application logic stay with you.
- Who is it for?
- hashicorp/raft fits when you are writing your own state machine in Go and want election, replication and snapshotting handed to you instead of reinvented, and it does not fit when you wanted a database or a service someone else operates.
- Can I use it commercially?
- Yes, with conditions. MPL-2.0 is a weak copyleft licence: you can use it inside commercial and closed-source software, but if you distribute changes to its own files, you must publish those changes under the same licence.
- Is it still maintained?
- Yes. The repository last received commits 5 days 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 23, 2026, and from our analysis. They are not legal advice.
Star history
- hashicorp/raft9,151 ★ · +11
| Repository | 2026-10-07 | 2026-10-23 |
|---|---|---|
| hashicorp/raft | 9.1k | 9.2k |
11 Stars in the last 16 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
A consensus library, not a database or a service
hashicorp/raft describes itself narrowly: a Go library that manages a replicated log and can be used with an FSM to manage replicated state machines. That framing matters, because people often arrive looking for a datastore and leave confused. There is no server here. No HTTP API, no bundled storage engine, no configuration file format. What you get is the part of a consensus system that is genuinely hard to get right: leader election, log replication, commitment tracking, snapshotting and dynamic changes to the peer set.
Everything above that line belongs to you. The FSM is an interface you implement, so the state machine can be a key-value store, a SQL-backed store or something with side effects, and the library never needs to know which. Log entries coming from clients are described in the README as an opaque binary blob to Raft, which is the cleanest statement of the contract available: the library orders and commits bytes, and does not interpret them.
The dependency list is short enough to read in one pass, and the module declares a current toolchain:
module github.com/hashicorp/raft
go 1.25.0Four direct requirements: go-hclog for logging, go-metrics for instrumentation, go-msgpack for encoding and testify for the tests.
Follower, candidate, leader and the arithmetic that sizes a cluster
Every node sits in one of three states. Followers accept entries from a leader and cast votes. When a follower hears nothing for a while it promotes itself to candidate and asks its peers for votes. A candidate that collects a quorum becomes leader, and the leader is then the only node accepting new entries. That last point has a consequence the README calls out directly: if stale reads are unacceptable in your system, every query has to go to the leader as well, which makes the leader a throughput ceiling you need to plan for rather than discover in production.
The arithmetic follows from the same design. Three nodes tolerate one failure, five tolerate two, and the README recommends running either three or five as the way to keep availability up without giving up much performance. What it also shows, with a two-node example, is the trap at even numbers. With peers A and B the quorum is two, so if either one fails there is no quorum, and the cluster can neither commit entries nor change its own membership. Recovery there is manual: remove the dead node by hand and restart the survivor in bootstrap mode.
That manual step is the honest answer to the most common question about raft libraries, and it is worth deciding in advance which nodes you are willing to restart by hand.
Build check and the Makefile targets you will actually use
The README asks for one thing before you build, a Go toolchain, checked with a single command:
go versionThat is the whole build prerequisite, and it is worth noticing the mismatch with go.mod, which declares `go 1.25.0`. The floor stated in prose is much lower than the declared one, so treat the module file as the real requirement and the README as out of date on this point.
The Makefile is more useful than the README for day-to-day work. It runs the unit suite twice, once normally and once under the `batchtest` build tag, with the race detector on and a three minute timeout per run:
GOTRACEBACK=all go test $(TESTARGS) -timeout=180s -race .A separate `integ` target depends on `test` and sets INTEG_TESTS=yes, while `fuzz` drops into the `./fuzzy` directory with a twenty minute timeout, matching the `fuzzy/` directory in the tree. Linting is gofmt plus golangci-lint:
gofmt -s -w .The `dep-linter` target installs a pinned golangci-lint v1.19.0 into your GOPATH bin instead of tracking latest. Pinning a linter is an unfashionable choice, and one more hint that the same people have been running this repository for years. The last push was on 2026-09-21 and the repository is not archived.
Transports and stores are separate files by design
Storage and networking are pluggable at the file level, and the tree makes the seams obvious. Three transport implementations sit in the root: `tcp_transport.go`, `net_transport.go` for arbitrary net.Listener and net.Conn pairs, and `inmem_transport.go` for tests. Stores follow the same pattern, with `inmem_store.go` for tests and `file_snapshot.go` on the snapshot side, alongside in-memory and discard variants.
Production stores live elsewhere, and this is the part that surprises people. The primary backend, MDBStore, sits in a separate repository called raft-mdb because it depends on cgo. A pure Go option exists as raft-boltdb, built on bbolt, and it can serve as either LogStore or StableStore. Adopting this library therefore means choosing a second dependency for durability before you write a line of consensus code, and that choice has an operational consequence: cgo in your build.
Version 1.0 also moved the transport to an address provider abstraction, which matters if you are not listening on a fixed TCP port, since the transport can then resolve peers dynamically rather than holding a fixed address list.
Snapshot compaction puts determinism on your side of the interface
A replicated log grows without bound, and the answer is snapshots plus log compaction. The library snapshots the current state and removes the entries used to reach it, without waiting for you to ask. The README is precise about the condition that makes this safe: restoring from a snapshot must land the FSM in exactly the state a replay of those old entries would have produced. Your state machine has to be deterministic, and it has to be able to rebuild itself from a snapshot you hand it.
That requirement is the most common source of subtle bugs in systems built on this library, and it sits entirely on your side of the interface. A state machine that reads the clock, generates random values or calls an external service during Apply will diverge between a node that restored a snapshot and a node that replayed the log, and the divergence will not show up until it matters.
`log_cache.go` and `snapshot.go` hold the mechanics, and `bench_test.go` at the root is a reminder that write throughput here is bounded by disk I/O and network latency rather than by CPU.
Version 1.0 broke the API on purpose and 1.8 dropped a shim
Version history is unusually well documented for a Go library, and it explains most upgrade decisions. HashiCorp started tagging in September 2017 and asks users to vendor the dependency. v0.1.0 was the original stable line with no breaking API changes, and it was what Consul used before version 0.7.0. v1.0.0 is the one to understand: it moved server identity to a UUID, versioned the Raft protocol itself, and therefore needs special steps when interoperating with nodes running older library versions. The README points at the compatibility discussion in config.go and at a Consul pull request showing what porting involved. That release also added non-voting servers, the transport address provider and more resilient snapshots.
Three recent releases show the current pace. v1.7.2 on 2025-01-09 added retrieval of the current term and a go-metrics compatibility path. v1.7.3 on 2025-03-20 added logging when a snapshot is opened and sent to a follower. v1.8.0 on 2026-09-16 removed the long-standing shim between armon/go-metrics and hashicorp/go-metrics, so build tags are no longer needed, and began persisting the commit index in the LogStore to speed recovery.
The license is MPL 2.0, which is file-level copyleft: modified versions of the library's own files carry the obligation, while files that merely link against it do not. Check that against your own distribution model.
Where raft stops and your system has to take over
Four limits are worth pricing in before you commit.
First, membership changes need a quorum, and there is no automatic path out of the two-node case described earlier. Bootstrap mode is a manual restart, so run three or five nodes.
Second, non-stale reads must reach the leader. On a read-heavy workload that is a scaling decision, not only a correctness one.
Third, the README documents no client retry or backoff policy. Requests are issued against the library API rather than a managed client, so what a caller does when leadership moves mid-request is decided in your code, in files such as `api.go`, `future.go` and `commands.go`.
Fourth, the README is a protocol summary rather than an operations guide. It does not cover snapshot interval tuning, snapshot sizing or what to do when log replay is slow at startup, and it points to Godoc for the API instead. For those answers, expect to read the source, and start with `raft.go`, `replication.go` and `configuration.go`.
Editorial conclusion
hashicorp/raft fits when you are writing your own state machine in Go and want election, replication and snapshotting handed to you instead of reinvented, and it does not fit when you wanted a database or a service someone else operates. Three things to check first: whether your FSM is deterministic enough to restore from a snapshot, whether you can live with leader-only reads, and which store backend you will use, because the recommended MDBStore means cgo and a second repository. The README settles the protocol and the version history. It does not settle snapshot tuning or slow log replay at startup, so budget time in raft.go and configuration.go. Start on the in-memory transport with the in-memory store to get the state machine right before TCP and cgo enter the picture.
Frequently asked questions
Is Raft better than Paxos?
The README treats them as comparable rather than ranking them. Assuming stable leadership, committing an entry in Raft needs a single round trip to half the cluster, so throughput is bound by disk I/O and network latency. It sends readers to the Raft paper for the actual differences, and the repository contains no Paxos implementation or comparison.
What are Raft and Paxos, and how are they different?
Raft is a consensus protocol organised around three node roles, a replicated log and snapshot compaction, which is the part this Go library implements. The README only claims performance comparability with Paxos and does not document Paxos internals, so any detailed account of the two algorithms has to come from the original papers rather than from this repository.
How do I add or remove a node in a hashicorp/raft cluster?
The peer set can be updated dynamically as long as a quorum of nodes is available. With two peers the quorum is also two, so losing either node blocks commits and membership changes until you remove the dead node manually and restart the survivor in bootstrap mode, which is why the README suggests three or five servers.
Official sources
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.
[](https://hysenlabs.com/projects/hashicorp-raft)Star history chart for your README
[](https://hysenlabs.com/projects/hashicorp-raft)Was this useful?