Getting Started
T4 has two modes. Pick the one that fits your use case:
| Standalone server | Go library | |
|---|---|---|
| Use case | Drop-in etcd replacement, Kubernetes metadata store, any language | Go apps that want zero-overhead local reads, no sidecar |
| Client | Any etcd v3 client (etcdctl, Go, Python, Java, …) | Direct Go API calls |
| Install | Binary or Docker image | go get |
Standalone server
Section titled “Standalone server”Install
Section titled “Install”go install github.com/t4db/t4/cmd/t4@latestOr use Docker:
docker pull ghcr.io/t4db/t4:latestSingle node — local only
Section titled “Single node — local only”Good for development or when durability comes from infrastructure (PVC, RAID):
t4 run --data-dir /var/lib/t4 --listen 0.0.0.0:3379Single node — S3 durable
Section titled “Single node — S3 durable”WAL segments and checkpoints are uploaded to S3. The node recovers automatically if it loses its disk:
t4 run \ --data-dir /var/lib/t4 \ --listen 0.0.0.0:3379 \ --s3-bucket my-bucket \ --s3-prefix t4/AWS credentials are resolved from T4_S3_ACCESS_KEY_ID, T4_S3_SECRET_ACCESS_KEY environment variables.
Use any etcd v3 client
Section titled “Use any etcd v3 client”etcdctl --endpoints=localhost:3379 put /config/timeout 30setcdctl --endpoints=localhost:3379 get /config/timeoutetcdctl --endpoints=localhost:3379 watch /config/ --prefixThe etcd Go client, Python etcd3, Java client, and etcdctl all work unchanged.
Inspect local data offline
Section titled “Inspect local data offline”Use t4 inspect when you want to explore a local data directory without starting the server:
t4 inspect meta --data-dir /var/lib/t4t4 inspect list --data-dir /var/lib/t4 --prefix /config/t4 inspect get --data-dir /var/lib/t4 /config/timeoutt4 inspect history --data-dir /var/lib/t4 /config/timeoutt4 inspect diff --data-dir /var/lib/t4 --from-rev 10 --to-rev 20 --prefix /config/Multi-node cluster
Section titled “Multi-node cluster”All nodes run the same command — no membership config, no initial cluster flag. The first node to write the S3 lock becomes leader; the rest become followers.
# Node At4 run \ --data-dir /var/lib/t4 \ --listen 0.0.0.0:3379 \ --s3-bucket my-bucket \ --s3-prefix t4/ \ --node-id node-a \ --peer-listen 0.0.0.0:3380 \ --advertise-peer node-a.internal:3380
# Node B (same bucket, same prefix)t4 run \ --data-dir /var/lib/t4 \ --listen 0.0.0.0:3379 \ --s3-bucket my-bucket \ --s3-prefix t4/ \ --node-id node-b \ --peer-listen 0.0.0.0:3380 \ --advertise-peer node-b.internal:3380New nodes can join at any time — they restore the latest S3 checkpoint and start replicating. No configuration changes needed on existing nodes.
MinIO / S3-compatible stores
Section titled “MinIO / S3-compatible stores”t4 run \ --data-dir /var/lib/t4 \ --listen 0.0.0.0:3379 \ --s3-bucket my-bucket \ --s3-prefix t4/ \ --s3-endpoint http://minio:9000Kubernetes
Section titled “Kubernetes”See the Kubernetes deployment guide for the Helm chart, which handles multi-node clustering, PVCs, TLS, IRSA, and Prometheus out of the box.
Go library
Section titled “Go library”Use this when you want the store running inside your binary — no network hop for reads, no process to manage.
Install
Section titled “Install”go get github.com/t4db/t4Requires Go 1.22+.
Local only
Section titled “Local only”import "github.com/t4db/t4"
node, err := t4.Open(t4.Config{ DataDir: "/var/lib/myapp/t4",})if err != nil { log.Fatal(err)}defer node.Close()
// Writerev, err := node.Put(ctx, "/config/timeout", []byte("30s"), 0)
// Read (no network, served from local Pebble)kv, err := node.Get("/config/timeout")fmt.Println(string(kv.Value)) // 30s
// List all keys under a prefixkvs, err := node.List("/config/")
// Watch for changesevents, _ := node.Watch(ctx, "/config/", 0)for e := range events { fmt.Printf("%s %s=%s\n", e.Type, e.KV.Key, e.KV.Value)}With S3 durability
Section titled “With S3 durability”import ( awsconfig "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/t4db/t4/pkg/object")
awsCfg, _ := awsconfig.LoadDefaultConfig(ctx)store := object.NewS3Store(s3.NewFromConfig(awsCfg), "my-bucket", "t4/")
node, err := t4.Open(t4.Config{ DataDir: "/var/lib/myapp/t4", ObjectStore: store,})With MinIO
Section titled “With MinIO”import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/credentials" "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/t4db/t4/pkg/object")
cfg, _ := config.LoadDefaultConfig(ctx, config.WithRegion("us-east-1"), config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider("user", "pass", "")), config.WithEndpointResolverWithOptions( aws.EndpointResolverWithOptionsFunc(func(service, region string, opts ...any) (aws.Endpoint, error) { return aws.Endpoint{URL: "http://minio:9000", HostnameImmutable: true}, nil }), ),)store := object.NewS3Store(s3.NewFromConfig(cfg), "my-bucket", "t4/")Next steps
Section titled “Next steps”- Operations — Multi-node clusters, TLS, auth, observability, branching
- Configuration — All CLI flags and
t4.Configfields - Kubernetes — Helm chart for production deployments
- API Reference — Full Go embedded library API
- Recipes — Distributed locks, service discovery, config hot-reload
- Migrating from etcd — Compatibility guide and migration steps