simpleconf: A Lightweight Configuration Service in Go (HTTP/TCP)
simpleconf is a lightweight configuration service in Go. It serves JSON key-path read/write over HTTP and a minimal TCP protocol. Optional Raft clustering provides high availability.
I started simpleconf in 2017 while building real projects that needed a shared, remotely-accessible configuration store — something smaller than etcd or Consul, without a database server or consensus infrastructure to operate. The result is one Go binary, one YAML config file, and an append-only log.
Quick start
Requirements: Go 1.25+
git clone https://github.com/shaunlee/simpleconf
cd simpleconf
go run ./cmd/bin/main.go
By default the HTTP service listens on :23456. The service reads configs/config.yml:
db:
dir: data
listen: :23456
raft:
enabled: false
HTTP usage
The REST API is small — five operations on JSON key paths:
# set values (raw JSON body)
curl -s -X PUT http://127.0.0.1:23456/db/product.name -d '"Demo"'
curl -s -X PUT http://127.0.0.1:23456/db/product.year -d '2026'
# read the whole document, or one key path
curl -s http://127.0.0.1:23456/db/product
curl -s http://127.0.0.1:23456/db/product.name
# delete a key path, clone one key path to another
curl -s -X DELETE http://127.0.0.1:23456/db/product.year
curl -s -X POST http://127.0.0.1:23456/clone/product.name/product.alias
Key paths are dot-separated (product.name, product.year), so the configuration document behaves like a tree you can read and write at any depth — no schema, no reload, no restart.
TCP protocol
The part of simpleconf I have not seen in other configuration services: a raw TCP protocol for the same operations. It targets small clients, scripts, and embedded environments where pulling in an HTTP client is overkill.
The protocol is one command per line:
| Command | Meaning |
|---|---|
= |
get the whole JSON document |
=key.path |
get one key-path value |
+key.path + next line |
set value (next line is raw JSON) |
-key.path |
delete a key path |
<from + next line >to |
clone a value |
* |
vacuum (rewrite the append-only log) |
PING |
answers +PONG |
+product.name
"Demo"
=product.name
Raft cluster mode
For high availability, simpleconf can run as a Raft cluster with no single point of failure:
- Set
raft.enabled: trueon every node. - Give each node its own
raft.node_id,raft.listen,http_addr,listenanddb.dir. - Use the same
raft.peerslist (formatid,raft_addr,http_addr) on all nodes. - Set
raft.bootstrap: trueonly on the first node during initial cluster bring-up.
Writes to followers are auto-forwarded to the leader by default (raft.forward: true). With forwarding off, a follower answers 409 {"error":"not leader","leader":"http://..."} over HTTP, or -ERR not leader http://... over TCP.
Persistence: application data lives in db.dir/data.aof (append-only), Raft state in db.dir/raft. There is no BoltDB dependency.
Design notes
- Append-only persistence — every change appends to
data.aof; the file is the source of truth and is replayed on start. - Vacuum —
POST /vacuum(HTTP) or*(TCP) rewrites the append-only log, useful after heavy delete/clone activity. - No external storage — no database server, no etcd, no BoltDB: one binary plus one data directory.
- JSON key paths — values are raw JSON; keys are dot paths into the document, so you can set
product.namewithout touchingproduct.year.
Performance
Historical benchmark results from the README, measured on an AMD Ryzen 9 5900HX:
| Operation | ops/sec | ns/op |
|---|---|---|
| Get | 35,865,764 | 31.97 |
| Set | 7,825,952 | 153.10 |
| Del | 12,272,269 | 96.00 |
| Clone | 5,811,598 | 205.70 |
Under load, a 10-second TCP test on :23466 with 500 connections sustained ~492,844 GET requests/sec, ~287,454 SET req/s and ~339,307 DELETE req/s. Over HTTP, wrk with 2 threads and 10 connections measured ~207,040 GET req/s, ~143,629 SET req/s and ~172,110 DELETE req/s.
These numbers are historical — measure on your own hardware — but the shape is the point: a config read is a map lookup, and even the TCP path stays in the hundreds of thousands of requests per second.
simpleconf vs etcd vs Consul
Numbers only mean something in context, so here is simpleconf next to the published figures for etcd and Consul:
| Store | Deployment | Writes | Reads |
|---|---|---|---|
| simpleconf (HTTP) | 1 node, Ryzen 9 5900HX | ~144K req/s (SET) | ~207K req/s (GET) |
| simpleconf (TCP) | 1 node, same hardware | ~287K req/s (SET) | ~493K req/s (GET) |
| etcd v3.x | 3 nodes, 8 vCPU + SSD each | ~50K QPS (PUT) | ~142K QPS linearizable · ~186K QPS serializable |
| Consul | 3 nodes, 16 vCPU each (2017) | ~16.5K req/s max ingestion | — |
Sources: simpleconf's historical README numbers; etcd's official performance guide (three-member cluster on 8-vCPU machines); Consul from the CoreOS dbtester series (etcd v3.1.0 vs Consul v0.7.4, one million keys, three 16-vCPU machines).
Read the table with three caveats:
- Different hardware, tools and years. simpleconf ran on a laptop-class CPU; etcd on three 8-vCPU cloud VMs; the Consul figures are from 2017 on 16-vCPU machines. This is not a controlled head-to-head.
- Different durability and consistency. etcd and Consul acknowledge a write only after syncing to disk on a quorum of nodes — replicated, durable and linearizable. simpleconf's single-node numbers carry no such guarantee, and the README does not document the persistence semantics of its load tests. In Raft mode, simpleconf writes pay the same replication cost.
- Different scale of problem. etcd and Consul provide consensus, service discovery, watches and coordination. simpleconf is a configuration store. Consul's own guidance is to watch the K/V store once updates pass about a hundred per second — all three systems are far past what configuration workloads need.
Why not etcd or Consul?
etcd and Consul are excellent, but they are distributed systems you operate: a multi-node cluster, fast disks (every committed write is synced on a quorum), and Consul's production sizing starts at 8-16 cores and 32-64 GB of RAM. As a minimal alternative to etcd or Consul KV, simpleconf trades those features for a single binary, a single YAML file, and an append-only log. When you do need high availability, the embedded Raft mode gives you a small cluster without adding etcd's footprint.
Related projects
simpleconf is one of my open-source projects. See all of them on the Projects page.
Project links
- GitHub: shaunlee/simpleconf
- License: MIT
If you need a tiny configuration store over HTTP or TCP, give it a try. Issues and pull requests are welcome.