Installing Nazeel

Nazeel installs with one Helm chart into the customer's own cluster. It needs:

Online install

The chart is not yet published to a public OCI registry: registry.nazeel.sa is reserved for it but not serving. Until it is, install from a packaged chart — make chart writes dist/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 the oci:// 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.