Concepts
Architecture
Napwake runs in your cluster as one binary, in two Deployments. One is the operator, the other the doorman.
flowchart TB
subgraph operator["Operator Deployment"]
optin["Opt-in reconcilers<br/>Deployments, StatefulSets"]
mwr["ManagedWorkload reconciler<br/>activity clock, pause and resume,<br/>dependencies, cost"]
forecast["Forecast engine<br/>one per workload"]
optin --> mwr
mwr --- forecast
end
subgraph doorman["Doorman Deployment"]
door["Holds requests to paused workloads,<br/>wakes them, passes requests through"]
end
clients["Clients, ingress controllers,<br/>load balancers"] -- "Service of a paused workload" --> door
door -- "napwake.dev/last-request" --> api
mwr --> api["Kubernetes API server"]
mwr --> ms["metrics-server"]
mwr -.-> prom["Prometheus<br/>optional"]Napwake is a Kubernetes operator built on controller-runtime. The doorman wakes paused workloads on request.
Reconcilers
Section titled “Reconcilers”The operator runs two kinds of reconciliation loop.
ManagedWorkload reconciler
Section titled “ManagedWorkload reconciler”The main one. It watches ManagedWorkload CRs and drives each workload through its lifecycle.
- A reconcile times out after 2 minutes.
--max-concurrent-reconcilesworkloads are reconciled at once, 8 by default. One slow metrics or Prometheus query doesn’t hold up another workload’s wake.- Each workload’s forecast engine runs in memory. It’s saved to
status.prediction.stateeach hour it learns, so it survives operator restarts and leader changes. - Reconciles wait in one queue, so a wake isn’t queued behind every workload’s checks:
| Order | Reconciles |
|---|---|
| First | What wakes a workload. A request the doorman holds, an activity or pause annotation, a scale-up from zero |
| Also first | A resume under way |
| Then | Other changes |
| Last | Periodic checks of awake and paused workloads, with their metrics and Prometheus queries |
One reconcile, step by step
- Checks no other ManagedWorkload manages the target.
- Checks the target exists and isn’t labelled
napwake.dev/ignore. Lets an ignored one go. - Lets a protected one go. Both scale a paused one back up first.
- Wakes a paused workload scaled up outside Napwake.
- Reports the HPA or KEDA ScaledObject scaling it.
- Learns its dependencies from its environment and from requests that woke it.
- Routes a paused workload’s Services to the doorman, or stops routing them.
- Finishes a pause or resume an earlier reconcile started, from the target’s
napwake.dev/paused-fromif status lost it. Wakes a paused workload put in dry-run. - Leaves alone a target scaled to zero outside Napwake.
- Acts on
kubectl napwake pause. - Runs the activity clock and the forecast, to pause the workload or wake it.
- Adds up what dry-run held back, accumulates cost, and writes status.
Opt-in reconcilers
Section titled “Opt-in reconcilers”One for Deployments, one for StatefulSets. Each watches its workloads, their namespaces, and the ManagedWorkloads created from them. It turns the napwake.dev/managed: "true" label into a ManagedWorkload, as Opting in describes.
Internal packages
Section titled “Internal packages”| Package | Does |
|---|---|
internal/controller |
The reconcilers |
internal/forecast |
Holt-Winters model, phase lifecycle, confidence scoring, anomaly detection |
internal/signal |
Prometheus activity queries |
internal/autoscaler |
Finds HPAs and KEDA ScaledObjects. Holds a KEDA one at zero while paused |
internal/gitops |
Tells from field managers whether a GitOps tool sets the replicas, and how to tell it to leave them |
internal/lifecycle |
Pause and resume, through the scale subresource |
internal/discovery |
kubectl napwake scan |
internal/doorman |
The doorman |
internal/cost |
Rates, node list prices, monthly projection |
internal/metrics |
Metric definitions, and the K8s Metrics API reader |
External dependencies
Section titled “External dependencies”| System | Napwake uses it to |
|---|---|
| Kubernetes API server | Scale workloads, and route Services’ EndpointSlices to the doorman |
| Kubernetes API server, for pricing and dependencies | Read pods, PVCs, nodes’ labels, ConfigMaps, HPAs and KEDA ScaledObjects |
| metrics-server, required | Read pod CPU |
| Prometheus, optional | Run PromQL activity queries |
Freed and saved
Section titled “Freed and saved”Napwake works at the workload layer, and doesn’t manage nodes. What a pause or scale-down frees comes off the bill once a cluster autoscaler removes nodes. Every 5 minutes, the leader measures what’s saved, from cached nodes and pods. Every 10 minutes, it exports which node autoscaler runs as napwake_node_autoscaler.
Leader election
Section titled “Leader election”For high availability, run several operator replicas with --leader-elect, controller-runtime’s leader election. Only the leader reconciles, and a standby takes over if it fails. Every doorman replica serves traffic, with no leader.
Security model
Section titled “Security model”- The operator runs as non-root, in a distroless container.
- RBAC is scoped to the minimum. See Data and access.
- Metrics are served over HTTPS, with authentication, by default.
- HTTP/2 is off by default, to mitigate known vulnerabilities.