better/compute

Troubleshooting bcd

Symptom, cause and fix for the ways bcd fails on real hosts - joins, pins and TLS, the clock, renewals, revocation, sections stuck progressing, GPU holds, nvidia-smi, KVM and the helper socket - with the doctor check that detects each.

This page maps what you see on a host (a log line, a refusal, a section that never converges) to its cause and fix, drawn from the error strings in daemon/. Each entry names the bcd doctor check that detects it, where one does. What happens to the fleet when a gateway, the database or the network fails is on failure modes; this page is about one host.

Where these come from

Most entries come from unit tests against fakes. The GPU, unprivileged-node and section-logging entries come from running bcd on a real RTX 5090 under WSL 2 (tests/e2e/bcd-host-real.test.ts; f84f1f9, da60bbd). The KVM, libvirt, cloud-hypervisor and Kubernetes entries have not been seen on a real host yet.

Where to look first

sudo bcd doctor --config /etc/bcd/config.toml   # one line per check; exit 1 when one fails
sudo bcd status                                 # the running agent: session, sections, certificate
journalctl -u bcd.service -e                    # bcd's JSON log lines
journalctl -u bcd-helper.service -e             # on a node installed with --unprivileged

bcd status lists every NodeSpec section with its state, reason and message. Since da60bbd the node's own log says the same: a section that cannot converge logs section retrying (WARN) with the error on every pass, about every 30 seconds, and one that failed for good logs section failed (ERROR) with its reason (AllocationFailed, InstanceFailed, UpdateFailed or ConvergeFailed). Before that commit an operator on the host saw only PROGRESSING.

{"level":"WARN","msg":"section retrying","section":"devices","generation":42,"error":"the slice of alloc_01k…: start bcd-alloc-alloc_01k….slice: …"}

Session problems log at INFO as session ended; redialing with the reason and the back-off.

bcd will not start

SymptomCauseFixDoctor
bcd node: config /etc/bcd/config.toml: line N: ...A key, section or value the strict parser refusesFix the lineconfig (fail)
dev_insecure_h2c is only accepted by fakehw development buildsA development setting in a host buildRemove dev_insecure_h2c and use an https:// gatewayconfig passes; the agent refuses
agent: this node has not joined (run bcd join)No identity.json in data_dirJoinidentity (warn)
agent: the node key does not match the node certificate; join again: ...node.key and identity.json belong to different keys; with a TPM key, "(was the TPM cleared?)"bcd join --reset --yes, then join; see TPM cleared or unavailableidentity (fail)
session: the journal belongs to node X, not YA journal.db left by another identity, e.g. after copying identity.json by handbcd join --reset --yes moves the journal aside with the identity (b4fcc2e)none
[kubernetes] gpu_api must be "device-plugin" or "dra"Any other valueFix itnone
[kubernetes] allowed_clusters: "..." is not https://host[:port]#sha256:<64 hex>A malformed entryFix it; no path, a 64-digit hex CA digestkubernetes (fail)
bcd site: this data directory holds a node identity; join with a site token (bcd join --role site)bcd site on a node's data directoryJoin a site agent separatelynone
bcd status: the node agent is not running (no socket at /run/bcd/bcd.sock)The agent is not running, or socket_path is elsewhereStart bcd.service; pass --socketnone

Joining

Server-side refusals are deliberately opaque: every refused join is ACCESS_DENIED "Join refused", and the reason is recorded as a security.join.refused event. Read the reasons in refusals. A refused join is not retried and does not consume the token.

Symptom (bcd join: ...)CauseFix
no join token: set BCD_JOIN_TOKEN or pass the token on standard input (exit 2)Nothing in the variable or on standard inputprintf '%s\n' "$TOKEN" | sudo bcd join; sudo drops the environment unless -E
that is not a join token (bcjt_...) (exit 2)The input is not bcjt_<id>.<secret>Pass the token itself
... is readable by others (mode 0644): make it 0600 (a Kubernetes Secret: defaultMode 0400)--token-file others can readchmod 0600, or defaultMode: 0400
the JoinService encryption key: open /etc/bcd/join-encryption.jwk: no such file or directoryThe key file is missing and no --server was givenRerun the install script, or add --server https://...; see the join key and its fallback
the JoinService encryption key does not match join_encryption_key_pinThe file or well-known key is another deployment's, or rotatedReinstall with the current bundle's pins and key
pki: join response rejected: bad signatureThe response was not signed by the key join_signing_key_pin names: a wrong pin, or a gateway answering for another control planeCheck the pin against the bundle (bootstrapPins)
pki: join response rejected: response is for another CSRThe answer was not for this join's CSRRetry; persistent, suspect the path to the gateway
agent: join: ... Join refusedJoinService refused itRead security.join.refused for the reason
log join failed, retryingThe gateway is unreachable, busy (RESOURCE_EXHAUSTED) or timing out; bcd retries up to 20 timesCheck gateway_url and the network; doctor gateway
log join is pending approvalThe pool requires approvalApprove the join request; bcd asks again by itself
this machine already joined as X, not Y--node-id names another node than the identity on diskUse the right node id, or --reset --yes
joining without config.toml needs --serving-ca-pin, --join-signing-pin and --join-encryption-pin from the install--server bootstrap on a host build without the three pinsPass all three
the control plane's well-known document does not match --serving-ca-pin: refusing to joinThe published pin differs from the install'sTreat it as an attack or a stale pin; do not drop the flag
the control plane does not publish its gateway (well-known gateway.url); install /etc/bcd/config.toml insteadThe server's options set no gateway.urlInstall a config.toml, or configure gateway on the server
the TPM: open /dev/tpmrm0: ...--attestor tpm without a usable TPMRun as root on a host with the kernel resource manager
already joined as X but the node cannot connectThe identity loads but its certificate expired, was revoked or is refused at renewalbcd join --reset --yes as root, then join; see joining again with --reset

Doctor: gateway fails when the gateway is not reachable over TCP within 5 seconds; identity warns before the first join.

Pins and TLS

bcd trusts no system root for the gateway: the chain must contain a CA whose SPKI equals serving_ca_spki_pin, and the leaf must name the host. These appear as the reason of session ended; redialing, and as the failure of bcd join.

SymptomCauseFix
transport: the gateway chain does not include the pinned serving CAThe gateway's serving certificate is from another CA, a TLS-inspecting proxy answered, or the pin is wrongIssue the gateway certificate from the bundle's serving CA (pki issue-gateway); behind an inspecting proxy, use the WebSocket fallback
transport: gateway certificate: x509: certificate is valid for A, not Bgateway_url names an IP address or another name than the certificateSet gateway_server_name to the certificate's name
transport: gateway certificate: x509: certificate has expired or is not yet validThe gateway certificate expired, or this host's clock is wrongRenew the gateway certificate; check doctor clock
transport: the WebSocket endpoint's certificate is neither the pinned gateway's nor the configured proxy'sAn inspecting proxy whose CA is not [websocket] proxy_ca_filePoint proxy_ca_file at the proxy's CA
transport: gateway proof rejected: ...Over the fallback, the gateway's in-band proof does not chain to the pinned serving CA, or names another audienceCheck [websocket] audience and the gateway's certificate
the WebSocket fallback needs command-signing roots (every command and spec signed)[websocket] url on a node whose trust bundle has no command-signing rootsConfigure pki.commandSigning on the server, or drop [websocket]

Doctor: none checks TLS; gateway tests TCP only.

The clock

Certificates, signed intents, exec tickets and renewal proofs all compare times. Since e2a0cf0 doctor asks the kernel (adjtimex) whether the clock is synchronized.

SymptomCauseFixDoctor
the kernel reports the clock unsynchronized: run an NTP client (systemd-timesyncd or chrony); ...No NTP client keeps the clockEnable systemd-timesyncd or chronyclock (fail)
log protocol: server time went backwards; refusing the sessionA Welcome more than 5 minutes before bcd's server time mark: a gateway with a wrong clock, or a replayFix the gateway's clock; bcd drops each such session and redialsnone
renewal failed ending Renew refused, with security.renew.refused reason client time outside the windowclient_time more than 60 seconds from server timeSynchronize the clockclock

bcd evaluates not_after through the offset the last Welcome gave it, so its own clock matters for TLS and proofs, not for command deadlines. See clock skew.

Renewals, expiry and revocation

bcd renews at 60% of the certificate's life. A failed renewal logs renewal failed (WARN) with the error, the attempt and expiresIn, and retries with back-off from 30 seconds to 10 minutes (or when the gateway's pushback says).

SymptomCauseFixDoctor
renewal failed with an error ending Renew refusedJoinService refused it; the reason is a security.renew.refused eventRead the event (refused renewals)none
renewal: the certificate's node key is in the TPM, which is unavailable: ...The TPM, or under [helper] the helper that holds it, cannot be reachedRestore /dev/tpmrm0 or start bcd-helper.service; the next retry renewsnode key
renewal: this TPM's attestation key is not the one the certificate names ...Another TPM, or its endorsement seed changedJoin again: bcd join --reset --yes --attestor tpmnone
the certificate expired at ...; join again as root: bcd join --reset --yes, then bcd joinRenewal kept failing until expiry, or the machine was offJoin againidentity (fail)
log certificate revoked, then bcd node: agent: this node's certificate was revoked (exit 1)The control plane revoked the certificate (nodes.revokeIdentity, nodes.delete, a cloned identity)systemd restarts bcd, but the gateway refuses the serial at the handshake. Join again with a new token, --reset --yesidentity stays ok until expiry

See certificate expiry, certificate revocation and renewal.

Sections stuck progressing

A section is PROGRESSING with reason Retrying while its converger returns an error that is not terminal, and the spec is not observed meanwhile. The log line section retrying carries the error. Common ones:

Error in section retryingCauseFixDoctor
helper: dial unix /run/bcd-helper/helper.sock: connect: no such file or directorybcd helper is not runningsystemctl start bcd-helper.service; see why it stopped belowhelper
helper: not allowedThe helper refused the connectionSee helper socket refusalshelper
helper: protocol version N, not M or protocol version N; this helper speaks MThe node and the helper are different buildsRestart both units after replacing the binarynone
the slice of ...: start bcd-alloc-....slice: ... or set the limits of ...systemctl refused the hold's slice: not root, or systemd is not initRun bcd node as root or under [helper]; doctor systemdsystemd, cgroups
the slice of ...: enable hugetlb under /sys/fs/cgroup... or limit 2MB hugepages of ...A hold with hugepage limits, and bcd cannot write cgroupfs: no hugetlb controller, or /sys/fs/cgroup is read-only for the process (the install unit sets ProtectControlGroups=yes for bcd.service; bcd-helper.service does not)Enable hugetlb; check which process writes the slice (the helper, under [helper])cgroups (warn)
... /etc/cdi/...: read-only file system/etc/cdi did not exist when bcd.service started: the unit's ReadWritePaths=-/etc/cdi skips a missing path, and ProtectSystem=strict leaves /etc read-onlymkdir -m 0755 /etc/cdi (the install script does it) and restart bcd.servicenone

A host section that fails a unit keeps going with the others; see status and failures.

GPU holds

The devices converger fails an assignment it can never bind at once, so the control plane re-places it instead of waiting. The reason travels in AllocationFailed.

ReasonMessageCauseFixDoctor
node-unprivilegedbcd node runs without root and without [helper]: it cannot write /etc/cdi or create hold slices (run bcd node as root, or under bcd helper)A host build of bcd node running as an ordinary user with no [helper] (da60bbd). Such a node also claims no cdi, devices.limits or binding.kubernetes capability in Hello, so capacity should not place thereRun it as root, or install with --unprivilegedgpu-binding (warn)
device-missingThe devices it namesA GPU, MIG instance or slot the assignment names is not on the hostCheck bcd status GPUs and the inventorygpu-bus, gpus
overlapThe conflicting holdA device is already held by another allocation, or passed to a VMRelease the other holdnone
resources-exhaustedWhat does not fitThe hold's limits do not fit what the host has leftRe-placed by the control planehugepages, cgroups
unsupported-bindingbinding ... is not supportedA binding this bcd does not implementUpgrade bcdnone

Before da60bbd an unprivileged node retried the hold every 30 seconds on systemd's "Access denied" for the slice, logging nothing above debug, and the reservation stayed binding forever. A log warning NVIDIA's CDI spec does not name held devices means the vendor spec predates the GPUs; the container-toolkit unit regenerates it.

nvidia-smi errors

bcd reads GPUs through nvidia-smi. Doctor's gpus check fails with the tool's error and, since f84f1f9, a hint for the failures seen on real hosts:

Error containsHint bcd addsFix
Driver/library version mismatchThe kernel module and user-space libraries differ, usually after a driver upgradeReboot, or unload and reload the nvidia modules
couldn't communicate with the NVIDIA driverThe nvidia module is not loadedmodprobe nvidia; under Secure Boot the module must be signed by an enrolled key (see dmesg)
No devices were foundThe driver sees no GPUCompare lspci -d 10de: with dmesg (RmInitAdapter failed, Xid 79)
a timeout or signal: killednvidia-smi did not answer within a minute: a GPU may be wedged (Xid 79 or 119)gpu.reset or a reboot

Related checks around the GPUs:

Doctor checkSymptomFix
nvml (warn)nvml: nvidia-smi not found (no NVIDIA driver on this host)Install the driver, or ignore on a host without GPUs
nouveau (fail)nouveau is loaded and keeps the NVIDIA driver off the GPUsBlacklist it, rebuild the initramfs, reboot
gpu-bus (fail)More GPUs bound to nvidia than nvidia-smi sees: one fell off the busgpu.reset or a reboot; check dmesg
gpu-bus (warn)GPUs on the bus with no driver bound, or no driver answeringInstall the driver (nvidia-driver)
fabric-manager (fail)NVSwitches but nvidia-fabricmanager inactive: CUDA fails on every GPUStart it (fabric-manager)
persistence (warn)Persistence mode offEnable nvidia-persistenced
gpu (fail)row remapping failed (RMA) or uncorrectable ECC errorsTake the GPU out of service
gpu-host (warn)A WSL 2 paravirtual GPU (/dev/dxg)Expected: no VFIO, gpu.reset, MIG or DCGM there

KVM and instances

SymptomCauseFixDoctor
the CPU supports virtualization but /dev/kvm is missing: load kvm_intel or kvm_amd (modprobe), and check it is not blacklistedThe module is not loadedmodprobe kvm_intel or kvm_amdkvm (warn)
this is a VM without nested virtualization: ...bcd runs in a VMEnable nested virtualization on its hostkvm (warn)
no VT-x/AMD-V flags: enable virtualization in the firmware setupDisabled in firmwareEnable Intel VT-x or AMD SVMkvm (warn)
A non-empty instances section fails with this host runs no instance runtimeNeither virsh nor cloud-hypervisor on PATH, and no runtime pluginInstall a runtimelibvirt (warn)
Instances without disks, user data or network policyqemu-img, genisoimage/xorriso or nft missingInstall themqemu-img, seed, nftables (fail)
cloud-hypervisor instances do not bootNo /usr/share/cloud-hypervisor/CLOUDHV.fdInstall the firmwarecloud-hypervisor (warn)
A cloud-hypervisor VMM cannot open /dev/kvmEach VMM runs as its own uid with DevicePolicy=closed and DeviceAllow=/dev/kvm rw; bcd adds the kvm group to the unit only when a kvm group existsMake /dev/kvm group kvm (the usual udev rule)none
GPUs cannot be passed to instancesNo IOMMU, vfio-pci not loaded, or a GPU shares its IOMMU group with a NIC or diskEnable the IOMMU; modprobe vfio-pci; move the GPU or enable ACSiommu, vfio, iommu-groups
Under [helper], libvirt failsThe bcd account is in the libvirt group, dynamic_ownership = 0 is missing, or QEMU's user is not in the bcd groupRerun the install script with --unprivileged; see upgrading an unprivileged libvirt hostlibvirt (fail)
Under [helper], no instance gets a NIC[helper] instance_bridges is emptySet the bridgescloud-hypervisor (warn)

Helper socket refusals

On a node installed with --unprivileged, every privileged operation goes to bcd helper. The node only sees the answer; the helper's log says why.

Node seesHelper logsCauseFix
helper: not allowedhelper: connection refused reason uid N gid M is not bcdThe caller is neither root nor the [helper] user with its primary groupRun bcd node as that user (the unprivileged.conf drop-in)
helper: not allowedreason the peer does not run an installed bcd binarybcd node runs from a path other than [update] entrypoint, a slot binary or the helper's own binaryRun the installed binary
helper: malformed requesthelper: malformed requestA request with unknown fields: a different buildRestart both units on the same binary
helper: dial unix ...: no such file or directory(nothing)The helper is not runningjournalctl -u bcd-helper.service; it may have refused to start (below)

bcd helper refuses to start, and logs to standard error, when:

MessageCauseFixDoctor
/usr/local/lib must be a directory root owns that no one else can write (the helper socket lives there)A directory on the way to the slots, the socket or the entrypoint is group-writable, such as root:staff 2775 on older Debian (c139b9c, 9e71b1f)chmod g-w that directoryhelper (fail)
/etc/bcd/config.toml must be a regular file root owns that no one else can writeThe config was given to bcd, made group-writable or replaced by a symlink (3fa9021)chown root /etc/bcd/config.toml, chmod 0640 with group bcdnone
helper.user: user: unknown user bcdThe account does not existRerun the install script, which creates itnone
runs as rootStarted without rootRun it from bcd-helper.servicenone

Host units through the helper fail when the helper cannot bind them to a node:

SymptomCauseFixDoctor
helper warns host units bind to the node the first signed spec names (trust on first use)The node joined before bcd join kept root's signed join recordRe-join as root to pin ithelper (warn)
root's join record names X, but this machine's identity is YThe identity changed without a root joinRe-run the join as roothelper (fail)
bcd helper applies host units unsigned unless the node itself presents a trust bundle ...Neither command_root_sha256 nor the join record names command-signing rootsPin them with --command-root-sha256helper (fail)
bcd is in the tss group: it can send the TPM any command ...An install from between 0ba2a79 and 8870b8dgpasswd -d bcd tss, restart bcd.servicetpm (fail)

See privilege separation and signed specs.

Kubernetes and self-update

SymptomCauseFixDoctor
joining a Kubernetes cluster needs [kubernetes] allowed_clusters in bcd's config ...A k8s-join unit with no allow-list (9f860ad)Add the cluster as https://host:port#sha256:<CA digest>kubernetes (fail on a joined node)
joined to ... but the kubelet is not runningThe kubelet stoppedStart itkubelet (fail)
GPUs withdrawn with ForeignGpuExposureAnother path hands out GPUsSee GPU isolationgpu-isolation (fail)
no [update] release_pins: self-update is refused (reinstall to upgrade)No release keys pinnedRegenerate the script with --release-pinsupdate (warn)
bcd X failed (...); bcd update --clear-failed lets a rollout retry itA version did not confirm and was swapped backFix the cause, then bcd update --clear-failedupdate (warn)
/usr/local/bin/bcd does not link to .../current: bcd.service would not start the current slotThe install script replaced the entrypoint link with a fileThe next self-update adopts it; or relinkslots (fail)

See self-update and bcd on Kubernetes.

Was this page helpful?

Better Compute is created by Sean Filimon

Last updated

On this page