Kubernetes-native distributed ID generator based on the Twitter Snowflake algorithm.
- Configurable epoch (
KUBEID_EPOCH_MS) - gRPC + HTTP APIs
- Kubernetes-native node ID via StatefulSet ordinal or Lease allocation
- Go SDK at
github.com/launchpad001/kubeid/sdk
Requires Go 1.26.0+.
+--------------------------------------------------------------------------+
| 1 bit unused | 41 bit timestamp | 10 bit node | 12 bit sequence |
+--------------------------------------------------------------------------+
| Field | Bits | Range |
|---|---|---|
| Timestamp | 41 | ms since configured epoch |
| Node ID | 10 | 0 – 1023 (configurable) |
| Sequence | 12 | 0 – 4095 per ms per node |
node_bits and step_bits are configurable as long as their sum is ≤ 22 and each is > 0.
All replicas must share the same epoch. Changing the epoch after IDs have been issued breaks ordering and uniqueness assumptions.
Default epoch: 1767196800000 (2026-01-01 00:00:00 UTC+8).
export KUBEID_EPOCH_MS=1609459200000 # 2021-01-01 00:00:00 UTCmake proto tidy build
# static node id for local run
KUBEID_NODE_ID_MODE=static KUBEID_NODE_ID=1 ./bin/kubeid# HTTP
curl -s http://127.0.0.1:8080/v1/id | jq
curl -s 'http://127.0.0.1:8080/v1/ids?count=5' | jq
curl -s http://127.0.0.1:8080/v1/info | jq
# parse — prefer id_str (see note below)
ID=$(curl -s http://127.0.0.1:8080/v1/id | jq -r .id_str)
curl -s "http://127.0.0.1:8080/v1/id/${ID}/parse" | jqJavaScript / TypeScript clients: always use
id_str. The numericidfield exceedsNumber.MAX_SAFE_INTEGERand will be corrupted if parsed as a JSON number.
| Method | Path | Description |
|---|---|---|
| GET | /v1/id |
Generate one ID |
| GET | /v1/ids?count=N |
Generate N IDs (count 1..1000; omit → 1) |
| POST | /v1/ids |
Body {"count":N} (count 1..1000; omit → 1) |
| GET | /v1/id/{id}/parse |
Parse an ID |
| GET | /v1/info |
Generator metadata |
| GET | /healthz |
Liveness |
| GET | /readyz |
Readiness (false when lease ownership is lost) |
Non-positive count is rejected with HTTP 400 on all batch endpoints.
Transient clock-rollback errors return HTTP 503.
See api/proto/kubeid/v1/kubeid.proto.
Service: kubeid.v1.IDGenerator
Generate/GenerateBatch/Parse/Info- gRPC health + reflection enabled
GenerateBatch.countmust be 1..1000 (no silent default)Parseacceptsid=0(valid first ID at epoch on node 0)
Default listen: :9090.
HTTP/gRPC are trusted-network APIs (no auth). Keep them on ClusterIP / mesh-internal networks; do not expose the Service publicly without additional protection.
package main
import (
"context"
"fmt"
"log"
kubeid "github.com/launchpad001/kubeid/sdk"
)
func main() {
// HTTP
httpClient, err := kubeid.New(kubeid.WithHTTPEndpoint("http://kubeid.kubeid.svc:8080"))
if err != nil {
log.Fatal(err)
}
id, err := httpClient.Generate(context.Background())
if err != nil {
log.Fatal(err)
}
fmt.Println(id.Value, id.String)
// gRPC
grpcClient, err := kubeid.New(kubeid.WithGRPCEndpoint("kubeid.kubeid.svc:9090"))
if err != nil {
log.Fatal(err)
}
defer grpcClient.Close()
ids, err := grpcClient.GenerateBatch(context.Background(), 10)
if err != nil {
log.Fatal(err)
}
fmt.Println(len(ids))
}When both HTTP and gRPC endpoints are set, the last endpoint option wins.
Use WithProtocol(...) after the endpoint options to force a transport.
Generate retries transient failures automatically (default: 2 retries → 3 attempts, 50ms base backoff), including when a node is not ready (HTTP 503 / gRPC Unavailable / ErrNotReady, e.g. lease loss):
client, err := kubeid.New(
kubeid.WithHTTPEndpoint("http://kubeid.kubeid.svc:8080"),
kubeid.WithMaxRetries(5), // total attempts = 6
kubeid.WithRetryBackoff(100*time.Millisecond), // optional
)
// WithMaxRetries(0) disables retries.
// Permanent 4xx / InvalidArgument fail fast (no retry).GenerateBatch / Parse / Info are not retried by the SDK.
Images are published to GHCR on pushes to the default branch and on version tags:
ghcr.io/launchpad001/kubeid:latest
ghcr.io/launchpad001/kubeid:sha-<short>
ghcr.io/launchpad001/kubeid:vX.Y.Z # from git tags
# local build
make docker
# or
make docker IMAGE=ghcr.io/launchpad001/kubeid TAG=devDeploy manifests use ghcr.io/launchpad001/kubeid:latest. After the first release,
ensure the package is public (or configure an imagePullSecret) under the repo’s
Packages settings.
Node ID = pod ordinal (kubeid-0 → 0).
Replicas must be ≤ 2^node_bits (default 1024). Scaling beyond that causes pods
with ordinals outside the bit layout to fail at resolve time.
kubectl apply -f deploy/statefulset.yamlEach pod claims a free node ID via coordination.k8s.io/Lease.
- Leases are renewed at ~1/3 of
KUBEID_LEASE_DURATION. - Transient renew failures are retried with backoff for up to ~½ lease TTL; permanent loss of ownership (holder changed) fails immediately.
- On terminal renew failure the process marks itself not ready (HTTP
/readyz+ gRPC health), refuses furtherGeneratecalls, then exits so Kubernetes restarts it. - Graceful shutdown (
SIGTERM) releases the lease (with conflict retries) and does not treat a concurrent renew error as fatal. - Take-over of “expired” leases applies a skew safety margin (~2s, capped at ⅓ TTL).
Run NTP (or equivalent) on all nodes so lease
RenewTimeremains meaningful.
kubectl apply -f deploy/rbac.yaml
kubectl apply -f deploy/deployment-lease.yaml| Variable | Default | Description |
|---|---|---|
KUBEID_EPOCH_MS |
1767196800000 |
Snowflake epoch (Unix ms) |
KUBEID_NODE_BITS |
10 |
Bits for node ID (must be > 0) |
KUBEID_STEP_BITS |
12 |
Bits for sequence (must be > 0) |
KUBEID_NODE_ID_MODE |
statefulset |
static | statefulset | lease |
KUBEID_NODE_ID |
— | Required for static mode |
POD_NAME |
— | Downward API (statefulset / lease) |
POD_NAMESPACE |
default |
Downward API (lease) |
KUBEID_LEASE_PREFIX |
kubeid-node |
Lease name prefix |
KUBEID_LEASE_DURATION |
15s |
Lease TTL (≥ 1s) |
KUBEID_MAX_NODES |
2^node_bits-1 |
Inclusive max node id for lease pool |
KUBEID_HTTP_ADDR |
:8080 |
HTTP listen address |
KUBEID_GRPC_ADDR |
:9090 |
gRPC listen address |
KUBEID_LOG_LEVEL |
info |
debug | info | warn | error |
api/proto/ Protobuf definitions
api/gen/ Generated gRPC/protobuf code
cmd/kubeid/ Server entrypoint
internal/snowflake/ Core ID generator (configurable epoch)
internal/nodeid/ K8s node ID resolution
internal/server/ HTTP + gRPC handlers
internal/config/ Env-based configuration
sdk/ Go client SDK
deploy/ Kubernetes manifests
make proto # requires protoc, protoc-gen-go, protoc-gen-go-grpc
make tidy
make test
make buildApache-2.0