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

Resource accessors

Every packaged resource has a typed accessor — on the facade, bound to the default cluster, and on every Cluster — that returns a fresh, cluster-bound resource object. Each accessor reads the resources map at call time, so a name you point at your own subclass returns that class; custom names resolve the same way:

use RoundlyConsulting\KubernetesApi\Facades\Kubernetes;
use RoundlyConsulting\KubernetesApi\Resources\Deployment;

Kubernetes::pods();                // Resources\Pod, on the default cluster

$cluster = Kubernetes::cluster('production');

$cluster->pods();                  // Resources\Pod
$cluster->deployments();           // Resources\Deployment
$cluster->namespaces();            // Resources\KubernetesNamespace
$cluster->traefikIngressRoutes();  // Resources\TraefikIngressRoute

// Namespaced resources target the cluster's default namespace unless you set one
$cluster->pods()->setNamespace('shop')->get();

// List across every namespace
$cluster->pods()->allNamespaces()->get();

// Cluster-scoped resources ignore setNamespace()
$cluster->nodes()->get();

// Any resource class, with or without an accessor
Kubernetes::resource(Deployment::class)->setNamespace('shop')->get();
$cluster->resource(Deployment::class)->setNamespace('shop')->get();
Deployment::make()->setCluster($cluster)->setNamespace('shop')->get();

Built-in resources

AccessorClassKindapiVersionScope
clusterRoles()ClusterRoleClusterRolerbac.authorization.k8s.io/v1Cluster
clusterRoleBindings()ClusterRoleBindingClusterRoleBindingrbac.authorization.k8s.io/v1Cluster
configMaps()ConfigMapConfigMapv1Namespace
cronJobs()CronJobCronJobbatch/v1Namespace
daemonSets()DaemonSetDaemonSetapps/v1Namespace
deployments()DeploymentDeploymentapps/v1Namespace
endpoints()EndpointsEndpointsv1Namespace
events()EventEventv1Namespace
horizontalPodAutoscalers()HorizontalPodAutoscalerHorizontalPodAutoscalerautoscaling/v2Namespace
ingresses()IngressIngressnetworking.k8s.io/v1Namespace
jobs()JobJobbatch/v1Namespace
limitRanges()LimitRangeLimitRangev1Namespace
namespaces()KubernetesNamespaceNamespacev1Cluster
networkPolicies()NetworkPolicyNetworkPolicynetworking.k8s.io/v1Namespace
nodes()NodeNodev1Cluster
persistentVolumes()PersistentVolumePersistentVolumev1Cluster
persistentVolumeClaims()PersistentVolumeClaimPersistentVolumeClaimv1Namespace
pods()PodPodv1Namespace
replicaSets()ReplicaSetReplicaSetapps/v1Namespace
replicationControllers()ReplicationControllerReplicationControllerv1Namespace
resourceQuotas()ResourceQuotaResourceQuotav1Namespace
roles()RoleRolerbac.authorization.k8s.io/v1Namespace
roleBindings()RoleBindingRoleBindingrbac.authorization.k8s.io/v1Namespace
secrets()SecretSecretv1Namespace
serviceAccounts()ServiceAccountServiceAccountv1Namespace
services()ServiceServicev1Namespace
statefulSets()StatefulSetStatefulSetapps/v1Namespace
storageClasses()StorageClassStorageClassstorage.k8s.io/v1Cluster
traefikIngressRoutes()TraefikIngressRouteIngressRoutetraefik.groupNamespace
traefikMiddlewares()TraefikMiddlewareMiddlewaretraefik.groupNamespace
traefikServersTransports()TraefikServersTransportServersTransporttraefik.groupNamespace
traefikTlsOptions()TraefikTlsOptionTLSOptiontraefik.groupNamespace
traefikTlsStores()TraefikTlsStoreTLSStoretraefik.groupNamespace

Classes live in RoundlyConsulting\KubernetesApi\Resources. The Namespace class is KubernetesNamespace, because namespace is a reserved word in PHP; its accessor is still namespaces(). apiVersion v1 maps to /api/v1, every other version to /apis/<version>. Traefik resources take their group from the traefik.group config key (traefik.io/v1alpha1 by default).

Scoping to a namespace

namespace() scopes a cluster to one namespace, and everything it hands out is pinned there: every request goes to that namespace, and any attempt to leave it — setNamespace('other'), allNamespaces(), ignoreNamespace(), re-scoping the cluster, binding the resource to a cluster scoped elsewhere — throws NamespaceScopeException. Items a scoped listing returns stay pinned too:

use RoundlyConsulting\KubernetesApi\Facades\Kubernetes;

$shop = Kubernetes::namespace('shop');           // or Kubernetes::cluster('production')->namespace('shop')

$shop->pods()->whereLabel('app', 'web')->get();  // GET /api/v1/namespaces/shop/pods?labelSelector=app%3Dweb
$shop->pods()->setNamespace('kube-system');      // NamespaceScopeException
$shop->pods()->allNamespaces();                  // NamespaceScopeException
$shop->nodes()->get();                           // cluster-scoped kinds are unaffected
$shop->namespaceScope();                         // 'shop'

// A soft default instead of a boundary — setNamespace() still works
$cluster = Kubernetes::cluster('production')->withDefaultNamespace('shop');

A namespace on a clusters entry, or withDefaultNamespace(), is only a default; namespace() is the boundary. Cluster-scoped kinds — nodes, namespaces, cluster roles and the like — are unaffected.

Names can’t get around it either. Before a request is sent, every value that goes into the URL path is checked — anything else throws InvalidResourceException and nothing is sent, so withName('../../kube-system/secrets/admin-token') can’t leave shop:

  • Names must be a single path segment: not . or .., and no /, % or whitespace. They are also percent-encoded.
  • Namespaces must be valid DNS-1123 labels.
  • Plurals and apiVersions must match their Kubernetes grammar.
  • The raw request($method, $path) escape hatch is the exception: it sends the path you give it exactly as written and is not scoped.

Per-kind operations

  • scale() — deployments, replica sets, stateful sets and replication controllers.
  • rolloutRestart() — deployments, stateful sets and daemon sets.
  • logs(), streamLogs() and exec() — pods.
  • Everything else — get, find, create, update, updateOrCreate, patch, delete, watch, dryRun — works on every resource.

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.