Docs
HelmApi

Status & Conditions

NetBoxEnterprise status fields, condition types, and component health reference

The operator maintains a detailed status on each NetBoxEnterprise resource. Use this to monitor cluster health, troubleshoot issues, and integrate with monitoring systems.

Viewing Status

# Summary view
kubectl -n netbox get netboxenterprises -o wide

# Detailed status
kubectl -n netbox describe netboxenterprise netbox

# JSON output for scripting
kubectl -n netbox get netboxenterprise netbox -o jsonpath='{.status}'

Top-Level Status Fields

FieldTypeDescription
readyboolOverall cluster readiness - true only when all components are ready
messagestringHuman-readable summary of the current state
versionstringOperator version managing this cluster
observedGenerationint64The spec generation last processed by the operator
convergedGenerationint64Highest spec generation that has reached all-components-ready. When it equals metadata.generation, the current config rollout has converged and the progress sentinel reports Ready.
lastReconcileTimestringTimestamp of the last successful reconciliation (RFC 3339)
migrationHashstringSHA-256 of migration-relevant inputs (image tag, wheelhouse, and similar). Detects when a migration must re-run.
migrationPhase1HashstringSHA-256 of Phase 1 migration inputs
secretChecksumstringAggregate hash of external secret resourceVersion values. Drives the debounced restart logic.
secretChecksumObservedAtstringRFC 3339 timestamp recording when secretChecksum was last observed
phaseenumCoarse startup phase. See Startup Phase.
licenseStatusstringComputed display string for the LICENSE column of kubectl get. Holds the license type (Dev, Trial, Paid) when the license is healthy, or the LicenseHealth condition reason when it is not. See License Information. Unset until the first reconcile completes.
dependencyWaitStartedAtstringRFC 3339 timestamp of when the operator first saw a dependency-wait timeout for the current generation. It measures how long the wait has lasted, so a brief wait is distinguishable from a hanging one. Cleared on the first successful reconcile.
wheelhouseFingerprintstringLast-seen custom-plugin wheelhouse fingerprint from the watcher sidecar, as <mtime>_<size> or absent. Stored so the migration hash is a function of stored state only.

Example:

status:
  ready: true
  phase: Serving
  message: "All components ready"
  version: "2.3.0"
  licenseStatus: "Paid"
  observedGeneration: 3
  convergedGeneration: 3
  lastReconcileTime: "2026-06-08T10:30:00Z"

Startup Phase

status.phase reports where an install has reached. Without it, a multi-minute install shows only an opaque 503. The Admin Console and the kubectl printer columns both read this field.

PhaseMeaning
WaitingForDependenciesPostgreSQL, Redis, or object storage is not serving yet.
MigratingThe database migration Job is running.
InstallingPluginsCustom plugins are being installed from the wheelhouse.
CollectingStaticNetBox is collecting its static files.
BootingThe NetBox pods are starting but not yet serving.
ServingNetBox is serving traffic.
DegradedA component failed. Read the conditions for the cause.

The variants are ordered along the install timeline. The operator observes WaitingForDependencies, Migrating, Serving, and Degraded directly, from the dependency-wait timer, the migration Job status, component readiness, and the existing conditions. The three in-pod phases -- InstallingPlugins, CollectingStatic, and Booting -- happen inside the NetBox entrypoint, which reports them back through a startup table the operator reads.

kubectl -n netbox get netboxenterprise netbox -o jsonpath='{.status.phase}{"\n"}'

Conditions

Standard Kubernetes conditions on the NetBoxEnterprise resource:

ConditionStatusReasonDescription
ReadyTrueAllComponentsReadyAll components are healthy and serving traffic
ReadyFalseReconcilingA reconcile is in progress; readiness not yet confirmed
ReadyFalseComponentsNotReadyOne or more components still initializing
ReadyFalseReconcileFailedReconciliation encountered an error
ProgressingTrueApplyingResourcesOperator is actively applying resources
ProgressingTrueWaitingForComponentsResources applied, waiting for pods to become ready
ProgressingFalseReconcileCompleteReconciliation finished successfully
DegradedTrueReconcileErrorReconciliation failed - check operator logs
RoutingHealthTrueGatewayActiveNorth/south traffic is served by the Gateway API
RoutingHealthUnknownGatewayProgrammingThe Gateway was created; waiting for its controller to report Programmed=True (not yet serving)
RoutingHealthFalseGatewayNotProgrammedThe Gateway controller reports Programmed=False; its listeners are not up
RoutingHealthTrueIngressActiveNorth/south traffic is served by operator-emitted Ingress
RoutingHealthTrueIngressFallbackspec.gateway is enabled but the Gateway API is unavailable (CRDs absent or RBAC missing); serving via Ingress
RoutingHealthFalseRoutingModeMismatchThe resolved routing path is disabled (e.g. mode: gateway with spec.gateway.enabled: false); nothing is emitted
PluginsReadyTruePluginsImportableAll enabled plugins import successfully
PluginsReadyFalsePluginUnimportableAn enabled plugin failed to import; the message names it
PluginsReadyFalseSettingsLoadFailedNetBox settings failed to load before any single plugin could be blamed (an enabled plugin broke settings import, or an invalid Python config override)
LeaderElectedTrueLeaderElectionAcquiredThe operator pod named in message holds the leader lease. Present only on HA (multi-replica) operator deployments; only the leader writes status.
HANotEnabledTrueHANotEnabledGates Ready. The cluster has 3 or more nodes but operator high availability is not enabled. Present only in that state. Run netbox-enterprise enable-ha to complete the setup. Embedded Cluster only — a Helm install has no such signal, so this condition does not appear.
HAWithoutExternalDataTrueHAWithoutExternalDataGates Ready. Operator HA is enabled but a data tier cannot survive node loss. The message names which of PostgreSQL, Redis, or object storage is at fault. Requires a confirmed Embedded Cluster HA signal, so this condition never appears on a Helm install — see the note below.
SharedStorageNotMultiNodeTrueSharedStorageNotMultiNodeAdvisory: netbox.replicas is above 1 with ReadWriteOnce/ReadWriteOncePod media and scripts volumes and object storage off, so the extra replicas will hang on volume multi-attach. Suppressed when object storage is in use. Never gates Ready, because the combination is legitimate on a single node or with pinned scheduling.
InClusterStorageBelowNodeGateTrueInClusterStorageBelowNodeGateAdvisory: storageBackend is in-cluster but the cluster has fewer than 3 nodes, so all three Garage replicas can share a node and the store holds no real redundancy. Derived from the live node count, so it does apply to Helm installs. Does not gate Ready.
MultiNodeNotEntitledTrueMultiNodeNotEntitledAdvisory: the cluster has 3 or more nodes but the license lacks the multi-node entitlement, so in-cluster PostgreSQL and Redis scaling is capped at a single instance. Does not gate Ready — a capped tier still runs correctly.
AssetLifecycleFalseRequiresPremiumLicenseAsset Lifecycle was requested but the license is below Premium, so it was not enabled
AssetLifecycleFalseConflictsWithNetboxInventoryAsset Lifecycle was requested but netbox_inventory is present, so it was not enabled
AssetLifecycleFalseConflictsWithNetboxLifecycleAsset Lifecycle was requested but netbox_lifecycle is present, so it was not enabled
RedisSpreadHeldTrueRedisSpreadHeldAdvisory: two or more in-cluster Redis pods share a node and their ReadWriteOnce volumes pin them there, so the hard one-pod-per-node spread is held as a soft preference. Redis serves normally but is not protected against losing that node, and the state is permanent until an operator acts. Does not gate Ready. See Recovery steps.
TlsCaTrustFalseCaSecretMissingA referenced CA secret is absent, empty, or holds no PEM certificate. Covers both the keychainCaCertificates references of a TLS-verifying service (Redis, PostgreSQL, S3) and the global spec.extraCaCertificates entries. Surfaced here so the failure is visible before the bundle init container fails fast at runtime.
TlsCaTrustTrueCaTrustResolvedThe cluster references at least one CA secret and every one of them resolves.
WheelhouseSourceProbeFalseSourceUnreachableThe configured custom-plugin wheelhouse source could not be reached. See Custom Plugins.
MigrationSucceededTrue / FalseReports the outcome of the most recent database migration.
SuperuserSecretFalseSuperuserSecretKeyMissingGates the NetBox pod, not Ready. A bring-your-own superuser Secret exists but does not carry a key named against it. The NetBox pod then never starts, and nothing else operator-side says why. See spec.netbox.config.superuser.
SuperuserSecretFalseSuperuserSecretMissingA named superuser Secret is absent. Self-correcting: a resource may be applied ahead of its Secret, and the condition flips True once the Secret exists.
SuperuserSecretTrueSuperuserSecretResolvedEvery superuser Secret key resolves.
VeleroRestoreConfigInvalidTruefive reasons, belowAdvisory: the velero namespace does not hold exactly one usable pod-volume-restore plugin config, so a future restore cannot return your volume data. See Velero restore plugin config. Does not gate Ready -- nothing is wrong with the running cluster.

New in 2.3.0, the four PostgreSQL restore conditions:

ConditionStatusGates Ready?Description
PostgresRestorePendingTrueNoA restored cluster whose PostgreSQL is wedged. Patroni failed to bootstrap over the empty data volume that a Velero restore leaves behind, and the instance pod parks with its database container not ready. The dependency wait already holds Ready=Unknown, so this condition only names the cause. See PostgreSQL Backups and Disaster Recovery.
PostgresRecoveryRequiredTrueYesA restored cluster whose PostgreSQL bootstrapped cleanly over an empty data volume. Every other surface reports success, and the cluster serves a database with none of your data in it. Derived live from Velero's velero.io/restore-name label and PGO's own PostgresDataInitialized condition, so it clears itself once the database is back.
PostgresRestoreFailedTrueYesThe pgBackRest restore Job is failing. It fires on the first failed attempt, because PostgreSQL is shut down for the whole retry cycle. It clears once PGO reports a database. See Restore fails in a loop.
PostgresReconciliationPausedTrueNoPGO paused reconciliation of the PostgresCluster because PostgreSQL backups were disabled on a live cluster. The pause is total: no spec change, no upgrade, and no resource change reaches the database, while the database keeps serving. Set spec.postgresql.backups.enabled back to true to resume.

PostgresRestoreFailed is a useful signal when it is present, and its absence proves nothing. On a cluster with no primary pod at all, the condition can be absent while the restore is still failing. Read status.pgbackrest.restore on the PostgresCluster directly in that case.

The AssetLifecycle condition is only present when Asset Lifecycle was requested and gated off. It is absent when Asset Lifecycle is disabled, and absent when it is enabled and healthy. See Asset Lifecycle Management.

TlsCaTrust is absent entirely when the cluster configures no custom CA trust, so an install that never asked for one carries no condition about it.

SuperuserSecret is absent when the operator generates the superuser credentials, because it then writes the very keys it reads.

Velero restore plugin config

Velero restores a filesystem-backed volume through a restore-wait init container, and it reads that container's security context from a ConfigMap in the velero namespace. It finds the ConfigMap by label selector, and it needs exactly one match.

The operator checks that on every reconcile and reports VeleroRestoreConfigInvalid when the check fails. The reason names which of five faults applies:

ReasonWhat is wrong
VeleroRestoreConfigMissingNo ConfigMap matches the selector.
VeleroRestoreConfigDuplicatedMore than one matches, and Velero needs one.
VeleroRestoreConfigUnparseableThe secCtx key is not valid YAML.
VeleroRestoreSecCtxIncompleteThe secCtx omits a required key.
VeleroRestoreSecCtxUnsafeA key holds a value the restore cannot survive.

Read the reason first for the kind of fault, then read the message for the detail you act on. Only the message says which ConfigMaps matched, or which keys are absent.

The condition is absent when Velero is not installed. It is also absent under rbac.scope: namespace, because the reads are cross-namespace: the operator emits nothing rather than a false alarm. A namespace-scoped install has to run the count itself:

kubectl get configmap -n velero \
  -l 'velero.io/plugin-config,velero.io/pod-volume-restore=RestoreItemAction'

See PostgreSQL Backups and Disaster Recovery for what to do about each fault.

Ready is a weaker signal on Helm than on Embedded Cluster

HANotEnabled and HAWithoutExternalData are the two conditions that gate Ready, and both depend on an Embedded Cluster signal that a Helm install does not have. On Helm, a data tier that cannot survive node loss therefore produces no condition and no effect on Ready. Verify replica counts directly — see High Availability.

Each condition includes:

FieldTypeDescription
typestringCondition type
statusstringTrue, False, or Unknown
reasonstringMachine-readable reason code
messagestringHuman-readable detail
lastTransitionTimestringWhen the condition last changed
observedGenerationint64Spec generation when set

Inspecting Conditions

# Get conditions as a table
kubectl -n netbox get netboxenterprise netbox -o jsonpath='{range .status.conditions[*]}{.type}{"\t"}{.status}{"\t"}{.reason}{"\t"}{.message}{"\n"}{end}'

GarageCluster Conditions

When in-cluster object storage is enabled, the object store is a separate GarageCluster resource with its own conditions. These do not appear on the NetBoxEnterprise resource, so kubectl describe netboxenterprise will not show them:

kubectl -n netbox describe garagecluster
TypeStatusReasonDescription
LayoutAppliedTrueLayoutAppliedGarage has applied its storage layout
LayoutAppliedFalseLayoutPendingThe storage layout has not been applied yet
ServingTrueClusterServingGarage reports a write quorum for every partition and answers an S3 round-trip
ServingFalseClusterNotServingGarage does not yet report a write quorum for every partition
ReadyTrueGarageServingGarage is laid out and serving S3 requests
ReadyFalseLayoutPendingGarage is not yet both laid out and serving
ReadyFalseWaitingForPodsToo few StatefulSet pods are Ready to reach the admin API — ordinary first-provision startup
ReadyFalseQuorumLostToo few pods are Ready and a layout already exists, so the cluster has lost quorum and genuinely cannot serve
ReadyFalseReconcileFailedReconciliation encountered an error
GarageSpreadHeldTrueGarageSpreadHeldAdvisory: two or more Garage pods share a node and their ReadWriteOnce volumes pin them there, so the hard one-pod-per-node spread is held as a soft preference. Object storage serves normally but is not protected against losing that node. See A held Garage spread.

Ready is the conjunction of LayoutApplied and Serving, so a held spread cannot gate it — the store is serving, it just is not protected.

Component Status

Each component reports its own health under status.components:

ComponentKeyDescription
NetBox appcomponents.netboxWeb application deployment
NetBox workercomponents.workerBackground task worker
PostgreSQLcomponents.postgresqlDatabase (internal or external)
Rediscomponents.redisCache/queue
Diodecomponents.diodeData ingestion pipeline (if enabled)
Copilotcomponents.copilotCopilot backend (present when Copilot is enabled)

Each component has:

FieldTypeDescription
readyboolWhether the component is ready
messagestringHuman-readable status (e.g., "2/2 replicas ready")
conditions[]ConditionComponent-specific conditions

Component conditions carry their own reason codes:

ReasonCondition / StatusDescription
ComponentReadyReady / TrueThe component is healthy and serving
ProgressingProgressing / TrueThe component is starting or rolling out
NotReadyReady / FalseThe component is not yet ready
UnknownReady / FalseComponent health could not be determined (for example, PostgreSQL or Redis status was unavailable)

A component that fails also sets Degraded / True with a component-specific reason and the failure detail in message.

Inspecting Component Health

# All component statuses (components is an object keyed by component name)
kubectl -n netbox get netboxenterprise netbox \
  -o jsonpath='{.status.components}' | jq
# Specific component
kubectl -n netbox get netboxenterprise netbox \
  -o jsonpath='{.status.components.postgresql}'

License Information

License details are surfaced in status.license:

FieldTypeDescription
tierenumThe tier your license carries: Professional or Premium. The schema accepts further values that no available license uses.
editionenumDead field. The operator no longer writes it. It stays in the schema so a status written by an older operator still parses. Do not read it.
licenseIdstringUnique license identifier
licenseTypestringLicense type (dev, trial, prod, paid)
customerNamestringCustomer name
customerEmailstringCustomer email
channelNamestringChannel (stable, beta, dev)
isAssuranceEntitledboolWhether NetBox Assurance features are available
isCopilotEntitledboolWhether Private Copilot is entitled by the license (defaults false)
isNdxEntitledboolWhether NetBox Data Exchange is entitled, meaning the license carries a paid NDX token. New in 2.3.0. See NetBox Data Exchange.
isVisualExplorerEntitledboolWhether Visual Explorer, the first UI Platform app, is entitled. New in 2.3.0.
platformOrgIdstringPlatform organization ID from the license
entitlementsmapRaw entitlements map keyed by entitlement name. Each entry has a title (string) and a value (any JSON type). Used for additional checks.
expiresAtstringExpiration date (ISO 8601)
kubectl -n netbox get netboxenterprise netbox -o jsonpath='{.status.license}'

The operator also reports a LicenseHealth condition. status.licenseStatus carries the same verdict as one string, which is what the LICENSE column of kubectl get netboxenterprise prints.

ConditionStatusReasonDescription
LicenseHealthTrueLicenseValidThe license is present and current.
LicenseHealthFalseLicenseExpiredThe license is past its expiry date. Enterprise features may be unavailable. Renew it in the Replicated vendor portal.
LicenseHealthFalseLicenseUnavailableNo license could be fetched and no cached license exists. Check that the in-cluster Replicated SDK is reachable. Reconciliation is blocked until a license is available.
LicenseHealthFalseLicenseUnrecognizedThe SDK answered with a license type this release does not recognize. Distinct from LicenseUnavailable, which means no answer at all.
LicenseHealthFalseLicenseInvalidThe license failed a tamper check. Reconciliation is blocked while that stands.

The LICENSE column's own help text, which kubectl explain prints, names a LicenseMismatch reason. The operator does not emit that reason. Read the five rows above as the real set.

Generation Tracking

The operator tracks observedGeneration to detect configuration drift:

  • metadata.generation: Incremented by Kubernetes when spec changes
  • status.observedGeneration: Set by the operator after processing a generation

If observedGeneration < metadata.generation, the operator has not yet processed the latest spec change.

# Check for pending changes
kubectl -n netbox get netboxenterprise netbox \
  -o jsonpath='gen={.metadata.generation} observed={.status.observedGeneration}'

Reconciliation Events

The operator publishes Kubernetes events for each reconciliation:

# All operator events
kubectl -n netbox get events --field-selector source=nbe-operator

# Watch events in real time
kubectl -n netbox get events -w --field-selector source=nbe-operator

Event types include:

  • Normal: Resource applied successfully, reconciliation complete
  • Warning: Readiness check failed, reconciliation error

See Also

On this page