# PD Automation Runner: Installation

Deploy the PD Automation Runner into your infrastructure using Kubernetes YAML, Helm, Terraform, Docker Run, or Docker Compose.

This page covers how to deploy the PD Automation Runner into your infrastructure. For what the runner is, how it works, and how to register it and use it in a Workflow, see [PD Automation Runner](/ai-automation/automation/pd-automation-runner).

> **Before You Start:** [Register the runner in PagerDuty](/ai-automation/automation/pd-automation-runner) and have your three credential values ready — `RUNNER_ID`, `RUNNER_SECRET`, and `RUNNER_PDTOKEN`. In all commands on this page, replace `<version>` with the runner image version specified in your PagerDuty onboarding documentation.

## Choose a Deployment Method

| Method | Runs As | Kubernetes Actions? |
| :--- | :--- | :--- |
| Kubernetes YAML | Pod inside your cluster | Yes (recommended) |
| Helm | Pod inside your cluster | Yes (chart not yet published) |
| Terraform | Pod inside your cluster | Yes |
| Docker Run | Standalone container | No |
| Docker Compose | Standalone container | No |

> **Kubernetes Actions Require In-Cluster Deployment:** Kubernetes Workflow Actions authenticate to the Kubernetes API using the runner pod's service account token, so the runner must run as a pod inside the cluster it manages. Use Kubernetes YAML, Helm, or Terraform for Kubernetes actions.

## Configure RBAC (Kubernetes)

The runner pod must have a Kubernetes service account with permission to perform the operations your Kubernetes Workflow Actions require. Using a service account (rather than a kubeconfig with personal credentials) is the correct pattern: tokens are automatically rotated, permissions are scoped and auditable, and no credential file needs to be managed.

The following ClusterRole grants the minimum permissions required for all six Kubernetes Workflow Actions. Apply it in place of any wildcard grant you may have used during initial setup. The Kubernetes YAML and Terraform methods in the following sections include these rules inline; this standalone copy is provided for reference or for applying RBAC separately.

```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: pd-automation-runner
  labels:
    app.kubernetes.io/name: pd-automation-runner
    app.kubernetes.io/part-of: pd-automation
rules:
  # Core resources — list and describe support
  - apiGroups: [""]
    resources:
      - configmaps
      - namespaces
      - nodes
      - persistentvolumes
      - persistentvolumeclaims
      - secrets
      - services
    verbs: [get, list]

  # Pods — all pod actions (list, describe, create, delete, logs, exec)
  - apiGroups: [""]
    resources: [pods]
    verbs: [get, list, create, delete]
  - apiGroups: [""]
    resources: [pods/log]
    verbs: [get]
  - apiGroups: [""]
    resources: [pods/exec]
    verbs: [create]

  # Apps resources
  - apiGroups: [apps]
    resources: [daemonsets, deployments, replicasets, statefulsets]
    verbs: [get, list]

  # Batch resources
  - apiGroups: [batch]
    resources: [cronjobs, jobs]
    verbs: [get, list]

  # Networking
  - apiGroups: [networking.k8s.io]
    resources: [ingresses]
    verbs: [get, list]

  # Policy — list-objects only (describe-object does not support PDBs)
  - apiGroups: [policy]
    resources: [poddisruptionbudgets]
    verbs: [list]

  # Storage
  - apiGroups: [storage.k8s.io]
    resources: [storageclasses]
    verbs: [get, list]

  # CRDs — list-objects only (describe-object does not support CRDs)
  - apiGroups: [apiextensions.k8s.io]
    resources: [customresourcedefinitions]
    verbs: [list]
```

> **Secrets Access:** The List Objects and Describe Object actions can target Kubernetes Secrets. The `secrets: [get, list]` entry gives the runner read access to all Secrets across the cluster when combined with a ClusterRoleBinding. If this is too broad for your security posture, restrict access to specific namespaces by using a `RoleBinding` (instead of a `ClusterRoleBinding`) in each namespace where the runner operates. A ClusterRoleBinding is only strictly required for cluster-scoped resources (Namespaces, Nodes, PersistentVolumes, Storage Classes, Custom Resource Definitions).

## Option A: Kubernetes YAML (Recommended for Kubernetes Actions)

Deploy the runner using `kubectl apply`. This is the recommended method for Kubernetes Workflow Actions.

**Step 1: Apply namespace and RBAC.** Save the following as `pd-runner-rbac.yaml` and apply it:

```yaml
apiVersion: v1
kind: Namespace
metadata:
  name: pd-automation-runner
  labels:
    app.kubernetes.io/name: pd-automation-runner
    app.kubernetes.io/part-of: pd-automation

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: pd-automation-runner
  namespace: pd-automation-runner
  labels:
    app.kubernetes.io/name: pd-automation-runner
    app.kubernetes.io/part-of: pd-automation

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: pd-automation-runner
  labels:
    app.kubernetes.io/name: pd-automation-runner
    app.kubernetes.io/part-of: pd-automation
rules:
  - apiGroups: [""]
    resources:
      - configmaps
      - namespaces
      - nodes
      - persistentvolumes
      - persistentvolumeclaims
      - secrets
      - services
    verbs: [get, list]
  - apiGroups: [""]
    resources: [pods]
    verbs: [get, list, create, delete]
  - apiGroups: [""]
    resources: [pods/log]
    verbs: [get]
  - apiGroups: [""]
    resources: [pods/exec]
    verbs: [create]
  - apiGroups: [apps]
    resources: [daemonsets, deployments, replicasets, statefulsets]
    verbs: [get, list]
  - apiGroups: [batch]
    resources: [cronjobs, jobs]
    verbs: [get, list]
  - apiGroups: [networking.k8s.io]
    resources: [ingresses]
    verbs: [get, list]
  - apiGroups: [policy]
    resources: [poddisruptionbudgets]
    verbs: [list]
  - apiGroups: [storage.k8s.io]
    resources: [storageclasses]
    verbs: [get, list]
  - apiGroups: [apiextensions.k8s.io]
    resources: [customresourcedefinitions]
    verbs: [list]

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: pd-automation-runner
  labels:
    app.kubernetes.io/name: pd-automation-runner
    app.kubernetes.io/part-of: pd-automation
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: pd-automation-runner
subjects:
  - kind: ServiceAccount
    name: pd-automation-runner
    namespace: pd-automation-runner
```

```bash
kubectl apply -f pd-runner-rbac.yaml
```

**Step 2: Create the credentials secret.** Do not put credentials in a YAML file that could be committed to source control. Create the secret directly from your credential values:

```bash
kubectl create secret generic pd-automation-runner-credentials \
  --namespace pd-automation-runner \
  --from-literal=RUNNER_ID="<your-runner-id>" \
  --from-literal=RUNNER_SECRET="<your-runner-secret>" \
  --from-literal=RUNNER_PDTOKEN="<your-pd-api-token>"
```

**Step 3: Deploy the runner.** Save the following as `pd-runner-deployment.yaml`:

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: pd-automation-runner
  namespace: pd-automation-runner
  labels:
    app.kubernetes.io/name: pd-automation-runner
    app.kubernetes.io/part-of: pd-automation
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: pd-automation-runner
  template:
    metadata:
      labels:
        app.kubernetes.io/name: pd-automation-runner
        app.kubernetes.io/part-of: pd-automation
    spec:
      serviceAccountName: pd-automation-runner
      automountServiceAccountToken: true
      containers:
        - name: runner
          image: rundeckpro/runner:<version>
          imagePullPolicy: IfNotPresent
          env:
            - name: RUNNER_ID
              valueFrom:
                secretKeyRef:
                  name: pd-automation-runner-credentials
                  key: RUNNER_ID
            - name: RUNNER_SECRET
              valueFrom:
                secretKeyRef:
                  name: pd-automation-runner-credentials
                  key: RUNNER_SECRET
            - name: RUNNER_PDTOKEN
              valueFrom:
                secretKeyRef:
                  name: pd-automation-runner-credentials
                  key: RUNNER_PDTOKEN
            - name: RUNNER_CLOUD_URL
              value: "https://api.pagerduty.com"  # EU: https://api.eu.pagerduty.com
            - name: RUNNER_LOG_OUTPUT
              value: "console"
          resources:
            requests:
              cpu: "100m"
              memory: "256Mi"
            limits:
              cpu: "500m"
              memory: "512Mi"
      restartPolicy: Always
```

```bash
kubectl apply -f pd-runner-deployment.yaml
```

> **API Endpoint:** `RUNNER_CLOUD_URL` defaults to the NA production endpoint (`https://api.pagerduty.com`). EU accounts should set it to `https://api.eu.pagerduty.com`. The value must match the region where the runner was registered — a runner registered in one region will not authenticate against another.

**Step 4: Verify.** Confirm the pod is running and follow its logs to confirm it connected to PagerDuty:

```bash
kubectl -n pd-automation-runner get pods
kubectl -n pd-automation-runner logs deploy/pd-automation-runner --follow
```

A successful connection produces log lines indicating the runner registered and is polling for jobs. Once connected, the runner appears as **Healthy** in **Incident Workflows**  →  **Automation Connectors**  →  **Self-hosted Runners**.

**Updating.** To update the runner image version, change the `image:` field in `pd-runner-deployment.yaml` and re-apply with `kubectl apply -f pd-runner-deployment.yaml`. Kubernetes performs a rolling update with zero downtime.

**Removing.** Delete the deployment, RBAC, secret, and namespace, then deregister the runner in **Incident Workflows**  →  **Automation Connectors**  →  **Self-hosted Runners**:

```bash
kubectl delete -f pd-runner-deployment.yaml
kubectl delete -f pd-runner-rbac.yaml
kubectl delete secret pd-automation-runner-credentials -n pd-automation-runner
kubectl delete namespace pd-automation-runner
```

## Option B: Helm

> **Note:** A PagerDuty-published Helm chart for the PD Automation Runner is not yet available. The commands in this section reflect the anticipated interface and will be updated when the chart is released. Use the Kubernetes YAML method in the meantime.

Add the repository:

```bash
helm repo add pagerduty https://charts.pagerduty.com
helm repo update
```

For production, create a credentials secret before installing so values are not stored in Helm release history:

```bash
kubectl create namespace pd-automation-runner

kubectl create secret generic pd-automation-runner-credentials \
  --namespace pd-automation-runner \
  --from-literal=RUNNER_ID="<your-runner-id>" \
  --from-literal=RUNNER_SECRET="<your-runner-secret>" \
  --from-literal=RUNNER_PDTOKEN="<your-pd-api-token>"
```

Then install referencing the existing secret (EU accounts set `runner.cloudUrl` to `https://api.eu.pagerduty.com`):

```bash
helm install pd-automation-runner pagerduty/pd-automation-runner \
  --namespace pd-automation-runner \
  --set runner.existingSecret="pd-automation-runner-credentials" \
  --set runner.cloudUrl="https://api.pagerduty.com"
```

Verify the runner connected:

```bash
kubectl -n pd-automation-runner logs deploy/pd-automation-runner --follow
```

### Key Chart Values

| Value | Description | Default |
| :--- | :--- | :--- |
| `runner.id` | Runner ID from PagerDuty | — |
| `runner.secret` | Runner secret from PagerDuty | — |
| `runner.pdToken` | PagerDuty API token | — |
| `runner.cloudUrl` | PagerDuty API endpoint. NA production default; EU customers use `https://api.eu.pagerduty.com` | `https://api.pagerduty.com` |
| `runner.existingSecret` | Name of a pre-created credentials secret | — |
| `rbac.create` | Create ServiceAccount, ClusterRole, ClusterRoleBinding | `true` |
| `rbac.clusterRole.rules` | Override RBAC rules | Scoped to Kubernetes action permissions |
| `image.repository` | Runner image repository | `rundeckpro/runner` |
| `image.tag` | Runner image version | Chart default |
| `resources.requests.cpu` | CPU request | `100m` |
| `resources.requests.memory` | Memory request | `256Mi` |
| `resources.limits.cpu` | CPU limit | `500m` |
| `resources.limits.memory` | Memory limit | `512Mi` |

## Option C: Terraform

Deploy the runner into a Kubernetes cluster using the Terraform Kubernetes provider. Create the credentials secret manually first — Terraform does not manage it, to avoid storing sensitive values in state:

```bash
kubectl create secret generic pd-automation-runner-credentials \
  --namespace pd-automation-runner \
  --from-literal=RUNNER_ID="<your-runner-id>" \
  --from-literal=RUNNER_SECRET="<your-runner-secret>" \
  --from-literal=RUNNER_PDTOKEN="<your-pd-api-token>"
```

> **EU Accounts:** Add `--from-literal=RUNNER_CLOUD_URL="https://api.eu.pagerduty.com"` and reference it as an env var in the deployment, or set the `runner_cloud_url` variable in the following configuration.

Configure the provider in `main.tf` (the example uses a kubeconfig file; see the [Terraform Kubernetes provider docs](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs) for EKS, GKE, and AKS authentication options):

```hcl
terraform {
  required_version = ">= 1.5"

  required_providers {
    kubernetes = {
      source  = "hashicorp/kubernetes"
      version = "~> 2.30"
    }
  }
}

provider "kubernetes" {
  config_path    = "~/.kube/config"
  config_context = "<your-kubectl-context>"
}
```

Declare variables in `variables.tf`:

```hcl
variable "runner_image" {
  description = "Runner Docker image to deploy"
  type        = string
  default     = "rundeckpro/runner:<version>"
}

variable "runner_cloud_url" {
  description = "PagerDuty API endpoint the runner connects to"
  type        = string
  default     = "https://api.pagerduty.com"
}
```

Define the namespace, service account, RBAC, and deployment in `runner.tf`:

```hcl
locals {
  runner_name      = "pd-automation-runner"
  runner_namespace = "pd-automation-runner"
  common_labels = {
    "app.kubernetes.io/name"    = "pd-automation-runner"
    "app.kubernetes.io/part-of" = "pd-automation"
  }
}

resource "kubernetes_namespace" "runner" {
  metadata {
    name   = local.runner_namespace
    labels = local.common_labels
  }
}

resource "kubernetes_service_account" "runner" {
  metadata {
    name      = local.runner_name
    namespace = kubernetes_namespace.runner.metadata[0].name
    labels    = local.common_labels
  }

  automount_service_account_token = true
}

resource "kubernetes_cluster_role" "runner" {
  metadata {
    name   = local.runner_name
    labels = local.common_labels
  }

  rule {
    api_groups = [""]
    resources  = ["configmaps", "namespaces", "nodes", "persistentvolumes", "persistentvolumeclaims", "secrets", "services"]
    verbs      = ["get", "list"]
  }

  rule {
    api_groups = [""]
    resources  = ["pods"]
    verbs      = ["get", "list", "create", "delete"]
  }

  rule {
    api_groups = [""]
    resources  = ["pods/log"]
    verbs      = ["get"]
  }

  rule {
    api_groups = [""]
    resources  = ["pods/exec"]
    verbs      = ["create"]
  }

  rule {
    api_groups = ["apps"]
    resources  = ["daemonsets", "deployments", "replicasets", "statefulsets"]
    verbs      = ["get", "list"]
  }

  rule {
    api_groups = ["batch"]
    resources  = ["cronjobs", "jobs"]
    verbs      = ["get", "list"]
  }

  rule {
    api_groups = ["networking.k8s.io"]
    resources  = ["ingresses"]
    verbs      = ["get", "list"]
  }

  rule {
    api_groups = ["policy"]
    resources  = ["poddisruptionbudgets"]
    verbs      = ["list"]
  }

  rule {
    api_groups = ["storage.k8s.io"]
    resources  = ["storageclasses"]
    verbs      = ["get", "list"]
  }

  rule {
    api_groups = ["apiextensions.k8s.io"]
    resources  = ["customresourcedefinitions"]
    verbs      = ["list"]
  }
}

resource "kubernetes_cluster_role_binding" "runner" {
  metadata {
    name   = local.runner_name
    labels = local.common_labels
  }

  role_ref {
    api_group = "rbac.authorization.k8s.io"
    kind      = "ClusterRole"
    name      = kubernetes_cluster_role.runner.metadata[0].name
  }

  subject {
    kind      = "ServiceAccount"
    name      = kubernetes_service_account.runner.metadata[0].name
    namespace = kubernetes_namespace.runner.metadata[0].name
  }
}

resource "kubernetes_deployment" "runner" {
  metadata {
    name      = local.runner_name
    namespace = kubernetes_namespace.runner.metadata[0].name
    labels    = local.common_labels
  }

  spec {
    replicas = 1

    selector {
      match_labels = {
        "app.kubernetes.io/name" = local.runner_name
      }
    }

    template {
      metadata {
        labels = local.common_labels
      }

      spec {
        service_account_name            = kubernetes_service_account.runner.metadata[0].name
        automount_service_account_token = true

        container {
          name              = "runner"
          image             = var.runner_image
          image_pull_policy = "IfNotPresent"

          env {
            name = "RUNNER_ID"
            value_from {
              secret_key_ref {
                name = "pd-automation-runner-credentials"
                key  = "RUNNER_ID"
              }
            }
          }

          env {
            name = "RUNNER_SECRET"
            value_from {
              secret_key_ref {
                name = "pd-automation-runner-credentials"
                key  = "RUNNER_SECRET"
              }
            }
          }

          env {
            name = "RUNNER_PDTOKEN"
            value_from {
              secret_key_ref {
                name = "pd-automation-runner-credentials"
                key  = "RUNNER_PDTOKEN"
              }
            }
          }

          env {
            name  = "RUNNER_CLOUD_URL"
            value = var.runner_cloud_url
          }

          env {
            name  = "RUNNER_LOG_OUTPUT"
            value = "console"
          }

          resources {
            requests = {
              cpu    = "100m"
              memory = "256Mi"
            }
            limits = {
              cpu    = "500m"
              memory = "512Mi"
            }
          }
        }

        restart_policy = "Always"
      }
    }
  }

  depends_on = [kubernetes_cluster_role_binding.runner]
}
```

Apply and verify:

```bash
terraform init
terraform plan
terraform apply

kubectl -n pd-automation-runner logs deploy/pd-automation-runner --follow
```

## Option D: Docker Run

> **Kubernetes Actions Are Not Supported with Docker Deployment:** Kubernetes Workflow Actions require the runner to run as a pod inside the target cluster. Use the Kubernetes YAML method for Kubernetes actions.

```bash
docker run -d \
  --name pd-automation-runner \
  --restart unless-stopped \
  -e RUNNER_ID="<your-runner-id>" \
  -e RUNNER_SECRET="<your-runner-secret>" \
  -e RUNNER_PDTOKEN="<your-pd-api-token>" \
  -e RUNNER_CLOUD_URL="https://api.pagerduty.com" \
  -e RUNNER_LOG_OUTPUT="console" \
  rundeckpro/runner:<version>
```

> **EU Accounts:** Set `RUNNER_CLOUD_URL` to `https://api.eu.pagerduty.com`.

Verify the runner connected:

```bash
docker logs -f pd-automation-runner
```

## Option E: Docker Compose

> **Kubernetes Actions Are Not Supported with Docker Deployment:** Use the Kubernetes YAML method for Kubernetes actions.

Create a `.env` file with your credentials (do not commit it to source control):

```bash
RUNNER_ID=<your-runner-id>
RUNNER_SECRET=<your-runner-secret>
RUNNER_PDTOKEN=<your-pd-api-token>
```

Create `docker-compose.yml` (EU accounts set `RUNNER_CLOUD_URL` to `https://api.eu.pagerduty.com`):

```yaml
services:
  pd-automation-runner:
    image: rundeckpro/runner:<version>
    container_name: pd-automation-runner
    restart: unless-stopped
    environment:
      RUNNER_ID: "${RUNNER_ID}"
      RUNNER_SECRET: "${RUNNER_SECRET}"
      RUNNER_PDTOKEN: "${RUNNER_PDTOKEN}"
      RUNNER_CLOUD_URL: "https://api.pagerduty.com"
      RUNNER_LOG_OUTPUT: "console"
```

Start the runner and verify it connected:

```bash
docker compose up -d
docker compose logs -f pd-automation-runner
```

In all cases, the runner appears as **Healthy** in **Incident Workflows**  →  **Automation Connectors**  →  **Self-hosted Runners** once connected.

## Troubleshooting

### Runner Shows Unhealthy in PagerDuty

The runner appears in **Incident Workflows**  →  **Automation Connectors**  →  **Self-hosted Runners** but its status is **Unhealthy**, or it does not connect at all. Check the runner logs first:

```bash
kubectl -n pd-automation-runner logs deploy/pd-automation-runner --follow
```

| Symptom in Logs | Cause | Fix |
| :--- | :--- | :--- |
| `401 Unauthorized` or `authentication failed` | Invalid `RUNNER_ID`, `RUNNER_SECRET`, or `RUNNER_PDTOKEN` | Verify credentials match what PagerDuty generated at registration. Re-create the secret if needed. |
| `connection refused` or `unable to connect` | Network egress blocked | The runner requires outbound HTTPS (port 443) to the PagerDuty API endpoint configured in `RUNNER_CLOUD_URL`. Check firewall and proxy rules. |
| Pod is in `CrashLoopBackOff` | Container exiting on startup | Run `kubectl -n pd-automation-runner describe pod <pod-name>` for the exit reason. Usually a missing or malformed secret. |
| No log output at all | Pod not starting | Run `kubectl -n pd-automation-runner get pods` to check pod status, then `describe` for events. |

### Action Fails with "Runner Offline" or Times Out

A Workflow action returns `Failed` with a message indicating the runner did not respond.

1. Confirm the runner pod is running: `kubectl -n pd-automation-runner get pods`
2. Confirm the runner shows **Healthy** in **Incident Workflows**  →  **Automation Connectors**  →  **Self-hosted Runners**.
3. Confirm the **Connection Input** in the Workflow action references the correct runner.
4. Check runner logs for a received job — a healthy runner logs each job it receives.

### Action Fails with a Permissions Error

The action executes but returns an error like `forbidden: User "system:serviceaccount:pd-automation-runner:pd-automation-runner" cannot list resource "pods"`. The runner's service account does not have the required RBAC permissions.

1. Verify the ClusterRole and ClusterRoleBinding exist:

```bash
kubectl get clusterrole pd-automation-runner
kubectl get clusterrolebinding pd-automation-runner
```

2. Check the ClusterRole rules match the scoped manifest in the [Configure RBAC](#configure-rbac-kubernetes) section. If you started with a wildcard grant and removed it, ensure the replacement rules cover the resource type the action is trying to access.
3. If you switched from a ClusterRoleBinding to per-namespace RoleBindings, verify a RoleBinding exists in the namespace the action is targeting:

```bash
kubectl get rolebinding -n <target-namespace> | grep pd-automation-runner
```

4. Apply the corrected RBAC and restart the runner:

```bash
kubectl apply -f pd-runner-rbac.yaml
kubectl -n pd-automation-runner rollout restart deploy/pd-automation-runner
```

### Action Fails with "Unknown Object Type" or Similar Plugin Error

The runner received the job but the Kubernetes plugin could not process it.

1. Check runner logs for the specific error message around the time of the action invocation.
2. Verify the Object Type input matches a supported type for that action (refer to the input tables in [Kubernetes Workflow Actions](/ai-automation/automation/pd-automation-runner/kubernetes-workflow-actions)).
3. Verify the Namespace input is correct for namespace-scoped resources.

### Credentials Secret Is Missing or Malformed

Symptom: the pod goes into `CrashLoopBackOff` immediately and logs show a missing environment variable. Verify the secret exists and contains exactly these keys — `RUNNER_ID`, `RUNNER_SECRET`, `RUNNER_PDTOKEN`:

```bash
kubectl get secret pd-automation-runner-credentials -n pd-automation-runner
kubectl describe secret pd-automation-runner-credentials -n pd-automation-runner
```

To re-create the secret:

```bash
kubectl delete secret pd-automation-runner-credentials -n pd-automation-runner

kubectl create secret generic pd-automation-runner-credentials \
  --namespace pd-automation-runner \
  --from-literal=RUNNER_ID="<your-runner-id>" \
  --from-literal=RUNNER_SECRET="<your-runner-secret>" \
  --from-literal=RUNNER_PDTOKEN="<your-pd-api-token>"

kubectl -n pd-automation-runner rollout restart deploy/pd-automation-runner
```

### Checking Runner Health in PagerDuty

Navigate to **Incident Workflows**  →  **Automation Connectors**  →  **Self-hosted Runners** and select the runner by name. The detail page shows its **Status** (Healthy / Unhealthy), **Last seen** timestamp (most recent heartbeat), and **Active jobs** currently being processed. If the runner is Healthy but actions are still failing, the issue is likely in the action's configuration (wrong connection input, wrong namespace, or insufficient RBAC) rather than the runner itself.

## Learn More

- [PD Automation Runner](/ai-automation/automation/pd-automation-runner)
- [Kubernetes Workflow Actions](/ai-automation/automation/pd-automation-runner/kubernetes-workflow-actions)
