Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Kubernetes Client Laravel Package

teknoo/kubernetes-client

Teknoo Kubernetes Client is a PHP library for interacting with Kubernetes clusters through its API. Manage pods, deployments, services and other resources programmatically with a clean, object-oriented client suited to automation tools, controllers and integrations.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and wire up an HTTP transport. The package does not ship a PSR-18 client of its own — it discovers whatever adapter is installed. Pick one explicitly:

composer require teknoo/kubernetes-client php-http/guzzle7-adapter
# alternatives: symfony/http-client, php-http/curl-client, php-http/socket-client

Check the PHP requirement before you commit. The library targets PHP 8.4+. If your Laravel app is on 8.2/8.3, pin to an older release or you will not get past composer update. Verify against composer.json in the tag you install, not the README.

Connect and make one call. In-cluster is the simplest first case (service-account token + CA):

use Teknoo\Kubernetes\Client;

$client = new Client([
    'endpoint' => 'https://kubernetes.default.svc:443',
    'token'    => file_get_contents('/var/run/secrets/kubernetes.io/serviceaccount/token'),
    'cert'     => '/var/run/secrets/kubernetes.io/serviceaccount/ca.crt',
    'verify'   => true,
    // 'kubeconfig' => '/home/me/.kube/config' also works for local dev
]);

foreach ($client->pods()->setNamespace('default')->all() as $pod) {
    echo $pod->getMetadata()->getName(), ' => ', $pod->getStatus()->getPhase(), PHP_EOL;
}

Where to look first, in this order:

  1. src/Client.php — constructor option keys, kubeconfig loading, error mapping, setTmpDir().
  2. src/RepositoryRegistry.php — the definitive list of $client->pods() / ->deployments() / ->configMaps() accessors. This file tells you what the library can actually do faster than any docs.
  3. src/Repository/ — the verbs: all(), find(), first(), create(), update(), delete(), plus the selector setters.
  4. src/Model/Attribute/Explorer.php — how $model->getSpec()->getTemplate()->getMetadata() chains resolve. Once you understand this, every resource type is approachable.

Best first production use case: a read-only "cluster inventory / health" command — list pods per namespace, flag non-Running phases and restarts. It exercises namespaces, selectors, collections, and the exception path without risking a write.

Implementation Patterns

Wrap the client in one application-level gateway. With 0 dependents and 3 stars, do not let Teknoo\Kubernetes\Client leak across your codebase. Bind it once and expose intent-named methods:

// app/Services/Kubernetes/Gateway.php
final class Gateway
{
    public function __construct(private Client $client) {}

    public function runningPods(string $namespace, array $labels = []): Collection
    {
        return $this->client->pods()
            ->setNamespace($namespace)
            ->setLabelSelector($labels)
            ->all();
    }

    public function restartDeployment(string $namespace, string $name): void
    {
        $repo = $this->client->deployments()->setNamespace($namespace);
        $deployment = $repo->find($name);
        $deployment->getSpec()->getTemplate()->getMetadata()->setAnnotations([
            'kubectl.kubernetes.io/restartedAt' => (new DateTimeImmutable())->format(DATE_ATOM),
        ]);
        $repo->update($deployment);
    }
}

Register it in a service provider as a singleton, with the kubeconfig path / endpoint pulled from config/kubernetes.php and env. That gives you one place to swap the library out later.

Always set the namespace on the repository you just fetched. Repositories are stateful builders — setNamespace(), setLabelSelector(), setFieldSelector() mutate and persist on the instance. The safe idiom is $client->pods()->setNamespace($ns)->… on one fluent line, never $repo = $client->pods(); …later… $repo->all().

Reads: selectors beat client-side filtering. Push server-side filters down so you are not transferring the whole namespace:

$pods = $client->pods()
    ->setNamespace('prod')
    ->setLabelSelector(['app.kubernetes.io/name' => 'api', 'tier' => 'web'])
    ->all();

$ready = $client->pods()
    ->setNamespace('prod')
    ->setFieldSelector(['status.phase' => 'Running'])
    ->all();

Writes: build a model, then hand it to the repository. Models are plain attribute bags populated via set* calls; the explorer handles nesting.

$configMap = new \Teknoo\Kubernetes\Model\ConfigMap();
$configMap->setMetadata(['name' => 'app-config', 'namespace' => 'prod'])
          ->setData(['FEATURE_X' => 'on']);

$client->configMaps()->setNamespace('prod')->create($configMap);

For anything you run repeatedly (deploy scripts, queue jobs), check-then-create/update is fragile — prefer update() against a known name, or use patch semantics below.

Patches for CRDs and surgical edits. Repository/Strategy/PatchMergeTrait.php plus the Enums\PatchType (merge / strategic / JSON) is the extension point when you need to touch one field without a full read-modify-write, or when the resource is a custom resource with no first-class model:

$repo = $client->customResources()->setNamespace('default');
// or a repository built with PatchMergeTrait for your CRD
$repo->patch($name, ['spec' => ['replicas' => 3]], PatchType::Merge);

Confirm the exact signature in the trait — this is the least-documented surface and the one you will be reading source for.

Streaming (logs / exec). Look at Contracts/ for the streaming parser interface. Wire it to a Symfony StreamedResponse or a console command's output rather than buffering into a string; pod logs on a busy container are unbounded.

Testing. Two workable strategies:

  • Unit: mock the PSR-18 adapter so HttpClientDiscovery resolves your fake, then assert method/URI/body. Wrap the client behind your Gateway interface and most tests never touch the library at all.
  • Integration: the repo's own Behat suite runs against an in-memory fake API server that records requests. Copy that idea — a small fake Kubernetes API in your test suite is the only way to trust write paths (create/update/patch) before they hit a real cluster.

Local dev. Generate a short-lived token against a kind/k3d cluster and point endpoint at it. Never point tests at a shared cluster; delete() has no dry-run guard of its own.

Gotchas and Tips

The PHP 8.4 floor is the biggest adoption risk. Verify the tag's composer.json before promising this to a team. If you're stuck on 8.2/8.3, either pin to an older major or budget for a PHP upgrade — do not assume the README matches the tag you installed.

Adapter discovery fails quietly-ish. If no PSR-18 implementation is installed, you get a discovery exception at construction time, not at install time. Install the adapter in the same composer require and add a smoke test that instantiates the client.

Temp files leak in long-running processes. PEM content and embedded kubeconfig certs are written to private temp files owned by the client instance and removed when it is released. Under Octane, queue workers, or a long-lived singleton, "released" means the object goes out of scope — it won't. Call Client::setTmpDir() to point at a scratch directory and recycle the client (or flush it) on a schedule; otherwise you accumulate cert files on disk.

Two exception namespaces exist: src/Exception/ and src/Exceptions/. Both are live. Do not catch a single leaf class and assume you're covered — catch the package's base exception type, or your handler will miss 404s from one code path and 409s from another. Log the raw response body; the exception message alone often omits Kubernetes' reason/message detail that tells you why.

409 Conflict is normal, not exceptional. update() sends the resourceVersion you read. On any concurrently-edited object you get a conflict. Treat it as retryable: re-find(), re-apply your mutation, retry (bounded). Building this into your gateway from day one saves a lot of flaky deploy scripts.

Collections extend Illuminate collections. That's convenient (fluent filter/map/pluck), but it means the package pulls illuminate/support and your Laravel version constrains it. Check for a satisfied constraint after install rather than discovering it during a framework upgrade.

TLS verification must stay on outside local dev. 'verify' => false is genuinely useful against k3d with self-signed certs; it is a foot-gun anywhere else. Gate it on app()->environment('local').

No informers/watch for controllers. There's a streaming parser contract for logs/exec, but do not build a reconcile loop on this. If you need controller semantics, poll on an interval with resourceVersion awareness, or don't use this library for that component.

Debugging requests. The fastest path is to configure logging on the underlying PSR-18 client (Guzzle middleware or Symfony's LoggerInterface), not to instrument the library. If you need request-level assertions, that same seam is your entry point.

Extension points worth knowing: src/HttpClient/ (add a transport instantiator), RepositoryRegistry (register a repository for a CRD), PatchMergeTrait (patch strategies), and custom Model/ classes. Everything else — Support/, Enums/ — is internal and fair game to change between minor versions.

Vendor risk management. Zero dependents and a small star count means you are the QA team. Pin an exact version, keep the Behat-style fake-cluster test for every write path you use, and keep the library behind your Gateway interface so that replacing it with raw kubectl calls or a hand-rolled Guzzle client is a one-file change.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core