NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages
Kubernetes API for Laravel

Client-side rate limiting

Every apiserver request is paced through http-client-rate-limits-for-laravel, keyed per apiserver: the cluster URL’s host, port and path prefix, so each cluster behind a Rancher-style proxy gets its own budget. One busy cluster never starves another, even when every client uses the same manager name, and clients of the same apiserver share one budget whatever their manager name or credentials. Kubernetes API Priority and Fairness is per-apiserver, so this maps directly onto how the server enforces its own limits.

By default the client makes up to 400 requests per minute per cluster and paces anything beyond that — it waits for the window to free up rather than erroring. With adaptive on, a 429 from the apiserver is read for its Retry-After value and self-tunes the limiter, so the next request already backs off by exactly what the server asked for. Tune it from env:

KUBERNETES_RATELIMIT=400            # attempts per window, per cluster
KUBERNETES_RATELIMIT_TIMESPAN=minute
KUBERNETES_RATELIMIT_ADAPTIVE=true  # honour 429 Retry-After
# KUBERNETES_RATELIMIT_MAX_WAIT=2000  # ms — fail fast instead of waiting
# KUBERNETES_RATELIMIT_JITTER=250     # ms of random spread per defer
# KUBERNETES_RATELIMIT_OWNER=app
# KUBERNETES_RATELIMIT_ENABLED=false  # the raw, unthrottled client

The budget key is k8s:<owner>:<apiserver>, where <apiserver> is the host plus any non-default port and path prefix of the cluster URL; rate_limits.owner lets several apps or workers share — or isolate — a budget.

Each rate_limits key is read strictly: an unrecognised switch, a non-integer or out-of-range number, a timespan typo or a non-string owner throws InvalidConfigurationException naming the key instead of quietly falling back; a blank value is not set and takes the default (see Configuration).

Fail fast instead of waiting

Set a max_wait ceiling in milliseconds. When a request would have to wait longer, it throws RateLimitExceededException instead of blocking. It exposes ->cluster (the registered cluster name, or the apiserver host for an ad-hoc client) and retryAfterSeconds():

use RoundlyConsulting\KubernetesApi\Exceptions\RateLimitExceededException;

try {
    $cluster->pods()->get();
} catch (RateLimitExceededException $e) {
    report("Cluster {$e->cluster} is throttled; retry in {$e->retryAfterSeconds()}s");
}

The exception implements the HasRetryAfter contract from package-toolkit-for-laravel, so a host can handle every Roundly rate-limit failure in one place:

use RoundlyConsulting\PackageToolkit\Contracts\HasRetryAfter;

if ($e instanceof HasRetryAfter) {
    return response('Too Many Requests', 429, ['Retry-After' => $e->retryAfterSeconds()]);
}

Shared budgets across workers

The limiter defaults to an in-memory store — per process, ideal for a single worker or CLI run. For a budget shared across queue workers or servers, point the underlying package’s store at Cache, Redis or the database (HTTP_CLIENT_RATE_LIMITS_STORE); this package does not force a store:

// config/http-client-rate-limits.php — published by the rate-limits package
'store' => RoundlyConsulting\HttpClientRateLimits\Store\RedisStore::class,

What counts

Resource operations, log requests, watches, raw request() calls and kubernetes:ping all go through the limiter; exec connections do not, and neither does anything under Kubernetes::fake(). Set KUBERNETES_RATELIMIT_ENABLED=false for the raw, unthrottled client.

Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

More ways to support, including crypto

By donating, you agree to our donation terms.

Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.