Upgrading NICo v2.1 New
Upgrading NICo v2.1 New
setup.sh is designed to be idempotent: running it against an existing NICo installation upgrades each component in place. The same script and values files used for initial installation are the mechanism for upgrades — there is no separate upgrade script.
This page documents how each component behaves when you re-run setup.sh against a live cluster, what to prepare before upgrading, and version-specific considerations for the 2.0-to-2.1 upgrade path.
How setup.sh handles upgrades
Every installation phase is safe to re-run:
What is preserved across upgrades
- Vault cluster: init state, unseal keys, PKI chain, AppRole credentials, all secrets. Vault is never re-initialized on an upgrade run.
- PostgreSQL data: all NICo Core and NICo REST database state, including host records, machine state, and site config in
nico-pg-cluster. Keycloak realm data and Temporal workflow history live in a separate plainpostgresStatefulSet in the same namespace (created in phase 7c) and are also preserved. - MetalLB site config:
IPAddressPool,BGPPeer,BGPAdvertisement, andL2Advertisementinstances. These are re-applied on every run so any manual changes outsidesetup.share reconciled back to the values invalues/metallb-config.yaml. - SSH host key: the cluster SSH identity is preserved so BMC consoles do not require known-host updates.
- Site UUID: the REST site identity and site-agent registration are preserved — as long as
NICO_SITE_UUIDis unset or identical to the initial install; a different value deletes and re-creates thesite-registrationSecret. - Certificates: all cert-manager-managed certificates remain valid until their natural expiry; they are not reissued on upgrade unless the new release changes a Certificate spec.
What changes during an upgrade
- All Helm release images are updated to the new tags set in
NICO_CORE_IMAGE_TAG,NICO_REST_IMAGE_TAG, etc. - CRD schemas are updated to their new versions via server-side apply.
- ConfigMaps and Secrets produced by Helm are updated to reflect new chart values.
- The NICo Core and REST database schemas are migrated forward by their respective pre-upgrade Jobs.
- DPF operator and DPUService images are updated to the new
NICO_DPF_VERSION.
Pre-upgrade checklist
Complete every item before running setup.sh. Missing any of these can cause the upgrade to fail or leave the cluster in a partially upgraded state.
Back up Vault unseal keys
Vault unseal keys are stored in the vault-cluster-keys Secret in the vault namespace. If this Secret is lost and all Vault pods restart simultaneously, the Vault data is unrecoverable — a Raft snapshot does not substitute for the unseal keys, because snapshots are encrypted with the same keyring the keys protect.
Export and store it offline:
This file contains the plaintext Vault unseal keys and the Vault root token. Anyone holding it has full control of Vault. Store it in a secure, offline location and delete the local copy after storing.
Back up the databases
Take the backup before the upgrade — once the new version’s migrations run, the prior image tags alone are no longer a working rollback. There are two PostgreSQL instances in the postgres namespace, and both need a dump:
These are logical dumps, not PVC snapshots. Restoring means recreating the databases from SQL — expect downtime proportional to database size — and in-flight Temporal workflows at dump time cannot be replayed. If your storage class supports volume snapshots, snapshot the PostgreSQL and Vault PVCs as well for a faster, more complete restore point.
Check cluster health before upgrading
Do not upgrade a cluster that already has degraded components. Resolve any existing issues first.
All pods should be Running or Completed. Check for pods stuck in CrashLoopBackOff, Pending, or Error.
Capture the current LoadBalancer VIP assignments as a baseline — the post-upgrade verification diffs against this. The command records only stable fields (namespace, name, VIP) across all namespaces, so cosmetic changes such as AGE or port ordering cannot show up as false diffs:
Verify that Vault is fully unsealed — a sealed Vault blocks the upgrade at phase 4:
Both should show Initialized true and Sealed false.
Pull the new release
Update your local checkout to the target release branch or tag. The commands below assume an upstream remote pointing at the main repository; add it once with git remote add upstream https://github.com/NVIDIA/infra-controller.git (or substitute origin if you cloned the main repository directly):
Review the release changelog for breaking changes:
fern/changelog/— user-facing changelog entrieshelm-prereqs/diff from the prior release — any new required values fields or removed flags
Review values file changes
Check whether the new release added required values fields or changed any default values. If so, update your site values files to include any new required fields before running setup.sh.
Update image tags
Set the new image tags for the target release:
If you are upgrading DPF as part of this release, the DPF version is read from NICO_DPF_VERSION (defaulting to the value baked into setup.sh). You do not normally need to set this explicitly unless your site uses a pinned version.
DPF is enabled by default, and on DPF sites two more variables are required — preflight raises hard errors when they are unset:
Set them to the same values used at initial install (they are not persisted by setup.sh).
Run the pre-flight check
Fix all errors before proceeding. Warnings about NICO_DPF_BMC_ROOT_PASSWORD being unset are safe to ignore on an upgrade (the credential is already stored in Vault from the initial install).
setup.sh -y does not stop on preflight errors — with -y set, hard errors are printed and the run continues (“Things may fail”). The preflight gate is only enforced interactively, so genuinely resolve every error here rather than relying on the script to stop you.
Running the upgrade
With the checklist complete — including the version-specific notes for your upgrade path, which describe behavior that happens during the run — run setup.sh exactly as you would for a fresh install:
If a phase fails, setup.sh prints SETUP FAILED and offers: Run clean.sh to revert the cluster now? [y/N]. Always answer N on an upgrade. clean.sh is a full teardown — it deletes the Vault key Secrets (vault-cluster-keys, vaultroottoken), the postgres, nico-rest, temporal, and flow namespaces, and flips local-path PVs to Delete so their host directories are reclaimed. Accepting it destroys the very data the upgrade preserves. Diagnose the failed phase and re-run setup.sh instead.
setup.sh processes all phases in order. Phases that find their components already at the correct state complete quickly. Phases that detect a version delta or config change apply the update.
Upgrade-specific flags
Estimated upgrade time
Total: typically 15–45 minutes for a full upgrade with DPF.
Post-upgrade verification
Run the same checks as after initial installation:
Verify the deployed image versions match the target tags:
Verify every LoadBalancer service in the cluster kept its VIP — an upgrade must not reassign them. Diff against the baseline captured in the pre-upgrade checklist (same stable namespace/name/VIP fields):
Any diff output or a missing address means MetalLB reassigned or stopped advertising a VIP — check the MetalLB CRDs and site config objects (see the 2.0→2.1 note below) and the pins in values/nico-core.yaml.
Verify PostgreSQL has an elected leader and the cluster is running:
The first command should print Running; the second should show exactly one master pod in Running state.
Verify NICo Core is serving — hit the same HTTP health port the liveness probe uses (1080, exposed via the metrics Service):
Then run the included health check, which covers the full stack (Vault, cert-manager, ESO, MetalLB, .forge DNS records):
Version-specific upgrade notes
2.0 → 2.1: MetalLB CRD ownership migration
Impact: This upgrade path requires special handling that setup.sh performs automatically. If you skip MetalLB’s phase in your upgrade (for example, by removing it from the helmfile run), your site config objects (IPAddressPool, BGPPeer, BGPAdvertisement) will be deleted.
Root cause: In NICo 2.0, MetalLB was deployed with crds.enabled: true (the Helm chart default), which places all seven MetalLB CRDs inside the Helm release manifest. In NICo 2.1, crds.enabled: false is set explicitly so that the MetalLB cert rotator can take SSA field ownership of the CRD caBundle without conflicting with Helm on every re-sync. When Helm sees crds.enabled change from true to false, it removes the CRDs from its manifest — and Kubernetes garbage-collects every IPAddressPool, BGPPeer, and BGPAdvertisement instance stored as CRD resources, permanently deleting your site config.
How setup.sh handles this: Before running helmfile sync for MetalLB, setup.sh strips the app.kubernetes.io/managed-by: Helm label and the meta.helm.sh/release-name / meta.helm.sh/release-namespace annotations from any existing MetalLB CRDs. With the labels removed, Helm does not consider the CRDs part of its managed set and does not delete them during the sync. CRDs are then applied directly (server-side, --force-conflicts) before and after the helmfile sync.
If you are upgrading manually (not via setup.sh), you must strip Helm ownership from all MetalLB CRDs before running helmfile sync or helm upgrade:
Then apply the CRDs directly before sync:
2.0 → 2.1: DPF version update
The default NICO_DPF_VERSION in setup.sh is updated with each NICo minor release to the tested DOCA Platform Framework version. On a 2.0→2.1 upgrade, DPF is upgraded from its 2.0 version to the 2.1 version automatically as part of phase 5b.
DPF manages DPU provisioning state in DPUCluster, DPUService, and DPF CRs, all of which persist across the upgrade. In-flight DPU provisioning workflows may pause while the DPF operator restarts; they resume automatically when the new operator pod comes up.
2.0 → 2.1: NICo Core startupProbe
NICo 2.1 requires startupProbe to be explicitly configured in the machine-a-tron deployment (issue #4298). The chart now validates this at render time and fails with a clear error if startupProbe is absent. The default values provide a suitable probe scaled to ~2,300 hosts; for larger sites, refer to helm-prereqs/values/machine-a-tron-scale.yaml for recommended parameters scaled to 13,500 hosts.
Rollback
Downgrades are not a supported version move. The release and QA process tests forward upgrades only, and the supported recovery for a bad release is a forward-fix in the next patch. Treat the following procedure as disaster recovery for a failed upgrade, not as a routine operation.
setup.sh does not have a built-in rollback mechanism. Rollback consists of:
- Checking out the prior release branch or tag.
- Re-running
setup.shwith the prior image tags.
For NICo Core and REST, the Helm release is rolled back in-place, and the database migration Jobs for the prior version run on startup. NICo’s database migrations are designed to be forward-compatible; rolling back does not guarantee schema compatibility if the new version added non-nullable columns. This is why a pre-upgrade database backup is essential.
The database dumps from the pre-upgrade checklist are the rollback foundation: nico-pg-cluster holds nico_system_nico (NICo Core), nico_rest (NICo REST), and — when Flow is enabled — flow, psm, and nsm, while the plain postgres StatefulSet holds temporal, temporal_visibility, and keycloak. Restoring means replaying the SQL against a clean instance (psql -f <dump>.sql).
Re-running setup.sh with the prior image tags alone is not a complete rollback if the new version’s migrations already ran. Restore the database dumps first, then deploy the prior version.
For DPF, rolling back to a prior DPF version is not supported by NVIDIA. If DPF fails to upgrade, reach out to NVIDIA support rather than attempting a downgrade.
Using setup.sh for individual component upgrades
You can narrow an upgrade to particular components with the --skip-* flags. --skip-core, --skip-rest, and --skip-flow skip exactly the phase they name — none of them skip the prerequisite stack, and there is no --skip-prereqs. (--skip-dpf is the exception: refer to its caveat in the flag table above. --skip-core also suppresses the imagepullsecret upsert that the Core migration Job uses.)
The prerequisite phases therefore run on every invocation. That is by design and is cheap: each one is idempotent, and a phase whose inputs have not changed reconciles to the same state and exits quickly.
For a single-Helm-chart upgrade (such as rotating the NICo Core image tag without going through the full script), run from the repository root, matching what setup.sh itself executes:
This skips the MetalLB CRD handling, DPF management, the imagepullsecret upsert the migration Job depends on, and the other prereq phases. Only do this when you are certain those components do not need updating.
Do not use this on a DPF-enabled site. Core deploys in two phases with different values (--set nico-api.dpf.rbacCreate=true plus the [dpf]-enabled block), which is why setup.sh refuses to print a standalone command on the DPF path. Use ./setup.sh -y --skip-rest instead.