Kubernetes audit logs provide a chronological record of requests processed by the Kubernetes API server.
They help platform, security and operations teams answer questions such as:
However, enabling audit logging does not automatically create useful visibility.
A production implementation must decide:
This guide covers those decisions for both self-managed Kubernetes clusters and Amazon EKS.
Important update: Promtail reached end of life on 2 March 2026. New Loki-based implementations should use Grafana Alloy or another supported collector rather than building new Promtail pipelines.
Every Kubernetes request passes through the kube-apiserver.
During this process, Kubernetes can generate an audit event containing information about the request, the requesting identity, the target resource and the result.
A typical audit event may include:
| Audit field | What it tells you |
|---|---|
| user.username | The user, ServiceAccount or component that made the request |
| user.groups | The groups associated with the requesting identity |
| verb | The Kubernetes operation, such as get, create, patch or delete |
| objectRef.resource | The type of resource being accessed |
| objectRef.namespace | The namespace containing the resource |
| objectRef.name | The name of the target resource |
| sourceIPs | The source addresses associated with the request |
| userAgent | The client or Kubernetes component making the request |
| responseStatus.code | The resulting HTTP response code |
| annotations | Additional context, including the authorisation decision |
| requestReceivedTimestamp | When the API server received the request |
| stageTimestamp | When the recorded audit stage was reached |
Kubernetes audit events can be recorded at different stages of the request lifecycle:
The Kubernetes audit policy determines which requests and stages are retained.
Audit logs focus on Kubernetes API activity. They do not provide complete visibility into every action inside a cluster.
| Signal | Main question answered |
|---|---|
| Audit logs | Who requested or changed something through the Kubernetes API? |
| API server metrics | Is the control plane healthy and performing normally? |
| Distributed traces | Where did a slow request spend its time? |
| Application logs | What happened inside the application? |
| Runtime security events | What happened inside a running container or on a node? |
| Cloud audit logs | Which cloud provider APIs were called, and by which identity? |
For example, Kubernetes audit logs can show that a user requested pods/exec. They will not provide a complete record of every command subsequently executed inside the container.
Audit logging should therefore be part of a wider Kubernetes security and observability architecture rather than treated as a standalone security control.

A common mistake is to collect as much information as possible and decide how to use it later.
This can create:
Kubernetes supports four audit levels.
| Level | Recorded information | Typical use |
|---|---|---|
| None | Nothing | Health checks and known low-value traffic |
| Metadata | Identity, time, resource, verb and result | General activity monitoring |
| Request | Metadata and request body | Selected configuration changes |
| RequestResponse | Metadata, request body and response body | Narrow investigations requiring full context |
The rules in an audit policy are processed in order. The first matching rule wins.

This means specific exclusions and sensitive-resource rules must appear before broad default rules.
The following example is deliberately conservative.
It removes common low-value requests, avoids capturing Secret and ConfigMap bodies, records selected security-related configuration changes and uses Metadata as the final default.
apiVersion: audit.k8s.io/v1
kind: Policy
omitStages:
- RequestReceived
rules:
# Exclude common health and readiness checks.
- level: None
nonResourceURLs:
- "/healthz*"
- "/livez*"
- "/readyz*"
- "/version"
# Exclude known high-volume kube-proxy watch operations.
- level: None
users:
- "system:kube-proxy"
verbs:
- "watch"
resources:
- group: ""
resources:
- "endpoints"
- "services"
- "services/status"
# Record access to sensitive resources without storing their bodies.
- level: Metadata
resources:
- group: ""
resources:
- "secrets"
- "configmaps"
- "serviceaccounts/token"
# Capture request bodies for selected security-related changes.
- level: Request
verbs:
- "create"
- "update"
- "patch"
- "delete"
- "deletecollection"
resources:
- group: "rbac.authorization.k8s.io"
resources:
- "roles"
- "rolebindings"
- "clusterroles"
- "clusterrolebindings"
- group: "admissionregistration.k8s.io"
resources:
- "mutatingwebhookconfigurations"
- "validatingwebhookconfigurations"
- group: "apiextensions.k8s.io"
resources:
- "customresourcedefinitions"
# Track interactive access to containers without capturing full bodies.
- level: Metadata
resources:
- group: ""
resources:
- "pods/exec"
- "pods/attach"
- "pods/portforward"
# Record read activity at Metadata level.
- level: Metadata
verbs:
- "get"
- "list"
- "watch"
# Metadata is the default for everything not matched above.
- level: Metadata
This example should be treated as a starting point, not a universal policy.
A production policy should be adjusted according to:
RequestResponse should only be enabled for narrowly defined resources and operations. Using it broadly can capture sensitive data and significantly increase log volume.
Scope note: This custom audit policy example applies to self-managed Kubernetes. On Amazon EKS, AWS manages the control plane audit policy. Customers configure whether audit records are delivered to CloudWatch Logs and how those records are retained, queried and monitored.
For self-managed Kubernetes, audit logging is configured through the API server.
For the file-based audit log backend, the API server needs:
--audit-policy-file="/etc/kubernetes/audit-policy.yaml"
--audit-log-path="/var/log/kubernetes/audit/audit.log"
File rotation should also be configured.
--audit-log-maxage=7
--audit-log-maxbackup=10
--audit-log-maxsize=100
These values are examples. Retention and rotation must be sized for the cluster’s request volume and the external log shipping interval.
When the API server runs as a static Pod, as it commonly does in kubeadm-based clusters, both the policy file and audit log directory must be mounted into the API server container.
The log directory also needs to remain available on the control plane host so that audit records can be collected and persisted outside the node.
A typical architecture is:
kube-apiserver
↓
Local audit file
↓
Grafana Alloy on each control plane node
↓
Grafana Loki
↓
Grafana dashboards and alerts
Grafana Alloy can read audit files from the control plane host and send them to Loki.
A minimal Alloy configuration looks like this:
local.file_match "kubernetes_audit" {
path_targets = [
{
__path__ = "/var/log/kubernetes/audit/*.log",
job = "kubernetes-audit",
cluster = "production-eu-west-2",
environment = "production",
},
]
}
loki.source.file "kubernetes_audit" {
targets = local.file_match.kubernetes_audit.targets
forward_to = [loki.write.audit.receiver]
}
loki.write "audit" {
endpoint {
url = "https://<loki-endpoint>/loki/api/v1/push"
}
}
Authentication, TLS configuration and secret handling have been omitted from the example and must be added for the target environment.
The example does not set tail_from_end = true. When that option is enabled and no saved file position exists, Alloy begins reading at the end of the file and does not ingest older entries already present in it.
The collector must have access to the host path where the API server writes audit records.
Possible deployment models include:
When using a DaemonSet, check:
Audit log collection should not introduce a new failure path for the Kubernetes API server.
Audit records contain many fields that appear useful as labels but have very high cardinality.
Avoid turning values such as these into Loki index labels:
High-cardinality labels create many short-lived Loki streams and can significantly affect index size, query performance and storage behaviour.
Prefer a small set of stable labels:
Fields such as usernames, resource names and source IPs should remain inside the structured JSON record and be parsed when queried.
Collecting audit events is not sufficient. Teams also need to know when the audit pipeline stops working.
For self-managed API servers, monitor:
rate(apiserver_audit_event_total[5m])
This indicates the rate of audit events generated and sent to the configured backend.
Audit errors should remain at zero:
sum(rate(apiserver_audit_error_total[5m])) > 0
Metric stability: Kubernetes currently classifies apiserver_audit_event_total and apiserver_audit_error_total as Alpha metrics. Confirm their availability against the Kubernetes version used by your cluster before depending on them in production alerts.
An alert should also detect:
A security control that can fail silently is not a reliable security control.
Amazon EKS manages the Kubernetes control plane, so customers do not have direct access to its audit files or control the underlying audit policy.
EKS can deliver control plane audit logs to Amazon CloudWatch Logs when the audit log type is enabled for the cluster.
The required control plane log types must be selected on a per-cluster basis. When enabled, EKS sends the selected logs to CloudWatch Logs in the same AWS account.
The following AWS CLI command enables the audit log type:
aws eks update-cluster-config \
--region eu-west-2 \
--name production-cluster \
--logging '{
"clusterLogging": [
{
"types": ["audit"],
"enabled": true
}
]
}'
When enabled, the logs are stored in a CloudWatch log group using the following pattern:
/aws/eks/<cluster-name>/cluster
EKS supports several control plane log types:
The audit log provides Kubernetes API activity. The other control plane logs answer different operational questions and may also be needed when investigating control plane performance or authentication behaviour.
Enabling these logs creates CloudWatch ingestion, storage and query costs. Retention should therefore be explicitly configured rather than leaving log groups without a defined lifecycle.
For broader AWS and EKS security controls, audit logging should be designed alongside IAM, Kubernetes RBAC, network security and AWS cloud security requirements.
Once audit logging is enabled, CloudWatch Logs Insights can be used to investigate activity and build detection logic.
fields @timestamp,
user.username,
sourceIPs.0,
verb,
objectRef.resource,
responseStatus.code
| filter @logStream like /kube-apiserver-audit/
| filter user.username = "system:anonymous"
| filter responseStatus.code in [401, 403]
| sort @timestamp desc
| limit 100
A sudden increase may indicate:
fields @timestamp,
user.username,
verb,
objectRef.resource,
objectRef.namespace,
objectRef.name,
responseStatus.code
| filter @logStream like /kube-apiserver-audit/
| filter objectRef.resource in [
"roles",
"rolebindings",
"clusterroles",
"clusterrolebindings"
]
| filter verb in ["create", "update", "patch", "delete"]
| sort @timestamp desc
| limit 100
RBAC changes should be correlated with:
fields @timestamp,
user.username,
sourceIPs.0,
objectRef.namespace,
objectRef.name,
objectRef.subresource,
responseStatus.code
| filter @logStream like /kube-apiserver-audit/
| filter objectRef.resource = "pods"
| filter objectRef.subresource in ["exec", "attach", "portforward"]
| sort @timestamp desc
| limit 100
Interactive access may be legitimate during incident response, but it should be rare and attributable in production environments.
fields @timestamp
| filter @logStream like /kube-apiserver-audit/
| filter responseStatus.code in [401, 403]
| stats count(*) as denied_requests by bin(5m)
This query converts individual audit events into a time-series signal that can be reviewed against a normal cluster baseline.

Audit logs are events.
Metrics are aggregated time-series signals derived from those events.
A single 403 response may not be important. Fifty denied requests from the same identity or source within five minutes may require investigation.
Useful audit-derived signals include:
| Signal | Example detection logic | Potential action |
|---|---|---|
| Failed authentication rate | Count 401 responses over five minutes | Investigate identity or credential failures |
| Authorisation denial rate | Count 403 responses by user and source | Check RBAC or attempted access |
| RBAC mutations | Create, patch or delete against RBAC resources | Validate against an approved change |
| Secret access | Reads by unusual users or ServiceAccounts | Review workload identity and scope |
| Interactive Pod access | Exec, attach or port-forward activity in production | Confirm operational approval |
| Admission control changes | Changes to validating or mutating webhooks | Review cluster-wide security impact |
| CRD changes | Create, update or delete of CRDs | Check platform and operator changes |
| Anonymous activity | Requests from system:anonymous | Validate API exposure and clients |
| Impersonation use | Requests containing impersonated identities | Confirm approved administrative use |
| Event-volume change | Sudden increase or decrease in audit events | Check cluster activity or pipeline health |
Thresholds should not be copied between environments without validation.
Development, CI and production clusters will have different request patterns. Baselines should account for:
For Loki environments, LogQL metric queries can aggregate matching audit records. Frequently used queries can be evaluated continuously through Loki recording rules. When remote write is configured, the resulting samples can be sent to a Prometheus-compatible metrics backend.
For CloudWatch, teams can use:
The target architecture should match the expected event volume and investigation workflow rather than introducing another disconnected dashboard.
The API server must retain audit context while requests are processed.
Broad Request or RequestResponse rules can increase memory usage, processing requirements and event volume. Monitor audit volume before and after policy changes rather than assuming the new policy has no platform impact.
Amazon CloudWatch Logs has a maximum log entry size that is smaller than the maximum Kubernetes API request size.
Large EKS audit entries may therefore be truncated or contain only request metadata. Detection logic should not assume every record contains a complete request or response body.
Retention should reflect:
A practical design may use:
In Loki, retention must be deliberately configured through the Compactor and storage lifecycle settings.
In CloudWatch, define a retention period for each EKS control plane log group.
Audit records can contain sensitive security information.
Controls should include:
The people operating workloads should not automatically have permission to delete the records used to investigate their activity.
Every production alert needs:
An alert that nobody owns will eventually be ignored.
This generates large volumes of data and may store sensitive content without improving detection.
Audit logs record API activity. They do not replace container runtime, node, network or application-level security monitoring.
A central log store is not the same as operational monitoring. High-value events must be converted into queries, metrics and tested alerts.
Promtail is end of life. Existing implementations should plan migration, and new deployments should use Grafana Alloy or another supported collector.
Values such as audit IDs, resource names, usernames and source IPs can create excessive stream cardinality.
Unlimited retention increases cost and may conflict with data governance requirements.
Single-event alerts create noise. Use severity, environment, identity, change windows and aggregation to separate normal operations from suspicious activity.
Before considering Kubernetes audit monitoring complete, confirm that:
For a self-managed cluster, the API server requires an audit policy and an audit backend. If no audit policy file is supplied, audit events are not recorded through the Kubernetes audit system.
On Amazon EKS, AWS manages the control plane audit policy. The audit control plane log type must be enabled for the cluster before those records are delivered to CloudWatch Logs.
Prometheus is designed for metrics rather than storing raw audit events. The Kubernetes API server exposes metrics about the audit subsystem, while additional metrics can be derived from logs using Loki recording rules or other log-processing systems.
Audit logs record requests handled by the Kubernetes API server. Application logs record events produced by the software running inside containers. A deployment change may appear in the audit logs, while an exception caused by that deployment will usually appear in the application logs.
Not on their own. Audit logs may show API activity performed by the compromised workload, such as reading Secrets or creating resources. They will not record every process, file or network action inside the container. Runtime security and node-level monitoring are required for that visibility.
There is no single retention period for every organisation. Retention should be based on security investigation needs, compliance obligations, detection time, storage cost and archive requirements. The decision should be documented rather than inherited from a platform default.
Kubernetes audit logging is not complete when logs arrive in Loki or CloudWatch.
The operational value comes from connecting:
Bion helps teams assess, design and implement Kubernetes audit monitoring across Amazon EKS, self-managed Kubernetes and hybrid environments.
Our work can include: