TerarkDB: a RocksDB fork you can migrate into and not back out of
A RocksDB compatible KV storage engine with better performance
At a glance
- What is it?
- TerarkDB is a storage engine from ByteDance that keeps the RocksDB API verbatim, so existing code compiles unchanged, and adds a table format you enable per LSM level to cut tail latency. The readme states its limitations in four lines that deserve to be read before anything else, including one that says data can be migrated from RocksDB and not migrated back, and a fork point from 2019 that is a long way behind the version it is benchmarked against.
- Who is it for?
- TerarkDB earns an evaluation if you have a large value workload on a fast solid state drive and a latency problem that a log-structured merge tree handles badly, because that is the regime the new table format targets and the benchmark was chosen to match it. Do not adopt it if you need a reversible migration, cross-platform support, or a binding other than C, because the readme rules all three out in as many words.
- 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 last received commits 79 days ago.
- What is it written in?
- Mainly C++, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 28, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Four notes, and the second one is a ratchet
The readme puts four notes near the top, before any feature or benchmark, and three of them change the evaluation. The first is that the engine was only tested and described as production ready under Linux, which rules out the two platforms most developers have and is not negotiable if your production estate is not Linux. The second is that language bindings other than C and C++ are not fully tested, and the repository does contain a directory for a Java binding, so the binding exists and is unproven. The third is the one that should stop an evaluation: existing data can be migrated from RocksDB directly into TerarkDB, but cannot be migrated back. That is a one-way ratchet, and it interacts with the fourth note in a way that compounds. The engine was forked from a specific RocksDB release, one from 2019, and a database written by the fork carries whatever on-disk structures the fork produces. So your rollback plan after a bad deployment is not a configuration change, it is a restore from a backup taken before the migration, and that is a different operational conversation from rolling back a release. The readme says the migration is possible in most cases without drawbacks, and that claim is about moving forward. The claim about moving back is a single clause long, and it is the clause that matters when you are deciding whether to try this in production.
The API is the upstream API, which is the compatibility evidence
The strongest thing in this readme is not a claim but a set of code samples, and the detail to notice is that they are written against the upstream header and the upstream namespace. The examples include the upstream database header, declare the upstream options type, and call the upstream open, put, get and delete entry points. Nothing is renamed, nothing is wrapped, and there is no adapter layer visible in the samples. That is what a drop-in replacement looks like in practice, and it is a stronger argument than the prose claim that existing instances migrate without drawbacks, because it means the source compatibility half of that claim is demonstrable by compiling the readme. Two further details in the samples are worth your time. Every example sets the same two synchronous-write options to the same value before opening the database, which is a strong hint about which part of the write path this project has been working on, and it is consistent with the tail latency claim in the introduction.
options.create_if_missing = true;
options.wal_bytes_per_sync = 32768;
options.bytes_per_sync = 32768;
auto status = rocksdb::DB::Open(options, "/tmp/testdb", &db);And the second example goes past the basics, constructing the table options directly, allocating a block cache with an explicit size, setting a block size, and installing the table factory by hand, which tells you the configuration surface is the upstream one and that you are expected to tune it. The example directory settles the scope question as well. Alongside the simple example there is one for transactions, one for optimistic transactions, one for column families, one for compaction filters and one for manual compaction control, and there are option-file examples for both the upstream format and a Terark-specific one. So the surface is the whole storage engine, not a read and write subset.
TerarkZipTable: pick the LSM level, supply the fallback, provide scratch space
This is the technical heart of the project and the readme explains its mechanism better than most documentation manages. The new table format is not a global replacement for the standard one. You construct a factory for it and pass it a fallback factory, and you tell it the LSM level at which to start using itself. The example sets that level to two, so the first two levels of the tree keep the standard table and everything below uses the new one, which means the design is explicitly aimed at the large, cold, rarely-read portion of a tree rather than at the hot top. That is a coherent engineering bet and it also means you have to think about it: choosing the level is choosing where the trade-off applies, and choosing it too low means paying the new format's cost on data that is rewritten often. Four other options are visible in the sample. One is a nesting level for the index. One is a sample ratio, so the format samples a fraction of the data while building. One is a temporary directory, which the sample points at a temporary filesystem and the comment on it says a slow device is acceptable, which tells you the scratch space is not on the critical path. And the fallback is not optional, which is the part that catches people out: there is no single-factory configuration, you always pass two. The build side has its own requirement, because enabling the format needs the asynchronous I/O development package installed, and the readme's advice for a first-time user is blunt about it: turn the option off and try the engine without this feature first. That is good advice and it also tells you the feature is the risky part.
A static build that bundles four compression libraries and tells you to strip them
The static library path is where a careful integration meeting becomes necessary, and the readme is refreshingly blunt about its own gaps. The build produces a directory containing the include tree and a library directory with the engine's own archive alongside a compression library's archive, and then it says, in terms, that all the static libraries have not been archived together yet, so you have to pack them all into your target yourself. The instruction that follows is a ten-item linker command line,
-Wl,-Bstatic \
-lterarkdb -lbz2 -ljemalloc -llz4 -lsnappy -lz -lzstd \
-Wl,-Bdynamic -pthread -lgomp -lrt -ldl -laiohalf of it a switch telling the linker to prefer static resolution for the first six libraries and dynamic for the rest. Six static archives means you are linking this project's copies of four widely used compression libraries, plus its allocator and itself, and the readme handles the consequence in a single sentence: because the engine is built with those compression libraries, a test framework and a header-only collection by default, you can remove them from your higher-level application. Which is exactly right and also exactly the kind of instruction that causes an afternoon of duplicate-symbol errors in a codebase that already uses compression. Two details in the link line are worth flagging now. It includes the asynchronous I/O library unconditionally, whether or not you enabled the feature that needs it, so you carry a dependency you may not use. And the set is hard-coded in documentation rather than produced by the build, so a future dependency change in the project will not update your build and will break it silently at link time. If you take the static path, copy that command line into your own build system as generated output rather than as a copy, or expect to maintain it by hand.
The build is a shell script driven by environment variables
There are two documented ways to consume the engine and they differ in how much they hide. The recommended one is a CMake subdirectory, which means you add the repository as a git submodule, initialise its own submodules recursively, add one line to your top-level build file to include the directory, and link your target against it. That is the path to take if you can, because your build system then knows about the dependency rather than your documentation. The other is the static library path, and it is where the project's build configuration becomes visible. The options are not in a build manifest. They are environment variables read by a shell script, and there are six of them: the build type, and switches for the allocator, the tests, the tools, the new table format, and zoned device support. Each gets a one-line explanation in the readme,
cd terarkdb && git submodule update --init --recursive
WITH_TESTS=OFF WITH_ZNS=OFF ./build.shThe subdirectory method, which the readme recommends, is two lines in your own build file:
add_subdirectory(terarkdb)
target_link_libraries({YOUR_TARGET} terarkdb)including the note that the allocator switch should be turned off if you use a different one, which is the kind of interoperability hint that saves a link failure. The build type is recommended as a specific optimisation level with debug information retained, which is right for a storage engine. Two observations about this arrangement. First, a configuration surface that lives in a shell script is not discoverable by a build tool, so your continuous integration has to know the variable names and the values, and nothing in the repository enforces that they stay in sync. Second, the two switches that are off by default are the interesting ones. The new table format is off, which the readme recommends anyway for a first attempt, and zoned device support is off, which tells you that the zoned namespace work exists and is not the default path. The repository also ships a benchmark script for that zoned path, so the feature is exercised even though it is not enabled.
Benchmarked against a newer RocksDB, with the results only in a chart
The performance section is unusually careful about the experiment and unusually silent about the result. The comparison target is named, and it is a later release of the engine this project forked, which is the right thing to benchmark against and also worth noticing. The hardware is specified in full: a two-socket server with a particular Xeon part at a stated clock, thirty-two cores and sixty-four threads, just under four hundred gigabytes of memory, and a solid state drive described by its NAND type and capacity. The workload is specified in full too: the upstream benchmark tool, ten client threads, twenty gigabytes of requests per thread, twenty-four byte keys, two thousand byte values, and two named mixes, one that is ninety percent writes and one that is ninety percent reads. Then the results are an image. There is no table, no number, and no caption in the text, so you can see the shape of the comparison and you cannot read a figure off it. The same is true of the real-world section, which offers two more images and a claim that the engine has been deployed in many applications inside the company and can reduce latency spikes and improve throughput. Two things to keep in mind. A two thousand byte value is a large-value workload, which is the regime where a compression-oriented table format wins most, so the benchmark is chosen to flatter this design and you should not read it as a general ranking. And the comparison against a newer upstream release is measured while the fork itself sits on a much older base, so any upstream improvement landed since the fork point is absent from the left-hand side unless it was ported across.
The documentation is a corporate wiki, and the user list is one entry
Two facts at the top of the readme tell you most of what you need to know about support. The first is the documentation link, which points at a document on the company's internal wiki domain rather than to anything in the repository or on a documentation site. That has three consequences. The canonical documentation is not versioned alongside the code, so a link that was accurate for one release may not be for another. It may require an account on that platform, which means an evaluator outside the company may not be able to open it at all. And the readme you are reading is a summary written for people who can see the other thing, so the gaps you notice here are the gaps the author did not think needed repeating. The second fact is the community link, which is a channel invitation rather than an issue tracker or a discussion forum, so questions and answers are not indexed by anything searchable and a question you ask is a question you asked in private. The user list, which the readme invites you to join a channel to join, contains exactly one entry: the company itself, in core online services. So there is no independent user, no third party running this in production, and no public corroboration of the real-world claims beyond the two charts. None of that makes the project worse, and the engineering in it is plainly serious. It does mean that if you adopt it you are the first outside adopter, and the people who can help you are the people who wrote it.
A branch at 1.4, a last release in 2021, and continuous integration files from another era
The version story is the last thing to reconcile. The default branch is not the branch you would expect. It is named for a development line, and the newest tagged releases are from the end of 2020 and the start of 2021, at version one point three point five and one point three point six. The last push was in July 2026, so work is continuing, and it is continuing on a branch whose version has moved to one point four without a corresponding release. If you want a tagged artefact you have a five-year-old one, and if you want current code you have a moving branch, and the readme does not tell you which you are supposed to use. The repository contents are consistent with a long-lived fork. The upstream directory layout is intact, including a directory for code the fork has marked as deprecated, which is how you accumulate things the upstream has since removed. There is a Buck integration, with a targets file and a buckifier directory, so the fork is kept buildable with three different build systems, which is a maintenance cost nobody volunteers for. There is a documented dump format file, which is the thing that makes a one-way migration diagnosable and recoverable in an emergency, and its presence is quietly the most reassuring file in the tree. Two continuous integration configuration files are present for services that have been superseded, which dates the project's own tooling. And both upstream licence texts are retained alongside the fork's own permissive licence, which is simply correct for a fork of an Apache-licensed project derived from an earlier permissively licensed one.
Editorial conclusion
TerarkDB earns an evaluation if you have a large value workload on a fast solid state drive and a latency problem that a log-structured merge tree handles badly, because that is the regime the new table format targets and the benchmark was chosen to match it. Do not adopt it if you need a reversible migration, cross-platform support, or a binding other than C, because the readme rules all three out in as many words. Three things to establish before you start. What your rollback plan is, since a TerarkDB data directory is not readable by RocksDB and a bad rollout is not a configuration change you can undo. What the fork has and has not picked up, given it forked from a 2019 release while benchmarking against a much newer one, because that gap is where upstream fixes and formats you may expect are missing. And what the documentation actually says, since the canonical document is a link to a corporate wiki rather than anything in the repository, which means the questions you cannot answer from the readme may need a Slack invitation rather than a search.
Frequently asked questions
Can I migrate a RocksDB database back to RocksDB?
No. The readme states that existing data can be migrated from RocksDB directly into TerarkDB but cannot be migrated back. Any adoption plan needs a restore-from-backup rollback, taken before the migration, rather than a version rollback.
Is TerarkDB API compatible with RocksDB?
The examples include the upstream database header, use the upstream options type, and call the upstream open, put, get and delete entry points with nothing renamed or wrapped. The example directory also covers transactions, column families, compaction filters and manual compaction, and provides option-file examples in both the upstream format and a Terark-specific one.
What is TerarkZipTable and how do I enable it?
A table format you install as a factory that takes a fallback factory and a level number, so the standard table keeps the upper levels of the tree and the new one is used from the level you choose. The sample sets that level to two. It also needs a temporary directory other than the data directory, an index nesting level, a sample ratio, and the asynchronous I/O development package installed at build time.
What are the stated limitations?
Four, in the readme: it was only tested and described as production ready under Linux, bindings other than C and C++ are not fully tested, data cannot be migrated back to RocksDB, and the fork base is RocksDB v5.18.3 while the benchmark compares against a later release.
How is the build configured?
By environment variables read by a shell script rather than by a build manifest: the build type, and switches for the allocator, the tests, the tools, the new table format, and zoned device support. The new table format and zoned support are both off by default, and the readme recommends turning the table format off for a first attempt.
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/bytedance-terarkdb)