tailscale/tailscale · error
--hardware-attestation cannot be used with portable state st
Error message
--hardware-attestation cannot be used with portable state stores (kube:, arn:) because TPM-bound keys cannot be migrated between machines
What it means
canUseHardwareAttestation's second rule (tailscaled.go:994-1000): attestation keys are TPM-bound, so --hardware-attestation is refused when --state points at a portable store — any known provider prefix (kube:, arn:, etc., via store.HasKnownProviderPrefix) other than the TPM: prefix, per isPortableStore. The error is a deliberate fail-fast to prevent a state blob that could never be restored on different hardware.
Source
Thrown at cmd/tailscaled/tailscaled.go:998
case !args.encryptState.set:
policyEncrypt, _ := policyclient.Get().GetBoolean(pkey.EncryptState, false)
if err := canEncryptState(); policyEncrypt && err == nil {
args.encryptState.v = true
}
}
}
// canUseHardwareAttestation returns an error if hardware attestation can't be
// enabled, either due to availability or compatibility with other settings.
func canUseHardwareAttestation() error {
if _, err := key.NewEmptyHardwareAttestationKey(); err == key.ErrUnsupported {
return errors.New("--hardware-attestation is not supported on this platform or in this build of tailscaled")
}
// Hardware attestation keys are TPM-bound and cannot be migrated between
// machines. Disable when using portable state stores like kube: or arn:
// where state may be loaded on a different machine.
if args.statepath != "" && isPortableStore(args.statepath) {
return errors.New("--hardware-attestation cannot be used with portable state stores (kube:, arn:) because TPM-bound keys cannot be migrated between machines")
}
return nil
}
// isPortableStore reports whether the given state path refers to a portable
// state store where state may be loaded on different machines.
// All stores apart from file store and TPM store are portable.
func isPortableStore(path string) bool {
if store.HasKnownProviderPrefix(path) && !strings.HasPrefix(path, store.TPMPrefix) {
return true
}
// In most cases Kubernetes Secret and AWS SSM stores would have been caught
// by the earlier check - but that check relies on those stores having been
// registered. This additional check is here to ensure that if we ever
// produce a faulty build that failed to register some store, users who
// upgraded to that don't get hardware keys generated.
if strings.HasPrefix(path, "kube:") || strings.HasPrefix(path, "arn:") {
return trueView on GitHub (pinned to cfe32b8be6)
Solutions
- Use a local file state (--state=/var/lib/tailscale/tailscaled.state) or the TPM store (TPM: prefix) together with --hardware-attestation
- If the portable store is required, drop --hardware-attestation
- Re-register the node on the new store rather than trying to combine both features
Example fix
# before tailscaled --hardware-attestation --state=kube:ts-ns/tailscaled-state # error: --hardware-attestation cannot be used with portable state stores (kube:, arn:) # after tailscaled --hardware-attestation --state=/var/lib/tailscale/tailscaled.state
Defensive patterns
Strategy: validation
Validate before calling
// Mirror canUseHardwareAttestation's rule before combining flags:
import "tailscale.com/ipn/store"
func attestationCompatible(statePath string) bool {
if statePath == "" {
return true
}
return !store.HasKnownProviderPrefix(statePath) || strings.HasPrefix(statePath, store.TPMPrefix)
}
if wantAttestation && !attestationCompatible(cfg.State) {
return errors.New("pick TPM: or file state, or drop --hardware-attestation")
} Try / catch
if err := start(); err != nil {
if strings.Contains(err.Error(), "portable state stores") {
// choose: local file + attestation, or provider store without attestation
return reconfigure(state: localFile, attestation: true)
}
return err
} Prevention
- Never template --state=kube:/arn: into hosts that also carry --hardware-attestation
- Encode the mutual exclusion in config management (schema validation, not runtime)
- Remember TPM: store is the only provider prefix compatible with attestation
When it happens
Trigger: `tailscaled --hardware-attestation --state=kube:tailnet/tailscaled` or `--state=arn:aws:ssm:...`; also Kubernetes deployments enabling attestation while storing state in Secrets/SSM per the sidecar pattern.
Common situations: K8s sidecar or AWS deployments copying state in object stores; users wanting 'HA' state via kube: then adding attestation; migration from file state to a provider store without re-evaluating attestation flags.
Related errors
- --encrypt-state can only be used with --state set to a local
- --hardware-attestation is not supported on this platform or
- --encrypt-state is not supported on this device or a TPM is
- this build does not support TPM integration
- failed to update tailscaled config: %w
AI-assisted analysis of tailscale/tailscale@cfe32b8be6 (2026-08-15).
Data as JSON: /api/errors/d3bfb6929b6c23e9.
Report an issue: GitHub.