Installing Nazeel
Nazeel installs with one Helm chart into the customer's own cluster. It needs:
- Kubernetes 1.28 or newer (tested on k3s and kind), an ingress controller, a default StorageClass (10 GiB for the API's data volume).
- A DNS wildcard
*.<domain>pointing at the ingress for preview URLs, and optionally a wildcard TLS certificate as a Secret in each application namespace. - Outbound network: none. The product contacts only the cluster API server, your git server, your identity provider and, if configured, your SIEM collector.
Online install
The chart is not yet published to a public OCI registry:
registry.nazeel.sais reserved for it but not serving. Until it is, install from a packaged chart —make chartwritesdist/nazeel-0.1.0.tgz, and the release pipeline attaches the same file and the air-gapped bundle to each tag. Substitute that path for theoci://reference below.
helm install nazeel oci://registry.nazeel.sa/charts/nazeel --version 0.1.0 \
--namespace nazeel-system --create-namespace \
--set ingress.domain=dev.bank.internal \
--set ingress.dashboard.host=nazeel.bank.internal \
--set ingress.dashboard.tlsSecret=nazeel-tls \
--set api.bootstrap.existingSecret=nazeel-bootstrap
Then follow the printed notes: sign in with the bootstrap token, install the licence, configure SSO, create the first workspace.
Check the install answered before going further:
helm test nazeel -n nazeel-system
That runs one pod inside the cluster that asks the API's own /readyz whether it is
healthy. It uses the Nazeel image, so it needs nothing extra on an air-gapped cluster.
Values worth setting before the first install
| Value | Default | Why you would change it |
|---|---|---|
ingress.domain |
none, required | Parent domain for preview URLs. The chart refuses to install without it, because every environment would otherwise come up with no reachable URL. |
api.insecureCookies |
false |
Set to true only for an evaluation reached over plain HTTP. Leave it false wherever a browser reaches Nazeel over https, including when TLS is terminated at an ingress controller, a mesh or a load balancer rather than by this chart. |
networkPolicy.egress.allowCIDRs |
the three private ranges | Where Nazeel may connect at all. Add your range if the git server, registry or identity provider sits outside 10/8, 172.16/12 and 192.168/16. |
previewAuth.enabled |
true |
Leave it on. With it off, a preview URL is reachable by anyone who can reach the ingress. |
operator.nodeSelector, api.nodeSelector, tolerations, affinity, topologySpreadConstraints, priorityClassName |
unset | Placing the control plane on a management node pool or a licensed-core pool. |
api.persistence.size |
10Gi |
Grows with audit history and terminal recordings. |
The bundled registry, and why it is not the default
Building from source needs a registry: a node cannot run an image that exists only on another node's disk. Connecting the registry you already operate is the supported path, and almost every air-gapped install has one for mirroring.
registry.enabled=true deploys one in the release namespace for an evaluation. It serves
TLS, with a certificate the operator issues and renews from its own certificate
authority — the same machinery as the admission webhook's. A node will not pull from it
until it trusts that CA. That is why this is an option rather than the default: it needs a
change on every node, which is a platform team's decision, not a chart's.
Take the CA out of the Secret the operator wrote:
kubectl -n nazeel-system get secret nazeel-registry-tls \
-o jsonpath='{.data.ca\.crt}' | base64 -d > nazeel-registry-ca.crt
Then on each node, with containerd:
# /etc/containerd/certs.d/nazeel-registry.nazeel-system.svc:5000/hosts.toml
server = "https://nazeel-registry.nazeel-system.svc:5000"
[host."https://nazeel-registry.nazeel-system.svc:5000"]
capabilities = ["pull", "resolve"]
ca = "/etc/containerd/certs.d/nazeel-registry.nazeel-system.svc:5000/ca.crt"
with nazeel-registry-ca.crt copied to that ca.crt path, then restart containerd. Without
it, builds succeed and the resulting pods sit in ImagePullBackOff, which points nowhere
near the cause.
skip_verify = true would also work and is what an earlier version of this guide told you to
do. Do not: it disables verification for that registry entirely, and the point of the
certificate is that you no longer have to.
The CA is renewed well before it expires and the Secret is updated in place. Re-copy it to the nodes when you upgrade, or script the copy — a node holding an expired CA fails to pull with a certificate error rather than anything that names the cause.
Preview images are per commit and are deleted with their environment. A CronJob garbage
collects nightly (registry.gc.schedule); without it the volume fills and every build
starts failing at the push.
Network policy
networkPolicy.enabled is true by default. Egress is an allow-list, not
"everything except a blocklist": Nazeel makes no outbound calls by design, and a policy
permitting 0.0.0.0/0 would make that a claim the CNI cannot check.
If your git server, registry or identity provider is outside the private ranges, add it:
networkPolicy:
egress:
allowCIDRs:
- cidr: 10.0.0.0/8
- cidr: 203.0.113.10/32 # git.bank.example
Each entry carries its own except list if needed. Kubernetes requires an except to be
a strict subset of its cidr and rejects the whole policy otherwise, which would leave the
namespace with no egress restriction at all.
Symptoms of a range that is too narrow: the operator reports SourceResolved=False with a
connection timeout, or sign-in fails against your identity provider. Set
networkPolicy.enabled=false to confirm the policy is the cause before widening it.
Air-gapped install
See air-gapped.md.
Upgrades
helm upgrade with the new chart version. CRDs live in charts/nazeel/crds and are
applied on install; on upgrade apply them explicitly first:
kubectl apply -f charts/nazeel/crds/
helm upgrade nazeel ./nazeel-0.2.0.tgz -n nazeel-system --reuse-values
The API's data directory (SQLite database, keys, terminal recordings) lives on its PVC.
That claim carries helm.sh/resource-policy: keep, so helm uninstall leaves it in place.
It holds the audit log, which is append-only and must never be deleted, and the accounts
and licence behind everything else. Removing it is a deliberate kubectl delete pvc.
Back it up with the built-in command rather than copying the file while it is open:
kubectl exec -n nazeel-system deploy/nazeel-api -- /nazeel-api backup --out /tmp/nazeel.db
kubectl cp nazeel-system/<pod>:/tmp/nazeel.db ./nazeel-$(date +%F).db
/nazeel-api verify-backup --in ./nazeel-$(date +%F).db
The chart also ships a backup CronJob; see backup in values.yaml.