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
Run at least two replicas and spread them across nodes with a preferred pod anti-affinity or a topology spread constraint . Add a PodDisruptionBudget so a node drain evicts one replica at a time.
Add a Horizontal Pod Autoscaler only if the load varies. Keep minReplicas at two and turn on the cluster autoscaler , so the new pods have nodes to run on.
Placement
Give each node pool a role label , then use a node selector 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
Set CPU and memory requests 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 security context 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. Enforce the restricted standard 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 network policy 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 ResourceQuota and a LimitRange so one component cannot use up the cluster resources.
Access control
Give people access through OIDC groups, not shared kubeconfig files, and bind each group to a role scoped to its namespaces .
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
Keep secrets out of Git 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
Terminate TLS at your ingress with a certificate from cert-manager , so renewal is automatic. Inside the cluster, enable TLS and authentication on data stores such as Redis and PostgreSQL.
Backups and restore
Back up any data 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 restore to any point in time . 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 Alertmanager . Enable readiness and liveness probes .
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 database run by an operator .
Versions and values
Pin the chart version and the image tag , 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 Argo CD 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 Kustomize , 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.
Deploy your first app
Deploy a sample application and expose it with a Hetzner Load Balancer using kubectl. See the external IP appear and access your app from the internet.
Survive node drains
The one page that wires every setting a real Deployment needs to survive a node drain, then narrates a single drain to show each one earning its place.