Upgrading NICo v2.1
Upgrading NICo v2.1
setup.sh is designed to be idempotent for supported deployment topologies: 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. For a predecessor Flow Deployment that bundles PSM or NSM, first choose whether to preserve it or follow the manual Flow-only overwrite guidance.
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
After any required manual Flow overwrite, 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 are preserved too. They stay onnico-pg-cluster, or on the deprecated standalonepostgresStatefulSet in the same namespace for a Site that still uses it. Upgrading withautonever moves a deployed database from one to the other. Refer to Standalone Temporal/Keycloak PostgreSQL deprecated. - 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.
- The DPF operator chart is reinstalled from the
helm-prereqs/doca-platformcommit the newsetup.shpins, with the operator image tag fromNICO_DPF_IMAGE_TAG(default: that release). The DPUServices (dts,doca_hbn, and NICo’sdpu_agent,dhcp_server,fmds,otel) are versioned independently by[dpf.services.*]in the NICo site config (dpf.md §3.5); the pin does not change them.
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.
If you are upgrading to asynchronous VPC peering deletion for the first time, plan an API maintenance window. You must stop the old API before starting the new one; the default rolling update does not do this.
If flow/flow or an active Flow Pod still contains a psm or nsm container, do not run setup yet. Preserve that release, or follow the manual Flow-only overwrite guidance. Setup rejects that predecessor topology or an incomplete Flow-only rollout before making any cluster change.
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 secrets KEK
If the site keeps credentials in Postgres (as detailed in Secrets Storage), the database dump below holds only ciphertext, and every key encryption key (KEK) its rows reference is needed to read it back.
With an Integrated provider, export every Secret that holds key material your [secrets.kms] providers reference, including keys that are no longer routed but are still needed for older rows or older dumps, and store them with the Vault unseal keys. Copy a key supplied from a file or an inline value from its source instead.
With a KMIP provider, the KEK lives only in the KMIP server. Back it up through that product’s key backup procedure, and keep every key that retained dumps still reference until those dumps age out.
The following command exports one Secret; repeat it for each:
With a Transit provider the KEK lives in the Transit engine’s storage, which the unseal-key export above does not include; take a Vault storage snapshot as well, for example vault operator raft snapshot save vault-storage.snap.
This file contains the plaintext KEK. Anyone holding it together with a database dump can decrypt every stored credential. Store it where you store the unseal keys and delete the local copy.
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. Dump every PostgreSQL instance in the postgres namespace. There are two on a Site that still runs the deprecated standalone postgres StatefulSet:
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/dsx-ai-factory/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
Build and push images from the same repository revision as the target release checkout, then set their tags below. The chart and setup script must come from that revision too; refer to the image compatibility policy for more details.
If you are upgrading DPF as part of this release, the DPF version is the pinned helm-prereqs/doca-platform commit of the setup.sh you run: the submodule gitlink in a git checkout, or helm-prereqs/doca-platform.pin in the packaged nico-prereqs chart, which setup.sh clones at that commit. There is no version variable to set, and both paths install the same commit. Air-gapped sites that set NICO_DPF_SRC manage that checkout themselves: update it to the same commit (git submodule status helm-prereqs/doca-platform, or the sha in doca-platform.pin), because setup.sh installs whatever it contains and only warns when its HEAD differs from the pin. Remove NICO_DPF_VERSION and NICO_DPF_SRC_DIR from your environment files: setup.sh now rejects them when installing DPF. A leftover helm-prereqs/.dpf-src/ clone from earlier releases is no longer used and can be deleted.
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).
Prepare Virtualized-to-Flat Routing for a 2.2-to-2.3 Upgrade
Before upgrading an agent that serves an active Virtualized-to-Flat peering, inspect every affected Flat prefix contained by an effective site_fabric_null_routes prefix. Ensure the tenant VRF learns an imported or explicitly admitted underlay route that is at least as specific as the containing blackhole, and verify forward and return reachability before starting the agent rollout. A leaked default does not qualify when the blackhole is more specific than /0; combining a /0 null route with same-family default-route leakage is unsupported. Follow the FNN-to-Flat routing prerequisite for the supported route-provisioning methods.
Run the pre-flight check
Fix all errors before proceeding. On DPF sites, registration of a new DPUDevice
remains blocked until the site-wide BMC root is available from the configured
credential sources and the 60-second refresh publishes the derived
current-version bmc-shared-password Secret. NICo retains an existing Secret,
but does not infer startup readiness from that retained value. This does not
prevent a local_first or backend Core rollout; Core starts and retries the
source. Authoritative local mode requires version 0 before Core starts when
v0 is current or the current target cannot be resolved.
DPF BMC root ownership on upgrade
The backward-compatible default is
nico-api.credentials.bmcSiteWideRootSource: local_first. An existing
backend-owned version 0 credential therefore remains in use without a new
Secret when NICO_DPF_BMC_ROOT_PASSWORD is unset. When it is set, setup creates
nico-system/nico-bmc-v0-credentials, configures authoritative local ownership,
and deploys Core once with DPF enabled. A later DPF-enabled Core deployment
reuses that Secret even if the variable is omitted. Declining deployment leaves
it untouched. A later non-DPF Core deployment preserves the mount and local
ownership only when the installed release already uses this exact configuration;
a stray Secret is not adopted. A different supplied value fails rather than
overwriting version 0. If
coordinated BMC rotation has advanced the target to version 1 or
later, that version remains backend-resolved. A rerun also removes the obsolete
dpf-set-bmc-root Job and its dpf-bmc-root-pw and dpf-admincli-cert Secrets
if an interrupted older setup left them behind.
To opt into local ownership of version 0, first create a Kubernetes Secret whose
sparse credential file contains the exact existing version 0 value. Then add
both nico-api.credentials.file.existingSecret and
nico-api.credentials.bmcSiteWideRootSource: local to the persistent Core
values before rerunning setup.sh. As a shortcut when no other credential-file
Secret is configured, export the exact existing value as
NICO_DPF_BMC_ROOT_PASSWORD; setup creates and wires its dedicated Secret. Do
not use that shortcut when the Core values name another credential-file Secret.
Ensure the environment credential source does not also supply this entry,
because it precedes the file. A Secret created without the values update is not
mounted. With DPF enabled, carbide-api requires local v0 before starting on
fresh and existing sites whenever v0 is current or the current target cannot be
resolved. A target at version 1 or later does not require local v0 before
startup. This prevents a rolling update from activating local ownership while
an older replica can still register a DPU that uses the shared credential. A
transient rotation-target read failure does not block startup when local v0 is
present. After NICo accepts local v0, removing it retains the last accepted
shared Secret and logs an error until the original value is restored.
The default pinned DPF v26.4.0 does not support BMC credential rotation;
adopting and validating supporting DPF behavior is tracked by
#6147. If the rotation
target is already version 1 or later, the local
version 0 entry does not become current. If devices still use version 0, keep
the copied local value unchanged after this ownership migration; later password
changes must use coordinated BMC rotation so hardware and convergence records
advance together. Do not stage that rotation while DPF manages any DPU: its
single shared BMC Secret cannot authenticate a mixed-version fleet. DPF
credential rotation is tracked by
#6147. See
Credential Sources.
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. The one exception is the database checks for Temporal and Keycloak. When a value would repoint either one away from its data, or can’t be honored, setup stops even with -y. That covers a move off the standalone postgres StatefulSet without a valid migration receipt, and false on a database already on nico-pg-cluster. It also covers an invalid value, a database host setup.sh doesn’t manage, a KEYCLOAK_NS that differs from keycloak.namespace, and cluster state preflight can’t read.
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):
Moving credentials to Postgres
Migrating an existing site’s credentials from Vault to Postgres is a configuration walk described in Secrets Storage, and it can be folded into an upgrade window. Two points matter when you migrate:
- Every step changes the
nico-apiconfig and, in the reference installation, needskubectl -n nico-system rollout restart deployment/nico-api: the chart carries the Stakater Reloader annotation, so installations that run Reloader restart automatically, but the reference installation does not install it. - The default rolling update also keeps the old pod running until the new one is ready, so finish each step’s rollout before starting the next. Keep
bmc_rotation_enabledanduefi_rotation_enabledat their defaultfalseuntil the whole fleet runs one config.
Once the writer is Postgres, the KEK backup above belongs in every pre-upgrade checklist.
Version-specific upgrade notes
Upgrades That Introduce Asynchronous Peering Deletion
Follow these steps the first time you upgrade to the peering deletion behavior added in #6917. You can upgrade directly; no intermediate release is required.
The new API waits for DPUs to stop allowing a peering before deleting it. An
old API can still tell those DPUs to allow the connection, making the new API
mistakenly treat deletion as finished. Stop every old API before starting any
new one. The Helm chart’s default RollingUpdate can run both versions at once,
even with just one replica.
During your API maintenance window:
-
Record the namespace, Deployment name, and desired replica count for every
nico-apiDeployment serving the site. Pause any automation that would restore the old replicas during the maintenance window. -
Scale those Deployments to zero. Wait for all old API pods and any API processes outside those Deployments to exit and their requests to drain.
-
Run the normal upgrade with the new image and desired replica count. With
setup.sh, setnico-api.replicasin the Core values file to that count. Phase 6 runs the Core migration Job and starts the new API pods. Keep the old processes stopped. -
Check the API pods in each namespace before restoring automation:
Every expected API pod must be present, and its
nico-apicontainer must use the new image. The selector uses the chart’s default name; replacenico-apiif you setnameOverride. For API processes outside those Deployments, check their container image or executable version through their service manager, and confirm that every old process has stopped. Follow the peering deletion guide to confirm any pending deletions finish.
Do not roll back to an API that lacks this behavior. It deletes peerings without waiting for the DPUs, and restoring a database backup does not undo their network changes. For the network-version and migration details, refer to the configuration reference.
2.3 → 2.4: Standalone Temporal/Keycloak PostgreSQL deprecated
The standalone postgres StatefulSet for the Temporal and Keycloak databases is deprecated. Support for it will be removed in 2.5. Sites still on it are highly encouraged to migrate to nico-pg-cluster, which already holds the NICo Core and REST databases. Follow the migration guide to move them.
Before 2.4, temporal.useHaPostgres and keycloak.useHaPostgres in helm-prereqs/values.yaml defaulted to false, so every new Site got the StatefulSet. They now default to auto. With auto, both databases are created in nico-pg-cluster, but their location is left unchanged for an existing Site. So an existing Site needs no changes for the upgrade, and a new Site only gets the StatefulSet when a value is set to false. With postgresql.enabled: false, nico-pg-cluster is an external or out-of-band PostgreSQL, so auto means false unless the databases are already there. Such a Site can move them with true once it provisions them, as Using an external or out-of-band PostgreSQL describes. Copying existing data from the StatefulSet onto an external or out-of-band PostgreSQL isn’t supported in 2.4. Support is tracked in #7989. While either database stays on the StatefulSet, preflight warns that it’s deprecated, and setup.sh repeats the warning at the end of the run.
On an existing Site, the new temporal, temporal_visibility, and keycloak databases on nico-pg-cluster stay empty and unused until it migrates. To migrate, set temporal.useHaPostgres, keycloak.useHaPostgres, or both to true, because auto never moves an existing database. Then run the migration script with the matching --db temporal, --db keycloak, or --db both, as the migration guide describes. setup.sh won’t move a database off the StatefulSet until the script has copied it. A values file copied from an earlier release may still set false. That keeps the databases on the StatefulSet and creates nothing on nico-pg-cluster. Once a database is on nico-pg-cluster, preflight refuses false, because there’s no supported way back. After both have moved, setup.sh leaves the StatefulSet and its data in place, so remove them as Removing the standalone StatefulSet describes.
2.2 → 2.3: Core components are mandatory
Impact: The core NICo subcharts (nico-api, nico-bmc-proxy, nico-dhcp, nico-dns, nico-hardware-health, nico-pxe, nico-ssh-console-rs) no longer have <chart>.enabled toggles - they are unconditional dependencies in helm/Chart.yaml and always install. A site values file (including one passed via setup.sh --core-values) that still sets a core <chart>.enabled key, whether true or false, keeps working: the key is ignored and the component installs. No values changes are required for the upgrade. If a site previously disabled a core component (for example, a DHCP or BMC proxy provided externally), plan for the in-cluster component to appear after the upgrade. Optional services (nico-dsx-exchange-consumer, nico-ntp, unbound) keep their toggles.
NICo Flow is also mandatory within the REST install: --skip-flow and the helm-prereqs flow.enabled value were removed, phase 7h has no skip flag of its own and runs whenever REST is installed (only --skip-rest skips it, together with all of REST), and the Flow chart moved from helm/charts/nico-flow to helm/nico-flow. A leftover nico-flow.enabled key in umbrella values is ignored, because the umbrella no longer deploys Flow; setup.sh installs it. Sites running an external PostgreSQL (postgresql.enabled: false in helm-prereqs values) must provision the flow database and the flow.nico.nico-pg-cluster.credentials Secret in the flow namespace before running setup.sh, or phase 7h fails after its 120s credential wait.
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 helm-prereqs/doca-platform submodule pin 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.
2.1 → 2.2: NICo REST postgres volume size
NICo 2.2 raises the postgres StatefulSet’s volumeClaimTemplates storage request from 1Gi to 10Gi. Kubernetes forbids changing that field on an existing StatefulSet, so phase 7c of setup.sh deletes the StatefulSet with --cascade=orphan and re-applies it; the postgres-0 pod and its postgres-data-postgres-0 PVC are kept. The PVC of an upgraded site stays at 1Gi. It can be left as is, or grown in place if the StorageClass has allowVolumeExpansion: true and its storage provisioner supports expansion:
2.2 → 2.3: Machine-a-Tron startupProbe Default
NICo 2.3 raises the default startupProbe.failureThreshold from 20 to 120 (60 minutes), sized for a 250-rack site spread over ten pods (issue 5968). The threshold applies to each pod on its own, so size it for the pod that registers the most records. For larger sites, raise startupProbe.failureThreshold following the sizing rule in the chart’s values.yaml, as helm-prereqs/values/machine-a-tron-scale.yaml does.
2.2 → 2.3: Vault Probe Settings
NICo 2.3 raises the Vault server probe timeoutSeconds from 3 to 10 and failureThreshold from 2 to 5 in helm-prereqs/operators/values/vault.yaml.
For example, on a 250-rack site, the active node missed two 3-second liveness probes under load, was killed, came back sealed, and the standby nodes stayed leaderless.
The Vault StatefulSet uses updateStrategy: OnDelete. This means that helmfile sync updates the pod template, but running pods keep the old probes until they are deleted. Phase 3 prints a warning naming the pods still running the previous revision. If the StatefulSet status cannot be read or has not caught up, the warning indicates that the revision could not be verified.
After the upgrade finishes, find the active node, then roll the pods one at a time, standby nodes first, and the active node last. The active node is the pod whose HA Mode is active:
From the repository root, run the following commands for each standby in turn, and only then for the active node. Replace <pod> with the pod being rolled and <active> with the pod whose HA Mode is active at that point. A recreated pod starts sealed and is not Ready until it is unsealed, so the first wait is for Running:
The root token travels on standard input; it does not appear in the kubectl exec arguments that the API server records in audit events. The sed operation trims the output to the cluster summary: Healthy, Failure Tolerance, Leader, and Voters.
unseal_vault.sh returns once vault status reports the pod unsealed. Unsealing only opens the pod’s own storage, and does not show that the pod has rejoined the cluster as a healthy voter. Continue to the next pod only when Healthy is true, Failure Tolerance is 1 on a three-node cluster, and <pod> is listed under Voters.
Right after the unseal, the summary can still show Healthy false and Failure Tolerance 0 until the leader hears from the pod. Rerun the check after a few seconds. A pod that comes back without its Raft data joins as a non-voter and appears under Voters only after autopilot promotes it. Deleting the next standby before the promotion can leave the three-node cluster below quorum. When the former active node is rolled, a standby has taken over; run the status loop again to find <active>.
If kubectl wait reports the pod as not found, the StatefulSet has not recreated it yet. Run the wait again.
2.2 → 2.3: Kustomize deployment deprecated
The Kustomize deployment under deploy/ (deploy/kustomization.yaml,
deploy/nico-base, deploy/nico-system, deploy/nico-unbound-base) is
deprecated in 2.3 and will be removed in 2.4. Every service it deploys has a
Helm chart under helm/, and setup.sh installs NICo Core from those charts
only. Kustomize deployments keep working in 2.3 but receive no new
configuration. Before upgrading to 2.4, install with the Helm charts as
described in the quick start and retire the
Kustomize overlays. The rest-api/deploy/kustomize bases that setup.sh uses
for cert-manager, PostgreSQL, and Temporal are not affected.
2.3 → 2.4: Kustomize deployment removed
The Kustomize deployment under deploy/ is removed in 2.4. Install and
upgrade NICo Core with the Helm charts under helm/, either through
setup.sh as described in the quick start
or directly as described in helm/README.md. Sites
still running the Kustomize manifests must move to the Helm charts before
upgrading to 2.4. The annotated site configuration example now lives at
helm/examples/nico-api-site-config.toml,
and the .nico service endpoint reference moved to
DNS Service Endpoints. The
rest-api/deploy/kustomize bases that setup.sh uses are not affected.
Rollback
Downgrades are not a supported version move. The release policy 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), flow, and any retained psm or nsm databases from a predecessor bundled-manager deployment. The temporal, temporal_visibility, and keycloak databases are there too, unless the Site still runs the standalone postgres StatefulSet. 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.
If you have upgraded to asynchronous peering deletion, do not return to an API that lacks it, even after restoring the database. Fix the newer version instead.
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 and --skip-rest skip exactly the phase they name - none of them skip the prerequisite stack, and there is no --skip-prereqs. NICo Flow (Phase 7h) has no skip flag of its own; it upgrades together with REST and is skipped only by --skip-rest. (--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.
To upgrade only the NICo Core release without running the prerequisite phases, use a chart and Core image built from the same repository revision, following the image compatibility policy. Update both together when moving to a new revision. 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 command unchanged on a DPF-enabled site. It omits the
rendered [dpf] block and nico-api.dpf.rbacCreate=true values that setup.sh
adds to the single Core deployment. From the repository root, use
helm-prereqs/setup.sh -y --skip-rest instead.