Reference
kubectl plugin reference
Every flag, JSON field, exit status and permission of each kubectl napwake command, and the detail behind what it prints.
Every command
Section titled “Every command”The request limit and the connection flags every command shares. Namespaces and connections says which namespaces each command reads.
Each request to the API server gives up after 30 seconds. So a cluster that stops answering ends a command with an error. --request-timeout changes the limit. Its 0 keeps 30 seconds, where kubectl’s means no limit.
Every command takes kubectl’s connection flags:
--kubeconfig, --context, --cluster, --user, --as, --as-group, --as-uid, --token, --server, --certificate-authority, --client-certificate, --client-key, --insecure-skip-tls-verify, --tls-server-name, --proxy-url, --username, --password, --disable-compression and --request-timeout.
Its flags, with their defaults. Scan a cluster shows it in use.
| Flag | Default | What it does |
|---|---|---|
#--namespace -n |
all you can read | Namespace to scan. Repeat for several. |
#--all- -A |
Every namespace, failing rather than falling back. Not with -n. |
|
#--exclude- |
kube-system, kube-public, kube-node-lease |
Namespaces to skip |
#--output -o |
table |
table, json or yaml |
#--limit |
25 |
Rows in the table, idle first, then paused, then active, each by what pausing could free |
#--cpu- |
10 |
CPU use, as a percentage of requests from 0.1 to 100, such as 10 or 0.5, above which a workload is active |
#--cpu- |
0.031 |
Your price per vCPU-hour, in dollars, zero or more |
#--memory- |
0.004 |
Your price per GiB-hour of memory, in dollars, zero or more |
#--window |
7d |
Prometheus history to replay, such as 7d or 36h, up to 366d. 0 judges from CPU now. |
#--idle- |
1h |
Time without activity that makes a workload idle, as Napwake’s idleAfter |
#--prometheus- |
the one in the cluster with the most history | Prometheus Service in the cluster, as namespace/name or namespace/name:port, read through the API server |
#--prometheus- |
the one in the cluster with the most history | Prometheus API to read, such as Thanos, Mimir, or Google Cloud Managed Service for Prometheus |
#--prometheus- |
Label matchers added to every history query, such as 'cluster="prod"', for a Prometheus of several clusters |
|
#--prometheus- |
Header for --prometheus-url, as "Name: value", such as "X-Scope-OrgID: tenant" for Mimir. Repeat for several. |
|
#--prometheus- |
File holding a bearer token for --prometheus-url |
|
#--prometheus- |
PEM file of CA certificates to trust for --prometheus-url, besides the system’s |
|
#--prometheus- |
false |
Don’t verify --prometheus-url’s certificate |
#--timeout |
5m |
How long the scan may take before it gives up |
#--html |
a temporary file | Save the web report to this file, to share |
#--open |
true |
Open the web report, when the table is shown in a terminal |
More on the flags
Section titled “More on the flags”--limit 0 lists every workload. --limit also caps the dependency list, and JSON and YAML list everything.
--cpu-threshold and --idle-after apply now and in the replay. Workloads Napwake manages use their own settings. JSON and YAML give each workload’s as idleAfter.
--prometheus-selector values are double-quoted, as in PromQL.
More on the first line and the columns
Section titled “More on the first line and the columns”JSON and YAML keep the full context name. Their namespaces counts every namespace read.
Your bill:, under the first line, says what turns what pausing frees into savings. It’s from the node autoscaler nodes finds (JSON nodeAutoscaler).
| The node autoscaler | Your bill: says |
|---|---|
| Karpenter, Cluster Autoscaler or EKS Auto Mode | It drops only as it removes nodes |
| Karpenter | The same, with how its NodePools consolidate |
| Cluster Autoscaler, hinted at only by a Deployment | The same, and that it may not be running |
| GKE Autopilot, which bills what pods request | It drops as pods stop |
| None found | It won't drop until something removes nodes, with the guide to setting an autoscaler up |
| Your access doesn’t allow reading what would tell | It says so, and names what it couldn’t read |
Before Napwake runs, the scan can’t know what of COULD FREE/MO would be saved. So it never calls it a saving. Each money and history cell has the one source the table gives. A live workload isn’t replayed, as there are no pods for Prometheus to record while it’s paused. Its pauses are what FREED THIS MONTH and SAVED count.
A StatefulSet that deletes its PVCs when scaled down would lose its data to a pause. So Napwake refuses to pause it. It’s left out of what pausing could free and of the next steps.
A protected workload is left out of what pausing could free and the next steps. The scan sees only the label, not the operator’s protectedNamespaces patterns. A workload scaled to zero by hand is counted apart in the headline. It counts toward nothing the scan says could be freed.
How it decides a workload is idle
Section titled “How it decides a workload is idle”It uses the signals Napwake uses to pause.
| Workload | How it’s judged |
|---|---|
| Managed by Napwake | By the activity clock Napwake keeps for it. Idle is no activity until the clock’s pauseAt. That’s idleAfter after the last activity, or longer after pauses made in use. |
| Managed, after pauses made in use | BECAUSE says so, such as not idle yet: it woke 6m ago; after pauses made in use it waits 20 minutes, not 5 minutes |
| Managed, its activity clock | Combines every source configured, Prometheus queries too |
| Managed, held awake | The scan says so, such as for its dependents |
| Managed, its clock started without activity | BECAUSE says what started it |
| Started by a wake | not idle yet: it woke 3m ago |
| Started as Napwake took it on | not idle yet: Napwake began managing it 3m ago |
| Started by a gap in watching | not idle yet: Napwake wasn't watching until 3m ago |
| Any other, active | CPU above --cpu-threshold (10% by default) of what it requests |
| Any other, active | A rollout within --idle-after (1h by default) |
| Any other, active | A napwake.dev/last-activity annotation within --idle-after |
| Any other, active | A napwake.dev/active-until in the future |
| Any other, idle | None of those, with its CPU measured |
| Unmanaged, without metrics-server | As on EKS by default. Judged from its CPU history, and BECAUSE says so. |
| Its state then | Where the replay left the activity clock |
| No CPU requests, or no metrics yet | Can’t be judged. It’s in the notes, not the table, and out of the headline’s totals. |
What the notes list, and what the scan leaves out
Section titled “What the notes list, and what the scan leaves out”| In the notes | What it means |
|---|---|
| Replicas set from Git | A GitOps tool last set the workload’s replicas, by the field managers the API server records |
| Which tools | Argo CD, Flux, Fleet, kapp or Kluctl. Workloads are listed by tool. |
| Why it matters | The tool would undo every pause until told, once, to leave replicas to Napwake. See GitOps tools. |
| Once the tool is told | The workload isn’t listed |
| For Argo CD | The scan reads that from the Application and argocd-cm, as enable does |
| Without access to them | The note says so, and lists the workload to check |
| In JSON | The tool in replicasFromGit, gitOpsLeavesReplicas once told, and gitOpsUnchecked for why it couldn’t check |
| In the next steps | Never suggested, as the tool would revert a label put on by hand too |
| The cluster’s own infrastructure | Left out of the table, the totals and the next steps. A note counts it by namespace. |
| Infrastructure in JSON and YAML | Listed in platform, each with what it is |
| Left out | Found by |
|---|---|
| Kubernetes’ namespaces, never paused | kube-*, openshift-*, cattle-*, gke-* and gmp-* |
| Napwake’s namespace, never paused | Its Deployment’s app.kubernetes.io/name: napwake label |
| The operator’s and the doorman’s Deployments, never paused | control-plane: controller-manager or control-plane: doorman, in any namespace |
| The same, also needed | app.kubernetes.io/name: napwake, or a napwake helm.sh/chart label |
| What the scan guesses nobody means to pause | Well-known kinds, such as ingress controllers, cert-manager, Vault, Argo CD, Flux, service meshes, policy engines and monitoring |
| The same, by namespace | ingress-nginx, cert-manager, vault, argocd, flux-system, istio-system, linkerd, gatekeeper-system, kyverno, monitoring and others |
| The same, by label | app.kubernetes.io/name, app.kubernetes.io/part-of, app, k8s-app or helm.sh/chart |
Workloads labelled napwake.dev/ignore=true |
They aren’t listed at all |
Napwake never needs to wake what it never pauses, so apps’ dependencies on those aren’t listed. The guessed infrastructure is left out of the estimate only, and the operator has no such rule. Opted in, Napwake pauses it like any app, so dependencies on it are listed. There’s no guessing for a workload Napwake manages. Nor in a namespace labelled napwake.dev/managed=true, such as a preview with its own Vault. A namespace labelled napwake.dev/allow-protected=true is scanned like any other, apart from Napwake’s own Deployments.
History from Prometheus
Section titled “History from Prometheus”With a Prometheus in the cluster, the scan replays each workload’s last week of CPU and rollouts. It runs Napwake’s activity clock over them, as if managing it with --idle-after and --cpu-threshold.
| How it works | |
|---|---|
| Finding it | By the Services the Prometheus Operator (so kube-prometheus-stack) and the community Helm chart create |
| Reaching it | Through the API server’s service proxy, so there’s no port-forward or URL to give |
| Naming it | The History: line and the report’s subtitle (JSON history.prometheus) |
| How much was replayed | On the same line and subtitle (JSON history.hours) |
| Several | It reads the one with the most of the window’s CPU history, ranked as below |
| How many | It tries five at most. The History: line lists the others (JSON history.others). |
--prometheus-service namespace/name |
Reads that Service instead, found or not, through the API server |
| Its port | Its API port, or the one you add, as in monitoring/thanos-query:http |
--prometheus-url |
Reads one outside the cluster, such as Thanos or Mimir. See A Prometheus elsewhere. |
| Either, unreadable | The scan fails, rather than going on without history |
| CPU | Per container, from cAdvisor’s container_cpu_usage_seconds_total, which every common setup scrapes |
| Steps | Five minutes, coarser for windows over about 34 days |
| Pods | Matched to their workload by name |
| A Deployment’s pods | Those of deleted ReplicaSets too, named <deployment>-<hash>-<suffix> |
| A long Deployment name | If Kubernetes cut its pods’ names short, only current ReplicaSets’ pods are replayed |
| Sidecars | Those added at pod creation are left out of activity, as for the activity clock |
| An active step | A rollout, or the workload’s own CPU above the threshold of its running pods’ requests |
After --idle-after with none |
It sleeps until the next active step, which is one wake |
CPU within 15 minutes of a pause, or within --idle-after if that’s shorter |
Shows it was in use |
| Each such pause in a row | Doubles the next wait, up to --idle-after plus an hour, as Napwake does after a pause made in use |
| A pause that lasts past that window | Ends the longer wait |
| A rollout | Wakes it, without counting either way |
| What it can’t see | Requests and activity annotations, which aren’t in Prometheus. It sees only CPU and rollouts. |
| So | A workload used with little CPU can look like it would sleep more than it would |
| Requests and prices | Today’s |
The active-step threshold is as the operator counts it.
| Rank | Preferred |
|---|---|
| 1 | cAdvisor’s container CPU from the window’s start, then any CPU, then any that answers |
| 2 | A monitoring namespace, in order monitoring, prometheus, observability, kube-prometheus-stack, cattle-monitoring-system, openshift-monitoring |
| 3 | A usual name, in order prometheus-operated, kube-prometheus-stack-prometheus, prometheus-server, prometheus, prometheus-k8s |
| 4 | The Prometheus Operator’s Service over the community chart’s |
| 5 | The first by namespace and name |
| Time and short history | What happens |
|---|---|
| History’s share | The first 60% of --timeout, so a slow Prometheus or Thanos can’t use up the scan |
| A namespace’s reads | Its API reads come before its history query, sharing that time |
| Once it’s spent | A namespace reached after that isn’t queried |
| A slow query | It may take whatever is left, however long. --request-timeout doesn’t cut it short. |
| Not read in time | Judged from CPU right now. The notes name these namespaces. |
| The notes’ advice | A longer --timeout gives history more time, or --window 0 skips it |
| A query given up with time left | By Prometheus or the API server’s proxy. The notes name the namespaces, and suggest a shorter --window. |
| Less history than the window | The replay covers what there is, and says how much, such as “Replaying the last 30 minutes” |
| The same, in the notes | A note says it’s shorter than asked |
| Under a day | It can’t show a day’s pattern. The headline says so, in the terminal and the report. |
| The headline’s advice | Scan again after a few days |
| No Prometheus, or no permission to reach it | The scan judges from CPU right now, and says why |
A longer --timeout wouldn’t help with a query Prometheus gave up on by itself, which is why the advice differs.
A Prometheus elsewhere
Section titled “A Prometheus elsewhere”--prometheus-url reads a Prometheus API your machine can reach, rather than one in the cluster. It needs no cluster permission at all. If it doesn’t answer like a Prometheus API, the scan fails rather than going on without history.
| Store or need | What to do |
|---|---|
| A store of several clusters, such as Thanos or Mimir | Pass --prometheus-selector with the label that tells them apart |
| The notes’ hint | They suggest it when they see series from more than one source |
| Authentication | --prometheus-header, such as X-Scope-OrgID for a Mimir tenant, or Authorization |
| Or | --prometheus-bearer-token-file. Not both for one header. |
| Its certificate | --prometheus-ca-file and --prometheus-insecure-skip-verify |
| A Prometheus in the cluster | These flags don’t apply. It’s reached through the API server, with your kubeconfig. |
| Google Cloud Managed Service for Prometheus | GKE’s keeps its data in Cloud Monitoring, so the scan doesn’t find it |
| Its collectors | In gmp-system, with nothing to query. The scan reads another Prometheus if the cluster has one. |
| To read it | Give it an access token, and name your project and cluster, as below |
| Its frontend, deployed in the cluster | --prometheus-service NAMESPACE/frontend reads through that, with the frontend’s credentials |
| Either way | The replay’s CPU is there only if it collects cAdvisor’s metrics |
| Turning those on | kubeletScraping in its OperatorConfig |
| Amazon Managed Service for Prometheus | It needs SigV4 signing, which the scan doesn’t do. Point --prometheus-url at a signing proxy. |
Name the cluster too, since the store holds every cluster in the project.
kubectl napwake scan \ --prometheus-url https://monitoring.googleapis.com/v1/projects/PROJECT_ID/location/global/prometheus \ --prometheus-bearer-token-file <(gcloud auth print-access-token) \ --prometheus-selector 'cluster="CLUSTER_NAME"'The headline’s counts
Section titled “The headline’s counts”| Count | What it counts |
|---|---|
pausing 14 live workloads |
Only live workloads Napwake pauses |
| Its money | What they freed, and what any put back in dry-run freed while live |
| With none live | It says the money came from workloads since put back in dry-run, if there are some |
| Put back in dry-run | A workload keeps what it freed and saved while live |
| Would-be pauses | Never add to what’s freed or saved |
| Protected live workloads | A line of their own, with what they freed this month before protection, if anything |
0 of 13 unmanaged workloads would have slept |
Unmanaged workloads replayed |
Not counted: |
Listed workloads the replay count leaves out, and why, as the report’s summary does |
| Not counted, won’t pause | 1 that won't pause (PVCs deleted on scale-down) |
| Not counted, the others | Those in protected namespaces, scaled to zero by hand, or without CPU history |
The protected line reads like this. Its workloads are in a protected namespace, part of Napwake, or StatefulSets deleting PVCs on scale-down (Protected=True).
1 more live workload is protected, so Napwake never pauses it; it freed $12 this month before it was, and saved $5 of that.JSON gives totals.live and totals.liveProtected. It also gives totals.protectedFreedThisMonth and protectedSavedThisMonth, which are part of freedThisMonth and savedThisMonth.
Dependencies
Section titled “Dependencies”The scan lists the workloads each workload’s environment variables point at, literal or from ConfigMaps.
3 dependencies found, and what Napwake does with each:
preview-42 deployment/checkout-api -> statefulset/postgres DATABASE_URL declared; woken and held with it
preview-42 deployment/checkout-api -> messaging/statefulset/nats NATS_URL not managed, so never paused
preview-42 deployment/worker -> statefulset/postgres PGHOST woken with it once both are managed| How it reads them | |
|---|---|
| An address | It names a Service in a scanned namespace, in any form cluster DNS gives it |
| Its forms | postgres, postgres.preview-42, postgres.preview-42.svc.cluster.local, or a headless Service’s pod, postgres-0.postgres-hl |
| Where | In a URL, a host:port, or a list of either |
| The workload | The one the Service’s selector picks |
| Not an address | A bare word on its own, such as MODE=postgres |
| Names | Variable names don’t matter. Service names come from the cluster, not a list the scan keeps. |
| Shown | Only its scheme, host and port, such as postgres://postgres:5432 |
| Hidden | A user, password, path and query string, any of which could hold a credential |
| In the terminal | The variable it was found in. Only JSON and YAML carry the address. |
| Secrets | Never read, so addresses there aren’t found. The notes name workloads that take variables from Secrets. |
| Code and files | Addresses built in code or read from files aren’t found either |
When Napwake manages both, it wakes a dependency with the workload that needs it. It keeps it up while that workload is. So the first request doesn’t wait for it too. See Dependencies.
| Last column | When |
|---|---|
woken with it once both are managed |
Napwake doesn’t manage the workload yet |
not managed, so never paused |
Napwake doesn’t manage the dependency, which stays up anyway |
woken and held with it |
Learned from the workload’s environment, or a wake |
declared; woken and held with it |
Declared with napwake.dev/depends-on |
not learned yet |
Napwake manages it, but hasn’t learned it |
Before Napwake manages any workload that needs them, every dependency would say the first. So the scan and the report say it once above the list, and leave the column out.
Prices
Section titled “Prices”| Case | Price |
|---|---|
| Any workload | Its requests while running, sidecars included. See Prices. |
| The rate | The list price of each pod’s node, by instance type and region |
| No pod on a node | Where Napwake last saw it run, or the cluster’s most common node type |
| A node with no list price | Assumed prices, AWS on-demand in us-east-1. The report says which. |
| No access to list nodes | Every workload at those assumed prices |
--cpu-price and --memory-price |
Yours, in place of list prices, for discounts, savings plans or negotiated rates |
| Spot and preemptible nodes | Their type’s spot price. The report says how many workloads run on them. |
| A spot node of a type not sold as spot | On-demand. The report names those types, and says they cost less than shown. |
| In JSON, per workload | onSpot and spotAtOnDemand |
| In JSON, per node type | spot and atSpot, in nodePrices. See Spot nodes. |
| An hour asleep | Frees capacity. It’s money only when a node autoscaler removes it. |
kubectl napwake nodes shows what keeps nodes from scaling down.
The Prices: line at the end sums up what costs were priced at:
| Priced at | The line gives |
|---|---|
| List prices | Their provider and region, the node type or how many there are, and how many workloads are on spot, priced at spot |
| Your prices | yours |
| Assumed prices, when no node has a list price | assumed |
| Too mixed for that | Each in full |
| Too mixed, such as | Some nodes priced and others not, the nodes unread, or one price yours and the other assumed |
The web report
Section titled “The web report”It’s written for whoever you send it to.
| Part | What it shows |
|---|---|
| Top | The cluster drawn as a hotel at night, one room per workload up to 35. Past that, the mix is rounded to 35 rooms, with the exact counts beside it |
| On a scan | The idle workloads’ lit rooms go dark by themselves once, to show what Napwake would do. Nothing is paused |
| Then | Prices and Your bill, as the terminal’s lines say them |
| A share card | Your browser draws it, wide or square, with totals only. The cluster’s name is on it only if you tick it |
| Buttons | Copy summary, three plain lines without the cluster’s name or the Prometheus source unless you tick it, and Save as PDF |
| What it sends | Nothing, anywhere |
| Headline figures | Two, below |
| Fact one | How many workloads, in how many namespaces, with their cost once that’s left the headline |
| Fact two | What’s idle right now |
| Fact three, with history | How many unmanaged workloads would have slept, and listed ones the replay doesn’t count |
| Fact three, without | What idle workloads cost an hour |
| Fact four | How many Napwake manages |
| Tables | A table by namespace, with a filter |
| Workloads | Every one the scan could judge, in the terminal’s columns, sortable and with a filter |
| Then | The dependencies found |
| End | What the numbers mean, in plain words, and the next steps |
| Headline figures | First | Beside it |
|---|---|---|
| Before Napwake has paused anything | What pausing could free a month, as a share of what those workloads cost | What the workloads cost a month |
| Once it has | What it freed this month, with what of it was saved | What pausing could free, while there’s something to |
| A zero | Says why |
Before anything is paused, the first figure is measured for dry-run and estimated from history for unmanaged workloads. It says “over the same time” when history was replayed. Then it’s measured against what they cost over the history, with the pods they ran. Without either, it’s what’s idle now costs while running, the most pausing could free. A zero with under a day of history, short of the window, says that’s too little history yet. Scan again after a few days.
| The file | |
|---|---|
| Loads | No scripts, fonts or images. Sorting and filtering are built in. |
| Leaves out | Dependency addresses, as it’s meant to be passed around. JSON and YAML keep them. |
| Hides | Any password in the history source’s URL |
| Access | Readable only by you |
| Where | Your temporary directory, unless you pass --html FILE |
| Opens | Only when the table is shown in a terminal on a machine with a desktop |
| No report | Piped output, -o json, CI and SSH, unless you pass --html |
| SSH with a display | It opens when X forwarding gives a Linux machine a display |
--open=false |
Keeps it from opening |
JSON and YAML
Section titled “JSON and YAML”The JSON and YAML fields this page names, and what each holds.
| Field | What it holds |
|---|---|
#nodeAutoscaler |
What would turn freed capacity into savings, the Your bill: line |
#wontPause |
A StatefulSet whose persistentVolumeClaimRetentionPolicy.whenScaled is Delete. BECAUSE says won't pause: PVCs deleted on scale-down. |
#namespaces |
Every namespace read. The first line counts only the namespaces listed workloads are in. |
#history. |
The Prometheus read, named on the History: line and the report’s subtitle |
#history. |
How much was replayed |
#history. |
The others found, which the History: line lists |
#replicasFromGit |
The GitOps tool that last set the workload’s replicas |
#gitOpsLeavesReplicas |
Once the tool is told to leave replicas to Napwake |
#gitOpsUnchecked |
Why it couldn’t check |
#platform |
The cluster’s own infrastructure, each with what it is |
#totals. |
Live workloads Napwake pauses, as the headline counts them |
#totals. |
Protected live workloads, which the headline gives a line of their own |
#totals. |
What those freed this month before protection. Part of freedThisMonth. |
#totals. |
What of that was saved. Part of savedThisMonth. |
#onSpot |
Per workload, on spot or preemptible nodes, priced at its type’s spot price |
#spotAtOnDemand |
Per workload, on a spot node of a type not sold as spot, priced on-demand |
#nodePrices |
Per node type, with spot and atSpot. See Spot nodes. |
#incomplete |
The namespaces, by why, as missing, denied, failed or unscanned. See Exit status. |
#workloads |
[] when there are none |
Exit status
Section titled “Exit status”| Exit | When |
|---|---|
0 |
It read everything your access allows |
0, with a 403 |
A namespace or resource your access denies is explained in the notes |
1, after writing the report |
It missed something it should have read |
1, causes |
A timeout, throttling, a server error, or a failed Prometheus query |
1, named namespaces |
A 403 or missing namespace among those you named with -n |
1, no report |
Only a scan that ran out of --timeout having scanned none |
A 403 changes the exit status only for a namespace you named with -n. On 1, the notes say what was missed. The error says to check a namespace’s name, ask for access, or retry with a longer --timeout. Out of --timeout, the scan still reports the namespaces it scanned in time, and lists the rest as unscanned.
| JSON and YAML | The namespaces in incomplete, by why, as missing, denied, failed or unscanned |
| No workloads | JSON and YAML give "workloads": [] |
| Every namespace | It lists each kind cluster-wide, a page at a time |
| So | Thousands of namespaces take a few dozen requests |
| Listing allowed only per namespace | That kind is read namespace by namespace |
Permissions
Section titled “Permissions”| Your user needs | Where, or why |
|---|---|
list on Deployments, StatefulSets, ReplicaSets, ControllerRevisions, Pods and pod metrics |
The scanned namespaces |
list on Services and ManagedWorkloads |
The scanned namespaces |
| To read the namespaces, and the ConfigMaps workloads take variables from | The scanned namespaces |
list on namespaces |
Unless you name them with -n |
list on nodes |
To price workloads at their nodes |
What nodes reads |
To name the node autoscaler |
get on services/proxy for the Prometheus Service |
To read history through the service proxy |
get on a Service you name with --prometheus-service |
To find its port |
The standard view role covers the namespaced reads, ManagedWorkloads too, as Napwake’s chart and manifests add them to it. It doesn’t cover nodes, which are cluster-wide, or services/proxy. Without a read, the scan carries on, and the notes say what it couldn’t see. Without services/proxy, it judges from CPU right now. It prints what an admin can run for that one Service and nothing else.
To replay history, an admin can let you read Prometheus, and nothing else, with:
kubectl create role napwake-scan -n monitoring --verb=get --resource=services/proxy --resource-name=prometheus-operated:web
kubectl create rolebinding napwake-scan -n monitoring --role=napwake-scan --user=jane@example.com
Or pass --prometheus-url if Prometheus is reachable from your machine.A Prometheus you name with --prometheus-service fails the scan instead, with the same commands in its error. view covers its get. All this is only when the API server refuses you the proxy. A 403 from behind it, such as a kube-rbac-proxy in front of Prometheus, shows as Prometheus’s own answer. A Role for services/proxy wouldn’t change it.
forecast
Section titled “forecast”Its flags, with their defaults. Replay the forecast shows it in use.
| Flag | Default | What it does |
|---|---|---|
#--namespace -n |
all you can read | Namespace to replay. Repeat for several. |
#--all- -A |
false |
Every namespace, failing rather than falling back |
#--exclude- |
system namespaces | Namespaces to skip |
#--window |
28d |
History to read, from 2d to 34d. The operator learns up to four weeks. |
#--forecast |
balanced |
How soon the forecast acts for unmanaged workloads. See Forecast setting. |
#--timezone |
the operator’s | The zone whose hours and weekdays the forecast learns, and times show in |
#--cpu- |
10 |
CPU use, as a percentage of requests from 0.1 to 100, such as 10 or 0.5, above which a workload is in use |
#--idle- |
1h |
How long without use before a pause |
#--output -o |
table |
table, json or yaml. JSON and YAML give every workload’s days. |
#--timeout |
5m |
How long the replay may take before it gives up |
--prometheus-url and the other --prometheus-* flags |
As for scan |
--forecast is eager, balanced or cautious. --timezone defaults to the operator’s timezone, from status.prediction.timeZone, or UTC if you can read none. Managed workloads use their own --cpu-threshold and --idle-after.
History, and what would wake a workload
Section titled “History, and what would wake a workload”| Prometheus | Read as scan reads it, with the same flags and permissions |
| No history | It fails |
| Under two days | A day to learn and a day to replay. It says so, and when there will be enough. |
| The same, managed | It says how to see the workload’s own forecast, which needs no history |
| Left out | Scan’s notes on dependencies, GitOps tools and infrastructure, which are about what pausing could free |
| A managed workload’s routing | What its operator found at its last pause, including Services served by other workloads’ pods |
| An unmanaged workload’s routing | Judged by the ManagedWorkload that opting in would make |
Its napwake.dev/wake |
From its own annotations, then its namespace’s, then the operator’s --default-wake, as opting in does. A value that can’t be read asks |
| One that asks before waking | people in WAKES ON, where the doorman would route its Services, and otherwise nothing or query. Only people wake it in the replay, and the forecast learns its paused hours from whether they did. noRequest says the doorman asks before waking it, and only a person's click does |
doorman.enabled and the NEG forwarder |
From the flags of the operator managing the namespace |
| That operator | forecast finds its Deployment by labels, across the cluster |
| Without access to Deployments there | It looks in the operator’s namespace, napwake-system by default |
| Or | The namespace a ManagedWorkload’s napwake.dev/operator annotation names |
| No operator flags | It assumes the defaults, the doorman on, the forwarder off, and --default-wake=request |
| Then | Its notes say so, with why, and the access that reads it |
| In JSON | forecast.wakes, with request, query, people, and noRequest, why a request wouldn’t wake it |
Finding the workload, gaps, and exit status
Section titled “Finding the workload, gaps, and exit status”| Case | What it does |
|---|---|
| NAME | Looked for in every namespace forecast reads, or those given with -n |
| What it matches | A Deployment or StatefulSet, or a ManagedWorkload’s own name, for one written under another name |
| Several matches | Such as api in two namespaces. forecast names them, and asks for -n or kind/name. |
| No match | It says so |
No match, in a -n namespace |
It names a workload labelled napwake.dev/ignore=true, which the forecast leaves out, as Napwake does |
| No forecast recorded yet | It says so, and what to check |
| Napwake leaves it alone | It says that instead, as status does in NEXT (JSON operator.stopped) |
| Left alone means | TargetAvailable=False on its ManagedWorkload, a napwake.dev/ignore=true label, or gone |
| No replay | A line says why, and what to do |
| No replay, too little history | A day to learn and a day to replay, with when there will be enough |
| No replay, no Prometheus | None to read, or one it couldn’t |
| No replay, the workload | No CPU requests to measure use against, no CPU history of its pods, or scaled to zero outside Napwake |
| A managed workload | It shows its own forecast whatever its history, and exits 0 |
| An unmanaged one, nothing else to show | Exits 1 when there’s no Prometheus it can read |
| An unmanaged one, too little history | It says so, and exits 0 |
| Times | In the operator’s time zone, as the ManagedWorkload records it, unless --timezone says otherwise |
| JSON and YAML | Each workload’s operator, forecast and noReplay |
operator |
learned (hours, fromHistory, hoursOfDay, hoursOfWeek) and busy, each span’s from, to, peak |
| Access | Reading the ManagedWorkload takes list on managedworkloads, which view has |
| Without it | The workload shows as one Napwake doesn’t manage |
JSON and YAML
Section titled “JSON and YAML”The JSON and YAML fields this page names, and what each holds.
| Field | What it holds |
|---|---|
#operator |
learned (hours, fromHistory, hoursOfDay, hoursOfWeek) and busy, each span’s from, to, peak |
#operator. |
Napwake leaves it alone, as status says in NEXT |
#forecast |
Each workload’s replay |
#forecast. |
What would wake it, with request, query, people, and noRequest, why a request wouldn’t wake it |
#noReplay |
Why there’s no replay |
status
Section titled “status”Its flags, with their defaults. See what Napwake is doing shows it in use.
| Flag | Default | What it does |
|---|---|---|
#--namespace -n |
all you can read | Namespace to show. Repeat for several. |
#--all- -A |
Every namespace, failing rather than falling back. Not with -n. |
|
#--output -o |
table |
table, json or yaml |
#--since |
24h |
How far back to list pauses and wakes |
#--html |
Also write the status as a web page to this file, or without one, to a temporary file | |
#--open |
true |
Open the --html page, when run in a terminal |
#--timeout |
1m |
How long to wait for the cluster before giving up |
Needs attention
Section titled “Needs attention”| Needs attention shows | Shown as |
|---|---|
| A GitOps tool undoing pauses | GitOpsConflict |
| A dependency that’s missing, in a cycle, or in a namespace that doesn’t allow it | DependencyNotAllowed |
| Two ManagedWorkloads for one target | |
| A protected namespace | |
| A target that’s gone | |
| Metrics or Prometheus that can’t be read | |
| A Prometheus query that has never matched a series | PrometheusQueryEmpty |
| A workload whose requests can’t wake it while it’s paused | WakeOnRequest |
| A resume that stopped with replicas not Ready | ResumeIncomplete |
| A pause that stopped waiting with pods still running | PauseIncomplete |
| A pause made while it was in use, until a pause holds, with what to change, such as a CPU threshold | PausedWhileInUse (Paused while in use) |
| A pause or wake taking over 10 minutes, with what it waits for | Stuck |
For an awake workload, WakeOnRequest is what its last pause found with its Services, to fix before the next. Its being awake (NotPaused) isn’t shown, nor the doorman being turned off.
In -o json, each problem’s target names its workload as the table does, such as deployment/worker.
The LAST ACTIVITY column
Section titled “The LAST ACTIVITY column”| LAST ACTIVITY, none since its clock started | What started the clock |
|---|---|
none since Napwake began managing it 3m ago |
Napwake taking it on |
none since it woke 3m ago |
A wake |
unobserved until 3m ago |
A gap in watching |
The NEXT column
Section titled “The NEXT column”| NEXT | What it means |
|---|---|
pauses in 45m |
When it pauses |
pauses now |
It’s Idle, with or without an idlePolicy, as after a pause request |
would pause … |
The same, in dry-run |
pauses now, early: the forecast expects quiet until Fri 20:00 UTC |
The forecast brought the pause forward |
expected, for expects |
That time has passed, as for a dry-run would-be pause, which stays Idle until activity |
pauses at 14:20 UTC (after GitOps conflict) |
A GitOps tool undid its last pause, so Napwake waits an hour. See GitOps tools. |
pauses soon |
Still running up to 7 minutes past the time shown, as status is written every few minutes |
overdue to pause |
After that. Something the table can’t see holds it. |
held awake while preview-7/worker uses it |
Not yet. Its dependents hold it, until they pause. |
held awake for 2h (active-until) |
Not yet. napwake.dev/active-until, on the ManagedWorkload or the workload, as wake --for sets. |
held awake by the forecast: busy from 13:00 UTC |
Not yet. The forecast expects demand that hour. |
never pauses: no idlePolicy, never pauses: namespace protected |
Never |
never pauses: part of Napwake |
Never. It’s the operator’s or the doorman’s own Deployment. |
never pauses: PVCs deleted on scale-down |
Never. A StatefulSet with persistentVolumeClaimRetentionPolicy.whenScaled: Delete. |
nothing: labelled napwake.dev/ignore, nothing: workload not found, nothing: another ManagedWorkload has it |
Never |
won't pause: dependsOn cycle, won't pause: no CPU metrics, won't pause: Prometheus failing |
Never |
wakes on a request or activity |
Paused, and what wakes it. Without the doorman, wakes on activity. |
wakes on a click, a wake request or activity |
Paused, and its doorman asks before waking it |
paused early: the forecast expected quiet until Fri 20:00 UTC; wakes on a request or activity |
The forecast brought the pause forward, before idleAfter ran out |
For overdue to pause, kubectl describe managedworkload says what holds it.
The FORECAST column
Section titled “The FORECAST column”| FORECAST | What it means |
|---|---|
acting |
Its forecast acts |
learning, with acts from Oct 8 |
When the operator expects it to start, if the coming days repeat the last week it saw |
relearning |
Demand broke from what it had learned |
not learning while paused: and why |
Paused, with neither the doorman nor a Prometheus activity query to see its demand |
| Such as | no Service routes requests to it. status.prediction.message says how to fix it. |
- |
The operator hasn’t recorded one yet |
| In JSON | The reason, from status.prediction.reason, in forecast, and forecastStartsActing |
Turned away while asleep
Section titled “Turned away while asleep”Under the table, when a workload whose doorman asks before waking it turned requests away during its current or last pause:
Turned away while asleep, by a doorman that asks before waking: something automated may call these. preview-42/web 312 requests during the pause from 3h agoA person shown the asleep page isn’t counted. So many mean something automated calls it, such as a monitor, a webhook or another service. -o json has them as turnedAway, from the ManagedWorkload’s status.turnedAway (What it turns away).
Recent pauses and wakes
Section titled “Recent pauses and wakes”| Recent pauses and wakes | |
|---|---|
| A wake’s cause | A request on a Service, an activity annotation, a wake request, the forecast, or a scale-up from outside Napwake |
| Activity annotations | Including a dependent waking |
| Wake requests | From kubectl napwake wake, or a tool setting napwake.dev/wake-requested, by their WakeRequested event |
| Requests the doorman closed without reaching the workload | Listed too |
| A wake with no recorded cause | Listed by its resumed event alone, such as a hand-back when a namespace becomes protected |
| How many | The table shows the latest 10, and -o json every one |
Each one’s kind, in -o json |
pause, wake or notServed |
wokenBy, in -o json |
On each wake. Each workload carries it too, for its latest wake until it pauses again |
wokenBy left out |
When the cause wasn’t recorded, and once the events have expired |
| A ManagedWorkload deleted and made again under the same name | Starts without one, since the old one’s events name its UID, not the new one’s |
| How far back | The API server keeps events for an hour, unless its --event-ttl says otherwise |
So on most clusters, an hour is what status can show.
wokenBy |
What woke it |
|---|---|
request |
A request on one of its Services, through the doorman, including a person’s click on one that asks first |
activity |
An activity annotation, a dependent waking, or a Prometheus activity query |
wakeRequest |
A wake request, such as kubectl napwake wake |
forecast |
autoResume, ahead of the demand the forecast expects |
outside |
A scale-up from outside Napwake |
pauseCancelled |
Whatever came as it paused, which the event’s message names |
JSON and YAML
Section titled “JSON and YAML”The JSON and YAML fields this page names, and what each holds.
| Field | What it holds |
|---|---|
#forecast |
The reason, from status.prediction.reason |
#forecastStartsActing |
When the operator expects the forecast to start acting, as acts from shows |
#turnedAway |
For a workload that asks, pausedAt and requests, what the doorman turned away during that pause, from status.turnedAway |
#wokenBy |
What woke it, on each wake, and on each workload for its latest wake until it pauses again (the values) |
#kind |
Each pause or wake’s kind, pause, wake or notServed |
#target |
Each problem’s workload, as the table names it, such as deployment/worker |
Permissions
Section titled “Permissions”| Your user needs | Where, or why | Without it |
|---|---|---|
list on managedworkloads and events |
Every namespace, or the -n ones |
Without events, it shows the rest, and says so |
| Karpenter’s NodePools, and nodes’ metadata | The node autoscaler | The autoscaler is unknown |
In kube-system, the cluster-autoscaler-status ConfigMap and Deployments’ metadata |
The node autoscaler | The autoscaler is unknown |
list on deployments and statefulsets, metadata only |
napwake.dev/active-until on the workload itself |
It goes without |
get on namespaces |
Telling a missing -n namespace, which is an error |
It shows as one Napwake manages nothing in |
The autoscaler is unknown when there isn’t enough to tell.
Its flags, with their defaults. See why nodes don’t scale down shows it in use.
| Flag | Default | What it does |
|---|---|---|
#--output -o |
table |
table, json or yaml |
#--timeout |
1m |
How long to wait for the cluster before giving up |
The BLOCKED BY column
Section titled “The BLOCKED BY column”| On a node | Blocked by |
|---|---|
| A Karpenter NodePool owns | The node or a pod annotated karpenter.sh/do-not-disrupt: "true" |
| The same | A pod whose PodDisruptionBudget allows no disruptions (status.disruptionsAllowed is 0) |
| The same | A NodePool with consolidationPolicy: WhenEmpty, and anything but DaemonSet pods on the node |
| The same | consolidateAfter: Never |
| Any other, by Cluster Autoscaler’s rules | The node annotated cluster-autoscaler.kubernetes.io/scale-down-disabled: "true" |
| The same | A pod annotated cluster-autoscaler.kubernetes.io/safe-to-evict: "false" |
| The same | A pod with an emptyDir not in memory, or a hostPath volume |
| The same, unless | It’s named in cluster-autoscaler.kubernetes.io/safe-to-evict-local-volumes |
| The same | A pod no controller owns |
| The same | A kube-system pod without a PodDisruptionBudget |
| The same | The PodDisruptionBudget rule |
| Any | DaemonSet and static pods never block |
| Any | A pod annotated safe-to-evict: "true" blocks only by its PodDisruptionBudget, as with Cluster Autoscaler |
Local storage and kube-system pods |
Block only while --skip-nodes-with-local-storage and --skip-nodes-with-system-pods are on |
| Those flags | On by default upstream. AKS turns the first off. |
| Not Karpenter’s, autoscaler unknown | Cluster Autoscaler’s rules |
| No autoscaler found scales it | not autoscaled |
| A Cluster Autoscaler node whose node group is at its minimum size | node group at its minimum. It stays, whatever runs on it, so no step would free it. Above the next steps, a line names its group and minimum instead of steps |
| Where the sizes come from | Cluster Autoscaler’s cluster-autoscaler-status ConfigMap, in the YAML it writes since 1.30 or the text before, GKE’s included |
| Which group a node is in | Matched as GKE, EKS managed node groups and AKS name them. Karpenter keeps no minimum |
| Without access to the ConfigMap | A note says nodes at their minimum aren’t told apart |
How it finds the node autoscaler
Section titled “How it finds the node autoscaler”| It shows | When it finds |
|---|---|
| Karpenter | The nodepools.karpenter.sh CRD, or nodes labelled karpenter.sh/nodepool |
| Karpenter’s detail | Each NodePool’s spec.disruption.consolidationPolicy and consolidateAfter |
| EKS Auto Mode | Nodes labelled eks.amazonaws.com/compute-type: auto. Its NodePools are Karpenter’s, read the same way. |
| Cluster Autoscaler | The cluster-autoscaler-status ConfigMap it writes in kube-system |
| Which | Self-managed, or run for you by GKE Standard and AKS |
| Its detail | The cloud its nodes are on |
| Without the ConfigMap | A kube-system Deployment ending in cluster-autoscaler is named, as a hint it may not be running |
| GKE Autopilot | That ConfigMap, and nodes all named gk3-, as Autopilot names them. It scales nodes itself. |
none found |
Nothing, with every place read |
| Then | Freed capacity won’t come off the bill until something removes nodes |
unknown |
Your access didn’t allow reading what would tell, which it names |
| Never | It never says none when it couldn’t look |
Permissions
Section titled “Permissions”| Your user needs | Without it |
|---|---|
list on nodes |
There’s no table |
list on pods and poddisruptionbudgets in every namespace |
What each node runs, and the reasons from its pods, are left out |
list on nodepools.karpenter.sh |
|
get on the cluster-autoscaler-status ConfigMap, and list on deployments, in kube-system |
|
list on managedworkloads |
It reads what it can, and the notes say what it couldn’t.
Its flags, with their defaults. Pause a workload shows it in use.
| Flag | Default | What it does |
|---|---|---|
#--namespace -n |
your kubeconfig context’s | Namespace of the workload |
#--wait |
true |
Wait until it’s Paused, or Napwake says why not |
#--yes -y |
false |
Pause even if the forecast expects it busy within the hour, without asking |
#--clear- |
false |
Clear a napwake.dev/active-until hold, on its ManagedWorkload or the workload, without asking |
#--timeout |
5m |
How long to wait, cluster calls included. It starts again after you answer a question. |
On the ManagedWorkload, wake --for sets that hold.
With wake: ask, its answer says a person’s click on the asleep page, or kubectl napwake wake, wakes it, not a request. On a ManagedWorkload Napwake hasn’t taken on yet, such as one created a moment ago, pause waits until Napwake has. A request already on it then isn’t acted on.
When the forecast expects it to be busy
Section titled “When the forecast expects it to be busy”When a confident forecast expects the workload busy within the hour, Napwake declines the pause and says when. It would likely be woken straight back, so pause asks.
preview-42/api wasn't paused: the forecast expects demand at 80% of requests in the hour from 09:00 UTC
Pause it anyway? [y/N]| Answer or case | What happens |
|---|---|
y |
It asks again, overriding the forecast, and it pauses |
| Anything else | It stays running, and pause exits 0 |
| Taking your time | The question waits as long as you take |
--timeout |
Counts only time on the cluster, and starts again after your answer |
--yes |
Overrides the forecast from the start, without asking |
| No terminal, as in a script | pause exits 1 with the reason, and says to pass --yes |
When something holds it awake
Section titled “When something holds it awake”A napwake.dev/active-until annotation holds it awake, on the ManagedWorkload, as wake --for sets it, or on the workload. Napwake would refuse the pause, so pause asks first, naming which holds it.
preview-42/api wasn't paused: the napwake.dev/active-until annotation on its ManagedWorkload keeps it awake until 2026-10-17T20:00:00Z
Clear it and pause now? [y/N]| Answer or case | What happens |
|---|---|
y |
It removes the annotation, from both if both hold it, and pauses |
| Anything else | It stays running, and pause exits 0 |
--clear-hold |
Clears it without asking |
No terminal, without --clear-hold |
pause exits 1, and says to pass it |
--yes |
Doesn’t clear a hold |
So a nightly script that pauses with --yes doesn’t end a hold someone set for a demo.
When a GitOps tool sets its replicas
Section titled “When a GitOps tool sets its replicas”A GitOps tool may set the replicas from Git, without being told to leave them to Napwake. Then pause warns before asking, with the one-time fix. GitOps tools says how it tells.
Warning: Argo CD application argocd/shop sets preview-42/api's replicas from Git, as argocd-controller, so its next sync will likely scale it back up, undoing this pause. To have Argo CD leave replicas to Napwake, once:
Add RespectIgnoreDifferences=true to the syncPolicy.syncOptions of Argo CD application argocd/shop, so syncs leave the replicas it ignores.
See https://docs.napwake.dev/guides/gitops/#argo-cdWhen it can’t tell, such as without access to Argo CD’s namespace, the warning says why. It says to make the fix if you haven’t.
Permissions
Section titled “Permissions”| Your user needs | For | Without it |
|---|---|---|
get, list and patch on managedworkloads in the namespace |
The pause | |
get on the workload |
The GitOps warning, and the workload’s own hold | They’re left out |
patch on the workload |
Clearing a hold on it | |
get on the argocd-cm ConfigMap |
Telling whether Argo CD leaves its replicas | The warning says it couldn’t check |
list on applications.argoproj.io in every namespace, or get in argocd |
The same | The same |
The Argo CD reads are only for a workload Argo CD applies.
Its flags, with their defaults. Wake a workload shows it in use.
| Flag | Default | What it does |
|---|---|---|
#--namespace -n |
your kubeconfig context’s | Namespace of the workload |
#--for |
Also keep it awake this long, by setting napwake.dev/active-until on its ManagedWorkload |
|
#--wait |
true |
Wait for Napwake’s answer to the request, then until it’s Running |
#--timeout |
5m |
How long to wait, cluster calls included |
With --for, a hold on the ManagedWorkload that ends later stays, and wake says so.
wake sets a fresh napwake.dev/wake-requested value on the ManagedWorkload, as any tool can. So it wakes the workload whatever its wake setting.
The WakeRequest condition says what came of it (Wake now). Another tool asking meanwhile replaces wake’s token with its own, and Napwake’s answer to that one stands for both.
| When | wake |
|---|---|
| Napwake refuses the request, such as for a protected workload | Fails at once with Napwake’s reason, without waiting out --timeout |
Napwake doesn’t answer by --timeout |
Fails with its phase, saying Napwake hasn’t taken the request up. That usually means its operator isn’t running |
| The ManagedWorkload is deleted meanwhile | Fails, saying Napwake handed the workload back as it let go |
| A slow start | --timeout covers the answer and Running. A request made as the workload pauses is answered at once, and calls the pause off. One made as it wakes is answered only once the wake finishes. For a start over 5 minutes, raise it, such as --timeout 10m |
Permissions
Section titled “Permissions”Your user needs get, list and patch on managedworkloads in the namespace.
Its flags, with their defaults. Show a workload’s dependencies shows it in use.
| Flag | Default | What it does |
|---|---|---|
#--namespace -n |
your kubeconfig context’s | Namespace of the workload |
#--timeout |
1m |
How long to wait for the cluster before giving up |
Permissions
Section titled “Permissions”It only reads ManagedWorkloads. Without access to them in every namespace, it reads the workload’s own namespace, and says dependents elsewhere aren’t shown. A dependency in another namespace then shows as unknown (can't read <namespace>).
enable
Section titled “enable”Its flags, with their defaults. Enable a workload shows it in use.
| Flag | Default | What it does |
|---|---|---|
#--namespace -n |
your kubeconfig context’s | Namespace of the workload |
#--all |
false |
Enable every workload in the namespace |
#--force |
false |
Change the cluster even when a GitOps tool applies the setting from Git |
#--timeout |
1m |
How long to wait for the cluster before giving up |
When it changes nothing
Section titled “When it changes nothing”enable only ends dry-run for a workload Napwake manages. Otherwise it changes nothing, and exits non-zero with the reason.
| Reason | What it means |
|---|---|
| The namespace is protected | Labelled napwake.dev/protected=true, or a kube-* namespace |
| Unless | It’s labelled napwake.dev/allow-protected=true |
| The workload is left out | Labelled napwake.dev/ignore=true |
| Or opted out | Its own napwake.dev/managed is anything but "true", in a managed namespace |
| No ManagedWorkload manages it | The operator isn’t running, doesn’t watch the namespace, or protects it by its own settings |
| Right after you label it | Its ManagedWorkload may not exist yet. Run enable again a few seconds later. |
| Its ManagedWorkload says Napwake never pauses it | Protected=True, a target labelled napwake.dev/ignore, or DuplicateTarget=True |
Protected=True |
Such as by a protectedNamespaces pattern, or for Napwake’s own operator and doorman |
| No ManagedWorkload CRD | It says Napwake isn’t installed |
With --all
Section titled “With --all”--all ends dry-run for every workload in the namespace.
| When | What it does |
|---|---|
The namespace’s own napwake.dev/dry-run, or the cluster default, keeps any in dry-run |
Sets the namespace’s annotation to "false" |
| The same | Drops the workloads’ own annotations, so the namespace decides for all |
| Each is in dry-run by its own annotation | Leaves the namespace alone, and sets each workload’s to "false" |
| Either way | Sets spec.dryRun: false on ManagedWorkloads written by hand in the namespace |
| Git owns the namespace | Leaves its annotation for you to change in Git, ending dry-run for workloads that take it |
| The same | Workloads with their own annotation get "false" |
You can’t change the namespace, as with edit or admin bound in it |
Leaves its annotation, and says so |
| The same | Sets every workload’s own to "false", which wins over it |
| The same, with only the namespace’s to change | Exits non-zero |
| Labelled workloads no ManagedWorkload manages | Left alone |
The same, after enable set the namespace to "false" |
Their dry-run ends once Napwake picks them up, unless they have their own annotation |
| The rest of those | Listed, and enable exits non-zero |
| None of the namespace’s workloads picked up yet | Changes nothing, the namespace’s annotation included, and exits non-zero |
| Why | The operator may not be running |
| Workloads Napwake never pauses, by their ManagedWorkload | Left alone, and listed too |
When a GitOps tool or Helm applies the setting
Section titled “When a GitOps tool or Helm applies the setting”A GitOps tool would put a changed setting straight back, so enable changes nothing it applies. The tool is Argo CD, Flux, Fleet, kapp or Kluctl. The setting is the napwake.dev/dry-run annotation on the workload or its namespace, or spec.dryRun. enable names the tool, prints the change to make in Git, and exits non-zero.
deployment/checkout-api is managed by Argo CD application shop, which would undo a change made here. In its manifest:
set the annotation napwake.dev/dry-run: "false"
Or pass --force to change the cluster anyway.| How it tells | From the field managers the API server records, as pause does for replicas |
| So | How the tool tracks what it applies doesn’t matter |
| With none recorded | By the tracking label or annotation Argo CD or Flux adds |
| The tool applies the workload, not the annotation | Such as when Git doesn’t have it. The tool leaves a change to it alone. |
| Set by Helm | It changes it, since Helm only applies a chart when you run it |
| Then | It warns that the next helm upgrade puts the chart’s value back, so set it in the chart too |
| Replicas set from Git | It warns as pause does, with the one-time fix, unless the tool is told |
| When it warns | Whether it ends dry-run, finds it ended already, or leaves the change to Git |
With --all |
Workloads a tool treats alike, such as one Argo CD application’s, share one warning |
Ending dry-run lets Napwake pause the workload. The tool then scales it back up on the next sync after each pause. That’s why it warns. See Ending dry-run.
Permissions
Section titled “Permissions”| Your user needs | Without it |
|---|---|
get and patch on the workload, and get on its namespace |
|
list on managedworkloads in the namespace |
It goes by the annotations alone, and says so |
With --all, list on Deployments and StatefulSets |
|
With --all, patch on the namespace, to end its dry-run there |
Each workload gets its own annotation instead |
For the GitOps warning, what pause reads of Argo CD |
Going by the annotations alone, enable can’t check that Napwake manages the workload. Nor can it see a cluster-default dry-run.