Skip to main content

Production readiness checklist

You can install a component with its default values using a single Helm command, but that does not make it production ready. The defaults often give you a single replica, no resource requests, and no PodDisruptionBudget. Before the component handles real traffic or data, apply the practices below.

Not every practice fits every component. A stateless controller has nothing to back up, and a component with steady load needs no autoscaler.

High availability 

and spread them across nodes with a or a . so a node drain evicts one replica at a time.

Add a only if the load varies. Keep minReplicas at two and turn on the , so the new pods have nodes to run on.

Placement 

, then use a so each component runs only on the nodes meant for it. Run databases on dedicated nodes, so a busy application cannot slow them down.

Resource requests and limits 

on every container, so the scheduler and the cluster autoscaler can plan capacity. Set a memory limit equal to the request. Do not set a CPU limit, to avoid CPU throttling when the node has CPU to spare.

Pod security 

Set a on every container: run as non-root, use the RuntimeDefault seccomp profile, block privilege escalation, drop all capabilities, and make the root filesystem read-only.

The platform sets no default Pod Security level. on each component's namespace. Some components, such as a log collector, need access to the node itself. Run those in a separate namespace with a less strict level.

Network policies 

Pod-to-pod traffic is open until a selects a pod. For each component, allow only the incoming and outgoing traffic it needs. Everything else is then blocked.

Use a CiliumNetworkPolicy when you need explicit deny rules.

Namespace separation 

Install each component in its own namespace, owned by the team that runs it. RBAC, network policies, quotas, and Pod Security all apply per namespace, so a mistake stays contained.

Add a so one component cannot use up the cluster resources.

Access control 

Give people access through groups, not shared kubeconfig files, and bind each group to a .

Separate who can manage a component from who can read its secrets. For example, the built-in view role shows Deployments, pods, and logs, but not Secrets.

Secrets 

and values files. Store them in an external secret store and sync them into the cluster, for example with External Secrets Operator.

Mount secrets as files, not environment variables. Environment variables are inherited by child processes, can end up in crash logs, and do not update automatically when a secret rotates.

Exposure and TLS 

with a certificate from , so renewal is automatic. Inside the cluster, enable TLS and authentication on data stores such as Redis and PostgreSQL.

Backups and restore 

the component stores that is not in Git. Run the backups on a schedule, store them outside the cluster, and decide how long to keep them. For a database, also archive its write-ahead log, so you can . Test the restore steps before you need them.

Monitoring 

Enable the component's metrics and scrape them, usually through a ServiceMonitor the chart creates. Add alert rules for the failures that matter and route them through . Enable readiness and liveness .

Minimal install 

A chart often installs extra parts you may not need, such as a web UI or a notification service. Turn off the ones you do not use: each one is one more thing to update and secure.

Some charts also include their own Redis or PostgreSQL, running as a single pod. If that pod fails, the component fails with it. Turn it off and connect the component to a highly available instance instead, such as a .

Versions and values 

, so an upgrade never pulls something untested. Keep only your overrides in the values file, each with a short comment on why.

Day 2 operations with GitOps 

Day 2 covers everything after the install: upgrades, configuration changes, and rollbacks. Store every change in Git and let or Flux⁠ apply it to the cluster instead of installing or upgrading manually.

Render the chart to plain manifests with helm template, or with kustomize build --enable-helm if you use , and commit the result. Argo CD or Flux then applies those files, and every upgrade shows up in the pull request as a diff of exactly what will change.