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

Pod logs, exec & status

logs() returns a pod’s log as one string; streamLogs() is a generator that yields it line by line — ideal for tailing a running container. With follow: true it runs until the container stops, or until client.stream_timeout seconds pass with no output — not the request timeout:

use RoundlyConsulting\KubernetesApi\DataTransferObjects\PodLogOptions;

$pod = $cluster->pods()->setNamespace('shop')->withName('checkout-5f7c9d8b6-x2kqj');

// Fetch logs as a string.
$logs = $pod->logs(new PodLogOptions(container: 'app', tailLines: 200, timestamps: true));

// The previous (crashed) instance of the container, last hour only.
$crash = $pod->logs(new PodLogOptions(container: 'app', previous: true, sinceSeconds: 3600));

// Stream logs line by line (tail). With follow: true it runs until the container stops
// (or client.stream_timeout seconds pass with no output), not the request timeout.
foreach ($pod->streamLogs(new PodLogOptions(container: 'app', follow: true)) as $line) {
    echo $line.PHP_EOL;
}

PodLogOptions

OptionTypeDefaultEffect
container?stringnullWhich container to read in a multi-container pod.
followboolfalseKeep the stream open for new lines (streamLogs() defaults to true).
tailLines?intnullOnly the last N lines.
sinceSeconds?intnullOnly lines newer than N seconds.
timestampsboolfalsePrefix every line with its timestamp.
previousboolfalseThe previous, terminated instance of the container — the crash log.
limitBytes?intnullCap the number of bytes returned.

Exec

exec() runs a command inside a container and returns an ExecResult with the captured stdout, stderr and exit code. It opens its own TLS WebSocket to the apiserver speaking the v4.channel.k8s.io subprotocol, with the client’s token, certificates and verification settings:

// Exec a command inside a pod (over a WebSocket; returns stdout, stderr, and exit code).
$result = $cluster->pods()
    ->setNamespace('shop')
    ->withName('checkout-5f7c9d8b6-x2kqj')
    ->exec(['php', 'artisan', 'cache:clear'], container: 'app');

$result->stdout;       // captured standard output
$result->stderr;       // captured standard error
$result->exitCode;     // 0 (null if the stream ended before the command finished)
$result->successful(); // true only for exit code 0
$result->completed();  // false if the connection dropped or client.stream_timeout passed

// Need a shell? Ask for one explicitly.
$cluster->pods()->setNamespace('shop')->withName('checkout-5f7c9d8b6-x2kqj')
    ->exec(['sh', '-c', 'df -h /var/cache && echo done']);
  • Pass the command as an argument list — each element is sent as its own command parameter, so no shell quoting is involved. Wrap it in ['sh', '-c', '…'] when you need a shell.
  • container selects the container in a multi-container pod; tty: true requests a TTY. stdin is never attached.
  • The connection dials the cluster URL’s own scheme, host, port (443 when none is given) and path prefix, so a proxied apiserver (https://rancher.example/k8s/clusters/c-abc) works just like a direct one. Connecting has a 30-second timeout; connection and upgrade failures throw WebSocketException. Exec calls are not counted against the rate-limit budget.
  • exec() waits for the apiserver to report the command’s exit status. If the stream ends first — a dropped connection or client.stream_timeout — exitCode is null, completed() and successful() are false: never a false success.

Pod and container status

Ask simple questions instead of decoding raw status — is the pod running, are its containers ready, why did one restart:

$pod = $cluster->pods()->setNamespace('shop')->withName('checkout-5f7c9d8b6-x2kqj')->find();

$pod->isRunning();             // status.phase === 'Running'
$pod->isSuccessful();          // 'Succeeded'
$pod->hasFailed();             // 'Failed'
$pod->containersReady();       // every container status is ready
$pod->initContainersReady();
$pod->getQos();                // status.qosClass, 'BestEffort' when absent
$pod->getStatusMessage();

$status = $pod->getContainerStatus('app');   // ?ContainerStatus
$status?->getState();          // 'running' | 'waiting' | 'terminated' | 'unknown'
$status?->getStateReason();    // e.g. 'CrashLoopBackOff'
$status?->restarts();          // restart count
$status?->isReady();
$status?->getStartedAt();      // ?Carbon

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.