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

ConfigMaps, secrets & networking

ConfigMaps

$cluster->configMaps()
    ->setNamespace('shop')
    ->setName('checkout-config')
    ->setData([
        'APP_ENV' => 'production',
        'nginx.conf' => $nginxConfig,
    ])
    ->create();

$config = $cluster->configMaps()->setNamespace('shop')->withName('checkout-config')->find();

$config->getData();                    // the whole map
$config->getData('nginx.conf');        // one key
$config->getData('MISSING', 'n/a');    // with a default

$config->addData('LOG_LEVEL', 'debug')
    ->removeData('APP_ENV')
    ->update();

Data keys are file names — nginx.conf, app.env — so getData(), addData() and removeData() always treat them as flat keys, never as dotted attribute paths. Removing the last key drops the data field altogether, because an empty map would encode as the JSON list [], which the apiserver refuses.

Secrets

Secret::setData() and addData() base64-encode values for you and getData() decodes them, so you only ever handle plain strings. Keys such as tls.crt or .dockerconfigjson stay flat:

$cluster->secrets()
    ->setNamespace('shop')
    ->setName('checkout-db')
    ->setData(['username' => 'checkout', 'password' => $password])   // base64-encoded for you
    ->create();

$secret = $cluster->secrets()->setNamespace('shop')->withName('checkout-db')->find();

$secret->getData('password');          // the decoded plain string
$secret->getData();                    // every key, decoded
$secret->addData('host', 'db.shop.svc.cluster.local')->update();

TLS certificate secrets

Secret supports a typed type plus a TLS convenience that base64-encodes a PEM certificate and key into a kubernetes.io/tls Secret — ready for an ingress or a Traefik TLSStore:

$secret = $cluster->secrets()
    ->setNamespace('default')
    ->setName('wildcard-tls')
    ->asTlsCertificate($pemCertificate, $pemPrivateKey)   // sets type + data['tls.crt'] / data['tls.key']
    ->create();

$secret->getType();              // "kubernetes.io/tls"
$secret->getData('tls.crt');     // the decoded PEM certificate

// Or set an arbitrary Secret type explicitly:
$cluster->secrets()->setName('dockercfg')->setType('kubernetes.io/dockerconfigjson');

Services

use RoundlyConsulting\KubernetesApi\Resources\Types\Port;

$cluster->services()
    ->setNamespace('shop')
    ->setName('checkout')
    ->setType('ClusterIP')
    ->setSelectors(['app' => 'checkout'])
    ->addPort(Port::http(8080)->setName('http'))     // port 80 -> targetPort 8080, TCP
    ->addPort(Port::make()->setName('metrics')->setProtocol('TCP')->setPort(9090)->setTargetPort(9090))
    ->create();

$service = $cluster->services()->setNamespace('shop')->withName('checkout')->find();

$service->getClusterDns();      // checkout.shop.svc.cluster.local
$service->getType();            // 'ClusterIP'
$service->getPorts();           // Port[]
$service->getSelectors();       // ['app' => 'checkout']

// A LoadBalancer that keeps client source IPs
$cluster->services()
    ->setNamespace('shop')
    ->setName('checkout-public')
    ->setType('LoadBalancer')
    ->useLocalTrafficPolicy()                        // externalTrafficPolicy: Local
    ->setSelectors(['app' => 'checkout'])
    ->addPort(Port::http(8080)->setName('http'))
    ->create();

Ingresses

addRule() writes a host, a path and a backend service in one call; addTls() attaches a certificate secret to a list of hosts:

$cluster->ingresses()
    ->setNamespace('shop')
    ->setName('checkout')
    ->setIngressClassName('nginx')
    ->addRule('shop.example.com', '/', 'checkout', 80)             // pathType defaults to Prefix
    ->addRule('shop.example.com', '/api', 'checkout-api', 8080, 'Prefix')
    ->addTls(['shop.example.com'], 'wildcard-tls')
    ->create();

$ingress = $cluster->ingresses()->setNamespace('shop')->withName('checkout')->find();
$ingress->getRules();
$ingress->getTls();
$ingress->getLoadBalancerIngress();   // status.loadBalancer.ingress

Network policies and select-all

An empty label selector means “select all” in Kubernetes and must be sent as the empty object {}, not [] — which the apiserver rejects. The selector setters handle this for you, so passing an empty array serialises correctly:

// Default-deny / select-all: an empty podSelector matches every pod in the namespace.
$cluster->networkPolicies()
    ->setNamespace('shop')
    ->setName('default-deny')
    ->setPodSelector([])           // serialises to "podSelector": {}
    ->setPolicyTypes(['Ingress'])
    ->create();

// Then allow the ingress namespace to reach checkout on 8080.
$cluster->networkPolicies()
    ->setNamespace('shop')
    ->setName('allow-ingress-to-checkout')
    ->setPodSelector(['app' => 'checkout'])          // podSelector.matchLabels
    ->setPolicyTypes(['Ingress'])
    ->addIngressRule([
        'from' => [['namespaceSelector' => ['matchLabels' => ['kubernetes.io/metadata.name' => 'ingress']]]],
        'ports' => [['protocol' => 'TCP', 'port' => 8080]],
    ])
    ->create();

Service::setSelectors([]) serialises the same way. Workload pod selectors are different: the apiserver refuses an empty selector on apps/v1 workloads (Deployment, ReplicaSet, StatefulSet, DaemonSet), so always give them at least one label, as in setPodsSelectors(['app' => 'checkout']). For any other node that must be an empty object, set it to a RoundlyConsulting\KubernetesApi\Resources\Types\EmptyObject — it always serialises to {}.

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.