go-carbon keeps the classic Carbon pipeline but swaps the language
Golang implementation of Graphite/Carbon server with classic architecture: Agent -> Cache -> Persister
At a glance
- What is it?
- go-carbon is a Go implementation of the Graphite Carbon server, keeping the agent, cache and persister architecture and the whisper storage files, with Carbonlink, a gRPC variant, hot configuration reloads and a metric namespace ordering trick for its trie index. It suits teams that inherited a Graphite deployment and want the same protocol with a single binary.
- Who is it for?
- Adopt go-carbon when you already run Graphite and want the same wire protocols and whisper files without the Python process, since it accepts plaintext, pickle, HTTP and Kafka input, speaks Carbonlink and its gRPC equivalent, and ships as a single binary with packages, container images and a source build.
- 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 3 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 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Agent, cache, persister, in that order
The architecture is the classic one, and the README is explicit about keeping it. go-carbon is a Golang implementation of the Graphite and Carbon server with the classic architecture of agent, then cache, then persister. The agent receives points, the cache holds the recent ones in memory, and the persister writes them out to disk as whisper files. Nothing about that pipeline is reinvented, which is the point: a team that already has a Graphite deployment can replace the Carbon process and keep the storage format, the configuration files and the way graphite-web talks to it. The repository layout mirrors the pipeline, with `receiver/`, `cache/`, `persister/`, `points/`, `carbon/`, `carbonserver/`, `api/`, `buckyd/`, `tags/` and `tool/` directories, and the entry point `go-carbon.go` with a `supervisor.go` that starts the pieces.
Four ways in: plaintext, pickle, HTTP and Kafka
The input side is broader than the original server, and the four protocols are listed in the feature list. Metrics can be received over TCP and UDP with the plaintext protocol, over TCP with the Pickle protocol, over HTTP, and from Apache Kafka. The Go module shows how the Kafka path is implemented, with `github.com/IBM/sarama` and the Google Cloud Pub/Sub client both in the dependency list. Pickle support has its own library, `github.com/lomik/graphite-pickle`, and the module also pulls in `github.com/go-graphite/protocol`, which is the shared protocol package. The receiver directories in the tree line up with those choices, and the HTTP receiver brings `github.com/NYTimes/gziphandler` for compressed request bodies. For an existing deployment, the practical question is only which of these your agents already speak, and the answer is usually the first two.
Carbonlink, plus a gRPC shape for the same job
The piece that ties go-carbon to graphite-web is Carbonlink, the mechanism by which the web frontend asks the cache for data it has not yet persisted. The README lists Carbonlink as a supported feature, requests to cache from graphite-web, and also lists a Carbonlink-like gRPC api as a separate item, with `google.golang.org/grpc` in the dependency list. So there are two ways for a query frontend to reach the in-memory cache. The gRPC variant matters for anyone who wants the cache reachable from something other than a Python process, since it removes the need for the Carbonlink protocol handler on the other side. The repository has an `api/` directory and a `carbonserver/` directory, which is where those two paths live.
HUP reloads and the USR2 dump
Two signal behaviours are worth knowing before you plan an upgrade. A dump and restore restart can be done on the `USR2` signal, configured through the `dump` section of the config: the persister is stopped, new data is written to a file, the cache is dumped to a file, everything is stopped, and the state is restored from those files after the next start. Separately, a `HUP` signal reloads some configuration options without a restart. The reloadable set is specific: the `whisper` section of the main config, `storage-schemas.conf` and `storage-aggregation.conf`, plus `graph-prefix`, `metric-interval`, `metric-endpoint` and `max-cpu` from the `common` section, and the `dump` section. Everything else in the file still needs a restart, which is worth checking against the specific keys your deployment changes at runtime.
Metric namespace order decides how fast queries are
There is a piece of operational advice in the README that has nothing to do with the code. Putting the most common namespaces at the beginning of a metric name can be beneficial for scaling. The README says this does not affect the performance of the trigram index, but when you switch to the trie index for serving a higher number of metrics in a single go-carbon instance, your query would be more efficient, and the naming pattern would also lead to a better metric namespace hierarchy. The example is concrete. Querying `sys.cpu.loadavg.app.host-0001` would be faster than querying `sys.app.host-0001.cpu.loadavg` with the trie index, especially with tens of thousands of hosts, because all of them share the prefix `sys.cpu.loadavg.app.host-` and that prefix is compared only once during the query. The blog post the README links expands this into a trie and DFA approach for globbing ten million metrics.
Installing it as a package, a container or a binary
There are three documented routes, and the package route is the one most deployments will take. The project publishes deb and rpm packages through packagecloud for i386, amd64 and arm64, with a stable repository and an autobuilds repository for master that might be unstable, and installation guides on packagecloud. Container images are uploaded to ghcr.io. To build the binary yourself you need Go 1.8 or later, and the commands are:
git clone https://github.com/go-graphite/go-carbon.git
cd go-carbon
makeThe container image is a two stage build. The first stage is `golang:alpine` with the toolchain packages installed, and it runs `make go-carbon` plus a test run, where the race detector is only used on amd64. The runtime stage is `alpine:latest` with a `carbon` user, the config baked in at `/etc/go-carbon/go-carbon.conf` alongside the storage schema files, and a default config generated at build time by running the binary with `-config-print-default`. It exposes ports 2003 over TCP and UDP and 8080, and declares volumes for `/var/lib/graphite` and `/etc/go-carbon`.
The command line is small, the config file is not
The flags are minimal, and the README prints them in full:
$ go-carbon --help
Usage of go-carbon:
-check-config=false: Check config and exit
-config="": Filename of config
-config-print-default=false: Print default config
-daemon=false: Run in background
-pidfile="": Pidfile path (only for daemon)
-version=false: Print versionThe configuration file is where the real decisions live. The `common` section covers the user to run as in daemon mode, the `graph-prefix` for internal go-carbon graphs with a `{host}` macro, the `metric-endpoint` for where internal metrics go, the `metric-interval` at `1m0s` by default and `max-cpu` for a multi worker setup. The `whisper` section points at the data directory, the schemas file, the optional aggregation file, the worker count, the `max-updates-per-second` limit on whisper update calls, `sparse-create`, `flock` for consistency under concurrent reads and writes, and `hash-filenames` for tagged metrics. The README flags the quotas file as a go-carbon only feature rather than a standard Graphite one, and the same is true of several other keys in that section.
Throughput claims and the experimental storage path
The README makes performance claims and it is fair to read them as the project's own numbers rather than as verified benchmarks. It says go-carbon is faster than default carbon, in all conditions, with the margin depending on hardware and storage schemas, and shows a success story of replacing carbon on a server with a load up to 900 thousand metrics per minute. It also reports an effort to find the maximum possible performance on hardware with two E5-2620v3 processors, 128GB of RAM and local SSDs, where stable performance was around 950k points per second with a short term peak of 1.2M points per second. On the storage side, the feature list marks shared compressed storage with an embedded buckyd as experimental, and the repository has a `buckyd/` directory and a `doc/shared-storage.md`. The licence is MIT, the releases are v0.19.0 and v0.19.1 on 2026-02-07 after v0.18.0 in October 2024, and the last push was on 2026-10-20.
Editorial conclusion
Adopt go-carbon when you already run Graphite and want the same wire protocols and whisper files without the Python process, since it accepts plaintext, pickle, HTTP and Kafka input, speaks Carbonlink and its gRPC equivalent, and ships as a single binary with packages, container images and a source build. Do not adopt it if you are designing a new metrics stack, because the storage layer and configuration model are inherited wholesale from Carbon, and the experimental shared compressed storage with embedded buckyd is not what you would design today. Verify first that your existing graphite-web deployment talks to it over Carbonlink, and check the `whisper` section of your configuration against the shipped example, since several keys there are go-carbon extensions rather than standard Carbon ones.
Frequently asked questions
What does go-carbon replace in a Graphite deployment?
It replaces the Carbon server process while keeping the classic agent, cache and persister architecture, the whisper storage files, the storage-schemas and storage-aggregation configuration files, and Carbonlink for talking to graphite-web. The README also lists a Carbonlink-like gRPC api as an alternative to the Carbonlink protocol.
How do I build go-carbon from source?
Clone the repository, change into the directory and run `make`, which needs Go 1.8 or later. Binary packages are also published as deb and rpm for i386, amd64 and arm64 on packagecloud, and container images are uploaded to ghcr.io.
Which configuration can be reloaded without restarting go-carbon?
A HUP signal reloads the `whisper` section of the main config, `storage-schemas.conf` and `storage-aggregation.conf`, the `graph-prefix`, `metric-interval`, `metric-endpoint` and `max-cpu` keys from the `common` section, and the `dump` section. A USR2 signal triggers the optional dump and restore restart described in the `dump` section.
Why does metric namespace order matter in go-carbon?
The README explains that putting common namespaces first does not affect the trigram index, but with the trie index a query such as `sys.cpu.loadavg.app.host-0001` is faster than `sys.app.host-0001.cpu.loadavg`, because hosts sharing the prefix are compared only once during the query, which improves memory usage and query performance.
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/go-graphite-go-carbon)Was this useful?