Bootstrapping Kubernetes namespaces with Kyverno
If you manage a Kubernetes cluster with multiple applications or tenants, you likely understand the repetitive task of configuring each namespace with the appropriate defaults. This includes setting up network isolation, image pull credentials, and custom certificates—essential but monotonous tasks. Kyverno is not only a policy enforcement engine but also a tool to automate the clean and declarative bootstrapping of these resources.

What is Kyverno?
Kyverno is a Kubernetes-native policy engine designed for security and governance. It's often compared to Open Policy Agent (OPA), but unlike OPA, Kyverno uses YAML and understands Kubernetes resources out of the box. While OPA requires learning Rego, a purpose-built policy language, Kyverno works directly with familiar Kubernetes manifests.
Most people primarily use it to enforce cluster-wide or namespace-level rules, such as blocking privileged containers or enforcing label conventions.
Why bootstrap namespaces?
In most clusters, each namespace needs to be initialized with a few baseline resources: network policies to enforce isolation, image pull secrets for private registries, shared config like certificate authorities, and resource quotas to prevent overuse. Doing this by hand—or wiring it into every Helm chart—is error-prone and hard to maintain consistently.
Kyverno solves this by making the defaults automatic, versioned, and self-healing through declarative policies.
Kyverno as a bootstrapper
Beyond enforcement, Kyverno is also great for bootstrapping namespaces with sensible defaults. It doesn't just create resources—it keeps them in sync with a source template. That means Kyverno can apply updates to your standard NetworkPolicy, default certificate bundles, or other shared configs.
At the core of this is Kyverno's generate
rule. It can create resources when a namespace is created or if a resource is missing. If someone changes the resource later, Kyverno puts it back in line. Combined with match and exclude rules, you can automate the setup of secrets, configmaps, policies, and more.
⚠️ Be cautious when using
synchronize: true
—Kyverno will enforce the generated resource to match the source, which can overwrite manual changes in downstream namespaces.
Let's look at four real-world examples:
1. Enforce network isolation with a default NetworkPolicy
Every namespace should have a baseline NetworkPolicy
that isolates traffic and only allows DNS resolution via kube-dns
. This ensures that applications start isolated, and teams must explicitly declare exceptions.
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: default-deny
spec:
rules:
- name: add-default-network-policy
match:
resources:
kinds:
- Namespace
generate:
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
name: default-deny
namespace: ""
synchronize: true
generateExisting: true
data:
metadata:
name: default-deny
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
ingress:
- to:
- podSelector: {} # allow traffic to any pod in same namespace
egress:
- to:
- podSelector: {} # allow traffic to any pod in same namespace
- to:
- namespaceSelector: # allow traffic to kube-dns in kube-system
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
This rule secures every new namespace, starting with tight ingress & egress controls. When applied, you'll see a default-deny
policy in each namespace, allowing only DNS traffic to kube-dns
in the kube-system
namespace:
$ kubectl create ns test-namespace
namespace/test-namespace created
$ kubectl get networkpolicy -n test-namespace
NAME POD-SELECTOR AGE
default-deny <none> 3s
2. Automatically generate image pull secrets
If you use a private container registry, every application namespace likely needs credentials to pull images. Instead of copying secrets manually or relying on manual steps in Helm charts, inject them using Kyverno.
Kyverno assumes there's an image-pull-secret
in the kyverno
namespace, you can create this secret with:
$ kubectl create secret docker-registry image-pull-secret -n kyverno --docker-username=foo --docker-password=password
By default, Kyverno isn't allowed to interact with secrets, so you need to permit it to do so. You can do this by creating these ClusterRole
resources. This works with aggregated roles, adding them to the existing kyverno:admission
and kyverno:background
roles.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: kyverno:admission:secrets
labels:
rbac.kyverno.io/aggregate-to-admission-controller: "true"
rules:
- apiGroups:
- ""
resources:
- secrets
verbs:
- list
- get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: kyverno:background:secrets
labels:
rbac.kyverno.io/aggregate-to-background-controller: "true"
rules:
- apiGroups:
- ""
resources:
- secrets
verbs:
- list
- get
- create
- update
- delete
- patch
Then, use Kyverno to copy the created secret into every new namespace.
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: copy-image-pull-secret
spec:
rules:
- name: generate-image-pull-secret
match:
resources:
kinds:
- Namespace
generate:
generateExisting: true
kind: Secret
apiVersion: v1
name: image-pull-secret
namespace: ""
synchronize: true
clone:
namespace: kyverno
name: image-pull-secret
This example copies the existing secret into every new namespace. You could add exclude
conditions to skip dev namespaces or add annotations if needed.
3. Inject a custom CA certificate
Some applications must trust internal services with a custom certificate authority (CA). You can use Kyverno to inject a CA bundle secret into each namespace so apps can mount or use it as needed.
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: add-ca-secret
spec:
rules:
- name: copy-ca-secret
match:
resources:
kinds:
- Namespace
generate:
generateExisting: true
kind: Secret
apiVersion: v1
name: custom-ca
namespace: ""
synchronize: true
clone:
namespace: kyverno
name: custom-ca
This example will copy the existing custom-ca
secret from the kyverno
namespace into every new and existing namespace.
4. Set ResourceQuota templates per namespace
You might want to apply baseline ResourceQuota
objects to each namespace to prevent noisy neighbor issues in a multi-tenant cluster.
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: default-resource-quota
spec:
rules:
- name: generate-default-quota
match:
resources:
kinds:
- Namespace
generate:
generateExisting: true
kind: ResourceQuota
apiVersion: v1
name: default-quota
namespace: ""
synchronize: true
data:
metadata:
name: default-quota
spec:
hard:
requests.cpu: "1"
requests.memory: 1Gi
limits.cpu: "2"
limits.memory: 2Gi
This example ensures new namespaces don't get to consume unbounded resources by default.
A note on trade-offs
Using Kyverno to bootstrap namespaces brings consistency, but it also means you're applying cluster-wide defaults—broad strokes that can limit flexibility later. For example, this article's default NetworkPolicy
allows all traffic within a namespace. It's a valid starting point but prevents more granular segmentation later since Kyverno will keep restoring the broader rule. The same applies to ResourceQuota
: if you need to scale an app beyond the limits, adjusting the quota might affect all namespaces unless scoped more carefully.
To avoid these limitations:
- Use labels to scope policies. You can apply defaults only to namespaces with a specific label, like
env=prod
orbootstrap=default
, allowing opt-outs or overrides elsewhere. - Split policies by environment or team to reduce blast radius. For example, use different policies for shared namespaces vs. team-specific ones.
- Use
exclude
blocks to prevent policies from applying to specific namespaces (e.g.,namespace != critical-apps
).
Wrap-up
Using Kyverno for bootstrapping is a form of policy-as-code that brings consistency to your cluster setup. Whether dealing with app isolation, access credentials, TLS configuration, or resource limits, Kyverno makes the defaults repeatable, auditable, and self-healing.
Kyverno has a repository with many more policies ready to use or draw inspiration from.