> ## Documentation Index
> Fetch the complete documentation index at: https://docs.formae.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Kubernetes configuration

> Configure a Kubernetes target for formae: credentials and target settings.

Deploying to a Kubernetes cluster? Point the K8s plugin at it. You get typed Pkl resources, schemas that match your cluster's exact version, and auth that works with EKS, AKS, GKE, OKE, or any kubeconfig.

## Configuration

### Target

```kotlin theme={"languages":{"custom":["/languages/pkl.json"]}}
import "@formae/formae.pkl"
import "@k8s/k8s.pkl" as k8s

target: formae.Target = new formae.Target {
  label = "k8s-local"
  config = new k8s.Config {
    kubernetesVersion = "1.31"
    auth = new k8s.KubeconfigAuth {}
  }
}
```

Apply it against your current kubectl context with `formae apply --mode reconcile`.

### Authentication

Pick an `auth` class on `k8s.Config` and the plugin handles token refresh, request
signing, and per-provider quirks. You configure it once and the plugin keeps it
valid for every API call the target makes.

| Cluster                 | Auth class       | How it stays valid                                             |
| ----------------------- | ---------------- | -------------------------------------------------------------- |
| Local kubectl context   | `KubeconfigAuth` | Reads your kubeconfig at apply time                            |
| formae running as a pod | `InClusterAuth`  | ServiceAccount token from the pod's mounted secret             |
| AWS EKS                 | `EKSAuth`        | Presigned STS token, refreshed on every request                |
| Azure AKS               | `AKSAuth`        | Azure AD token, auto-refreshed                                 |
| GCP GKE                 | `GKEAuth`        | OAuth2 access token, auto-refreshed                            |
| Oracle OKE              | `OCIAuth`        | OCI signed request, signed on each call                        |
| OVHcloud                | `OVHAuth`        | `endpoint`, `certificateAuthority`, `serviceName`, `clusterId` |

**KubeconfigAuth (local development).** The default for kubectl-reachable
clusters. With no fields set, the plugin uses whatever your current context
points at. Override either field for something specific:

```kotlin theme={"languages":{"custom":["/languages/pkl.json"]}}
auth = new k8s.KubeconfigAuth {
  context = "kind-formae-test"
  kubeconfig = "/path/to/kubeconfig"   // defaults to $KUBECONFIG or ~/.kube/config
}
```

**InClusterAuth (formae running as a pod).** When the formae agent runs inside
the cluster it manages, the plugin reads the ServiceAccount token mounted at
`/var/run/secrets/kubernetes.io/serviceaccount/`. The pod needs the RBAC to do
whatever the forma describes.

```kotlin theme={"languages":{"custom":["/languages/pkl.json"]}}
auth = new k8s.InClusterAuth {}
```

**EKSAuth (AWS EKS).** Reference the endpoint, CA, and cluster name from the EKS
cluster resource, and formae waits until the cluster exists before reading them.
The plugin uses your AWS credentials to fetch a presigned STS token and signs
every API call. The AWS principal needs `eks:DescribeCluster`, and its mapped
Kubernetes RBAC governs what it can do inside the cluster.

```kotlin theme={"languages":{"custom":["/languages/pkl.json"]}}
auth = new k8s.EKSAuth {
  endpoint             = eksCluster.res.endpoint
  certificateAuthority = eksCluster.res.certificateAuthorityData
  clusterName          = eksCluster.res.name
  region               = "us-west-2"   // optional, defaults to AWS_REGION
}
```

**AKSAuth (Azure AKS).** The AKS create API does not return the cluster CA
directly, so the plugin makes a follow-up call with the cluster's admin
credentials to fetch it. Referencing `certificateAuthority` runs that follow-up
automatically before your workload deploys. Auth uses `DefaultAzureCredential`
(environment variables, then the `az` CLI, then managed identity).

```kotlin theme={"languages":{"custom":["/languages/pkl.json"]}}
auth = new k8s.AKSAuth {
  endpoint             = aksCluster.res.fqdn
  certificateAuthority = aksCluster.res.certificateAuthority
  resourceGroup        = resourceGroup.res.name
  clusterName          = aksCluster.res.name
}
```

**GKEAuth (GCP GKE).** The plugin uses application-default credentials to mint an
OAuth2 access token. The principal needs `container.developer` at minimum, or
more granular RBAC bound at the cluster level.

```kotlin theme={"languages":{"custom":["/languages/pkl.json"]}}
auth = new k8s.GKEAuth {
  endpoint             = gkeCluster.res.endpoint
  certificateAuthority = gkeCluster.res.clusterCaCertificate
}
```

**OCIAuth (Oracle OKE).** The plugin signs each API call with your OCI keys
(api-key or session token, per `~/.oci/config`), so there is no long-lived bearer
token. Your OCI user needs the `OKE_CLUSTER_USE` policy plus Kubernetes RBAC. OKE
grants the cluster creator an admin token, so an apply that creates the cluster
gets RBAC for free.

```kotlin theme={"languages":{"custom":["/languages/pkl.json"]}}
auth = new k8s.OCIAuth {
  endpoint             = okeCluster.res.endpoint
  certificateAuthority = okeCluster.res.certificateAuthority
  clusterOcid          = okeCluster.res.id
  region               = "us-chicago-1"   // optional, defaults to ~/.oci/config
}
```

**Which to use.** When formae runs outside the cluster, use `KubeconfigAuth` for
local development, or the matching cloud-native class from CI or when you
provisioned the cluster in the same forma (reference the cluster resource so
there is no race). When formae runs inside the cluster, use `InClusterAuth`.

### Kubernetes version

Set `kubernetesVersion` to your cluster's K8s version (e.g. `"1.31"`). The plugin matches it to the right schema, so fields that don't exist in that version of Kubernetes fail at `pkl eval` time instead of failing against your live cluster.

```kotlin theme={"languages":{"custom":["/languages/pkl.json"]}}
config = new k8s.Config {
  kubernetesVersion = "1.31"     // must match the @k8s/v<X.Y>/* imports below
  auth = new k8s.KubeconfigAuth {}
}
```

Skip it and the plugin assumes `1.36` (the newest version it ships schemas for). Set it explicitly for anything older.

Each supported minor ships its own schema package and runs its own conformance suite on every push to main. The current supported set is visible in the [conformance badges](https://github.com/platform-engineering-labs/formae-plugin-k8s#kubernetes-plugin-for-formae) on the plugin's README.

### Set the namespace on every namespaced resource

Every namespaced resource must set `metadata.namespace` explicitly. The plugin won't fall back to K8s' `default` namespace. Missing values produce an error at apply time. Declare a `Namespace` in the same forma and reference its name so it lives in one place:

```kotlin theme={"languages":{"custom":["/languages/pkl.json"]}}
local appNs = new namespace.Namespace {
  metadata = new namespace.NamespaceMetadata { name = "my-app" }
}

forma {
  appNs

  new deployment.Deployment {
    metadata = new k8s.NamespacedObjectMeta {
      name = "api"
      namespace = appNs.res.name   // resolvable ref into the namespace above
    }
    spec { ... }
  }
}
```

<Note>
  Namespaced kinds use `NamespacedObjectMeta`. Cluster-scoped kinds (Namespace, ClusterRole, ClusterRoleBinding, PersistentVolume, StorageClass, and so on) use `ObjectMeta`. Pod templates and PVC templates also use `ObjectMeta`. Mix them up and `pkl eval` tells you exactly where.
</Note>
