Watching Kubernetes Resources
The Kubernetes backend plugin provides a watchResource() method on the
KubernetesWatcher interface that lets plugin authors stream resource changes
from the Kubernetes API in real time. This is the watch counterpart to the
existing get and list operations and follows the same error handling
patterns.
How it works
The method opens a long-lived HTTP connection to the Kubernetes API with
?watch=true and yields events as an
async iterator.
Each event represents a single change (creation, modification, or deletion) to a
resource in the cluster.
The stream processing pipeline is:
- HTTP GET with
?watch=trueopens a streaming connection. - The response body is piped through a line-delimited JSON parser.
- Each line is parsed and transformed into a
KubernetesWatchEvent. - Events are yielded to the caller via the async generator.
Usage
// The watcher is available through the KubernetesWatcher interface
// from @backstage/plugin-kubernetes-node
for await (const event of watcher.watchResource(
{
clusterDetails,
credential,
group: '', // empty string for core API group
apiVersion: 'v1',
plural: 'pods',
},
{ namespace: 'default', labelSelector: 'app=myapp' },
)) {
if (event.type === 'ERROR') {
logger.error(`Watch error: ${event.error.errorType}`);
break;
}
const obj = event.object as any;
logger.info(`${event.type}: ${obj.metadata.name}`);
}
Event types
The Kubernetes API sends the following event types, all of which are supported:
| Event type | Description |
|---|---|
ADDED | A resource was created or already exists at watch start. |
MODIFIED | A resource was updated. |
DELETED | A resource was removed. |
BOOKMARK | A checkpoint for the current resource version (minimal object). |
ERROR | An error occurred, such as an expired resource version. |
ADDED, MODIFIED, and DELETED events include the full Kubernetes object in
the object field and the resource version in the resourceVersion field.
BOOKMARK events include a minimal object (typically just
metadata.resourceVersion). ERROR events contain a structured
KubernetesFetchError with an errorType and statusCode.
Watch options
The KubernetesWatchOptions interface supports the following parameters:
| Option | Type | Description |
|---|---|---|
namespace | string | Namespace to watch (omit for cluster-scoped resources). |
labelSelector | string | Label selector to filter resources. |
resourceVersion | string | Resource version to start watching from. |
timeoutSeconds | number | Server-side timeout for the watch connection. |
allowWatchBookmarks | boolean | Enable bookmark events for efficient version tracking. |
sendInitialEvents | boolean | Begin the stream with synthetic events reproducing current state, ending with a bookmark annotated k8s.io/initial-events-end. Requires Kubernetes 1.32+ (Beta). |
resourceVersionMatch | 'NotOlderThan' | 'Exact' | How the resource version constraint is applied. Set to NotOlderThan when using sendInitialEvents so the server can serve from its watch cache. |
signal | AbortSignal | Abort signal to cancel the watch from outside the iteration loop. |
Watching grouped API resources
To watch resources from a named API group, provide the group, version, and plural name:
for await (const event of watcher.watchResource(
{
clusterDetails,
credential,
group: 'stable.example.com',
apiVersion: 'v1',
plural: 'crontabs',
},
{ namespace: 'production' },
)) {
// handle events
}
Error handling
The watch method follows the same errors-as-data pattern used by the existing
get and list operations. Errors are yielded as events rather than thrown as
exceptions, so consumers handle them in the same for await loop.
There are three categories of errors:
- HTTP errors (e.g., 401 Unauthorized, 404 Not Found): The method yields a
single
ERRORevent and stops. The error type is mapped using the same status code mapping asget/listoperations. - Stream errors from the Kubernetes API (e.g., 410 Gone for an expired
resource version): These arrive as
ERROR-type events in the stream and are yielded to the consumer. - Malformed JSON: Invalid lines are logged and skipped without interrupting the stream.
Authentication
The watch method reuses the same authentication mechanisms as the rest of the
Kubernetes backend plugin. Server-side auth providers (serviceAccount,
googleServiceAccount, aws, azure, localKubectlProxy) work with watch
connections. Client-side auth providers (google, oidc, aks) are not
supported because the watcher runs as a long-lived backend connection and cannot
refresh browser-mediated credentials.
Cancellation
To stop a watch from outside the iteration loop, pass an AbortSignal:
const controller = new AbortController();
// Cancel the watch after 30 seconds
setTimeout(() => controller.abort(), 30_000);
for await (const event of watcher.watchResource(
{
clusterDetails,
credential,
group: '',
apiVersion: 'v1',
plural: 'pods',
},
{ namespace: 'default', signal: controller.signal },
)) {
// handle events — loop ends cleanly when signal fires
}
Breaking out of the for await loop also stops the watch and cleans up the
underlying HTTP connection.
Limitations
- No automatic reconnection. When a watch connection ends (due to timeout,
network error, or server-side disconnect), the consumer is responsible for
reconnecting. Use the
resourceVersionfrom the last received event to resume without missing changes. - No informer behavior. This is a low-level watch primitive. It does not maintain a local cache, perform automatic list-watch initialization, or handle periodic resynchronization. These higher-level patterns can be built on top of the watch API.
- Single resource type per call. Each
watchResource()call watches one resource type. To watch multiple resource types, make separate calls.