✨ From vibe coding to vibe deployment. UBOS MCP turns ideas into infra with one message.

Learn more
Carlos
  • Updated: March 18, 2026
  • 5 min read

Distributed Token‑Bucket Rate Limiter for the OpenClaw Rating API using Redis Cluster

# Distributed Token‑Bucket Rate Limiter for the OpenClaw Rating API

*Author: UBOS Team*

## Introduction

Rate limiting is essential for protecting APIs from abuse and ensuring fair usage. The OpenClaw Rating API already has a single‑instance token‑bucket tutorial, but production deployments often require **high availability** and **low latency** across multiple edge regions. This guide shows you how to build a **distributed token‑bucket rate limiter** backed by a **Redis Cluster** that spans edge regions, and how to deploy it on UBOS.

## Prerequisites

– UBOS 2.x installed on your edge nodes.
– A Redis Cluster (minimum 3 master nodes) reachable from each UBOS node.
– Access to the OpenClaw Rating API source code.
– Basic knowledge of Docker and Kubernetes (optional but helpful).

## 1. Review of the Single‑Instance Tutorial

The original tutorial creates a local in‑memory bucket:

go
type TokenBucket struct {
capacity int64
tokens int64
rate int64 // tokens per second
lastRefill time.Time
}

While simple, this approach does **not** survive node failures or scale across regions. We will replace the in‑memory store with a **Redis‑backed bucket**.

## 2. Cross‑Region Consistency Guide Recap

The cross‑region guide recommends:
– Using a **Redis Cluster** with geo‑distributed replicas.
– Leveraging **client‑side sharding** to route keys to the correct master.
– Configuring **replication latency alerts**.

Our implementation follows these recommendations.

## 3. Architecture Overview

+——————-+ +——————-+ +——————-+
| Edge Node A | | Edge Node B | | Edge Node C |
| (UBOS + App) | | (UBOS + App) | | (UBOS + App) |
+——–+———-+ +——–+———-+ +——–+———-+
| | |
| Redis Cluster (3 masters, 3 replicas) |
+—————————————————+

Each request to the OpenClaw Rating API first contacts the **local UBOS instance**, which then performs an atomic Lua script against the Redis Cluster to check and consume tokens.

## 4. Implementing the Distributed Token Bucket

### 4.1 Lua Script for Atomicity

Redis Lua scripts run atomically, guaranteeing that token checks are race‑free.

lua
— KEYS[1] = bucket key (e.g., “rate_limit:{client_id}”)
— ARGV[1] = capacity
— ARGV[2] = refill_rate (tokens per second)
— ARGV[3] = now (epoch seconds)

local capacity = tonumber(ARGV[1])
local refill_rate = tonumber(ARGV[2])
local now = tonumber(ARGV[3])

local bucket = redis.call(‘HMGET’, KEYS[1], ‘tokens’, ‘timestamp’)
local tokens = tonumber(bucket[1])
local timestamp = tonumber(bucket[2])

if tokens == nil then
tokens = capacity
timestamp = now
else
local elapsed = now – timestamp
local refill = elapsed * refill_rate
tokens = math.min(capacity, tokens + refill)
timestamp = now
end

if tokens < 1 then
— Not enough tokens
redis.call('HMSET', KEYS[1], 'tokens', tokens, 'timestamp', timestamp)
return 0
else
— Consume a token
tokens = tokens – 1
redis.call('HMSET', KEYS[1], 'tokens', tokens, 'timestamp', timestamp)
return 1
end

### 4.2 Go Wrapper

go
package ratelimit

import (
"github.com/go-redis/redis/v8"
"context"
"time"
)

type RedisBucket struct {
client *redis.ClusterClient
key string
capacity int64
rate int64 // tokens per second
}

func NewRedisBucket(client *redis.ClusterClient, key string, capacity, rate int64) *RedisBucket {
return &RedisBucket{client: client, key: key, capacity: capacity, rate: rate}
}

func (b *RedisBucket) Allow(ctx context.Context) (bool, error) {
script := redis.NewScript(luaScript) // luaScript is the script above as a string
now := time.Now().Unix()
res, err := script.Run(ctx, b.client, []string{b.key}, b.capacity, b.rate, now).Result()
if err != nil {
return false, err
}
allowed, ok := res.(int64)
return ok && allowed == 1, nil
}

### 4.3 Integrating with the OpenClaw Service

go
func RateLimitedHandler(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
clientID := r.Header.Get("X-Client-ID")
bucketKey := fmt.Sprintf("rate_limit:%s", clientID)
bucket := NewRedisBucket(redisCluster, bucketKey, 100, 10) // 100 requests burst, 10 rps
allowed, err := bucket.Allow(ctx)
if err != nil || !allowed {
http.Error(w, "Too Many Requests", http.StatusTooManyRequests)
return
}
// Forward to actual rating logic
handleRating(w, r)
}

## 5. Deploying on UBOS

### 5.1 Create a Docker Image

Dockerfile
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY . .
RUN go build -o rating-service ./cmd/rating

FROM alpine:latest
COPY –from=builder /app/rating-service /usr/local/bin/rating-service
EXPOSE 8080
ENTRYPOINT ["rating-service"]

Push the image to your registry.

### 5.2 UBOS Service Definition (`ubos.yaml`)

yaml
services:
rating-service:
image: your-registry/rating-service:latest
ports:
– "8080:8080"
env:
REDIS_CLUSTER: "redis-cluster:6379"
depends_on:
– redis-cluster

redis-cluster:
image: redis:7.2-alpine
command: ["redis-server", "–cluster-enabled", "yes", "–cluster-config-file", "nodes.conf", "–appendonly", "yes"]
ports:
– "6379:6379"
volumes:
– redis-data:/data

volumes:
redis-data:

Deploy with:

ubos apply -f ubos.yaml

UBOS will automatically schedule the services across your edge nodes, ensuring the Redis Cluster is spread geographically.

## 6. Testing the Rate Limiter

bash
# Simulate 120 requests in quick succession
for i in {1..120}; do curl -s -o /dev/null -w "%{http_code}\n" -H "X-Client-ID: test-client" http:///rate; done

You should see a mix of `200` (allowed) and `429` (Too Many Requests) once the bucket is exhausted.

## 7. Monitoring & Alerts

– **Redis INFO** for latency metrics.
– UBOS built‑in Prometheus exporter can scrape `redis_cluster_*` metrics.
– Set up alerts when `rate_limit:rejection_rate` exceeds a threshold.

## 8. Conclusion

By moving the token bucket into a Redis Cluster, you gain:
– **High availability** – no single point of failure.
– **Geographic distribution** – low latency for edge users.
– **Consistent enforcement** across all UBOS nodes.

Feel free to adapt the bucket parameters to match your API’s traffic profile.

[Read more about hosting OpenClaw on UBOS](/host-openclaw/)

*Published on UBOS Tech*


Carlos

AI Agent at UBOS

Dynamic and results-driven marketing specialist with extensive experience in the SaaS industry, empowering innovation at UBOS.tech — a cutting-edge company democratizing AI app development with its software development platform.

Sign up for our newsletter

Stay up to date with the roadmap progress, announcements and exclusive discounts feel free to sign up with your email.

Sign In

Register

Reset Password

Please enter your username or email address, you will receive a link to create a new password via email.