Skip to content

CRD reference

Field tables are rendered from the installed CRD schemas (deploy/crds/*.yaml); the worked examples live in the how-to guides. Admission rules beyond the schema (CEL) are summarised in Validation ; condition reasons in Reasons .

Tailnet

tailnets.tsunaro.io · scope Cluster · short name tn · v1alpha1

Tailnet is a cluster-scoped binding to one control server.

Spec

TailnetSpec describes a control server and how the operator authenticates to it.

FieldTypeRequiredDescription
apiKeyLifetimestringnoLifetime of the self-minted working API key. Rotated at half-life. Default 720h. Max length 32.
apiSecretRefobjectyesBootstrap API key, read only. The operator mints its own rotating working key from it and never writes back here — so this may live in GitOps without a rotation fight.
apiSecretRef.keystringnoDefault apiKey.
apiSecretRef.namestringyes
apiUrlstringnoIn-cluster URL for the REST API if it differs from Server. Empty → Server.
authKeyLifetimestringnoLifetime of the reusable ephemeral pre-auth keys handed to agents. Rotated at half-life. Default 168h.
defaultsobjectnoDefaults inherited by ingress resources that do not select their own TLS source.
defaults.tlsobjectnoTLSDefaults supplies an issuer for HTTP ingresses without an explicit TLS source. Secrets are namespace-local and cannot be selected as a Tailnet-wide default.
defaults.tls.issuerRefobjectnoTLSIssuerReference identifies a cert-manager issuer without requiring its API dependency. Issuer references resolve in the ingress namespace; ClusterIssuer references are cluster-scoped.
defaults.tls.issuerRef.groupstringnoOnly cert-manager.io issuers are supported. Default cert-manager.io. One of cert-manager.io.
defaults.tls.issuerRef.kindstringnoDefault Issuer. One of Issuer, ClusterIssuer.
defaults.tls.issuerRef.namestringyesName of the issuer. Max length 253.
dnsobjectnoDNS records rendered from egress aliases and ingress names. Off when empty.
dns.hostsConfigMapstringnoName of the ConfigMap (operator namespace, key “hosts”) that receives the rendered hosts file. Empty → the feature is off and a previously rendered ConfigMap is removed.
dns.ingressAliasTemplatestringnoOptional template for a shorter DNS alias of an HTTP expose that opts in with alias: true. Same placeholders as ingressNameTemplate; {name} is the expose’s effective label (dnsLabel or the CR name). The operator requires the alias to end in the SAME literal zone as ingressNameTemplate, because only that zone is guaranteed to resolve over the tailnet. Empty → aliases are disabled. The alias becomes an additional certificate SAN and SNI route; a name claimed by two CRs is resolved oldest-first and the loser reports DNSNameConflict. Max length 253.
dns.ingressNameTemplatestringnoTemplate for a TailnetIngress’s DNS name; {name} is the CR name, {namespace} its namespace. Keep the result one label below your gateway’s wildcard certificate, e.g. “{name}-{namespace}.prod-1.tailnet.example.internal”. Empty → ingresses get no name. Max length 253.
headscaleobjectnoExternal Headscale metadata; compatibility is checked against the API, not the version hint.
headscale.refstringnoReserved for an operator-managed Headscale. Must remain empty until managed mode is supported.
headscale.versionstringnoOptional version hint for diagnostics, e.g. “0.29.3”. Does not bypass capability checks.
hostnameTemplatestringnoTemplate for a TailnetEgress’s tailnet hostname when it sets none. {name} is the CR name. Default egress-{name}.
ingressHostnameTemplatestringnoTemplate for a TailnetIngress’s tailnet (MagicDNS) hostname when it sets none. {name} is the CR name. Default ingress-{name}.
ingressTagTemplatestringnoTemplate for a TailnetIngress’s tag when it sets none. {name} is the CR name. The tag IS the authorization — its grants (dst = this tag) decide which peers may reach it. Default tag:ingress-{name}.
policyobjectnoPolicy generation from CRs. Off when baseConfigMap is empty.
policy.baseConfigMapstringnoConfigMap (operator namespace, key “policy”) holding the base policy in HuJSON. Empty → the feature is off and nothing is rendered or written.
policy.ingressSourceAllowlist[][]stringnoGlob patterns a TailnetIngress.spec.allowFrom entry must match, e.g. [“group:*”]. Ingress CRs live in tenant namespaces, so this is the admin’s veto over who tenants may grant access to. Empty → no ingress grants are generated (only the tag is declared).
policy.modestringnoDryRun renders into renderedConfigMap only; Apply also writes the control server when the live policy differs. Start with DryRun, compare, then switch. Default DryRun. One of DryRun, Apply.
policy.renderedConfigMapstringnoConfigMap (operator namespace, key “policy”) that receives the rendered policy for review. Empty → “-rendered”.
policy.routeApprovers[][]stringnoApprovers for the generated autoApprovers.routes entries (one /32 per egress target), i.e. the tag your subnet routers carry, e.g. [“tag:fabric”]. Required when on.
policy.tagOwners[][]stringnoOwners of the generated tags (tagOwners value), e.g. [“group:operator”]. Required when on.
proxyobjectnoProxySpec are the defaults for the per-CR agent pods this Tailnet spawns. A TailnetEgress may override image and resources.
proxy.imagestringnoContainer image carrying the tailnet-agent binary. Empty → the operator’s own image, so a single pushed image serves both control and data plane.
proxy.imagePullSecrets[][]stringnoNames of existing image-pull Secrets in each agent pod’s namespace. Omitted inherits the operator defaults; an explicit empty list disables those defaults. Duplicate names are collapsed when building pods because Kubernetes merges these references by name.
proxy.mtuintegernotsnet outer path MTU. 1200 leaves headroom for a ~1320-byte outer path (typical cloud CNI overlay). Default 1200. Range 576–1500. Format int32.
proxy.probeIntervalstringnoHow often the agent probes each target for reachability. “0s” disables probing. Default 5m.
proxy.resourcesobjectnoResourceRequirements describes the compute resource requirements.
proxy.resources.claims[][]objectnoClaims lists the names of resources, defined in spec.resourceClaims, that are used by this container. This field depends on the DynamicResourceAllocation feature gate. This field is immutable. It can only be set for containers.
proxy.resources.claims[].namestringyesName must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container.
proxy.resources.claims[].requeststringnoRequest is the name chosen for a request in the referenced claim. If empty, everything from the claim is made available, otherwise only the result of this request.
proxy.resources.limitsobjectnoLimits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
proxy.resources.requestsobjectnoRequests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
reservedNetworks[][]objectnoAddress space TailnetSite prefixes must not overlap (node prefix, every cluster’s LAN/pod/ service/LB ranges on this control server). Only consulted for TailnetSite CRs. Items ≤ 64.
reservedNetworks[].namestringyesMax length 63.
reservedNetworks[].prefixes[][]stringyesItems 1–32.
serverstringyesLogin/control URL, becomes the agent’s ControlURL (e.g. https://ts.example.com ).
tagTemplatestringnoTemplate for a TailnetEgress’s tag when it sets none. {name} is the CR name. The tag IS the authorization — its grants decide which routes the node sees. Default tag:egress-{name}.

Status

TailnetStatus is the observed state of the control server binding.

FieldTypeRequiredDescription
apiKeyExpiresAtstringnoExpiry of the current working API key, drives the ApiKeyValid condition and its metric. Format date-time.
conditions[][]objectnoConditions: ApiReachable, ApiKeyValid, HeadscaleCompatible, PolicyMode, SplitDNS, UsersUnique, PolicyRendered, PolicyApplied.
conditions[].lastTransitionTimestringyeslastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. Format date-time.
conditions[].messagestringyesmessage is a human readable message indicating details about the transition. This may be an empty string. Max length 32768.
conditions[].observedGenerationintegernoobservedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. Range ≥ 0. Format int64.
conditions[].reasonstringyesreason contains a programmatic identifier indicating the reason for the condition’s last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. Max length 1024.
conditions[].statusstringyesstatus of the condition, one of True, False, Unknown. One of True, False, Unknown.
conditions[].typestringyestype of condition in CamelCase or in foo.example.com/CamelCase. Max length 316.
observedGenerationintegernoFormat int64.
policyobjectnoPolicy rendering/apply state; conditions PolicyRendered and PolicyApplied carry the reasons.
policy.appliedAtstringnoFormat date-time.
policy.appliedHashstringnosha256 of the policy last written to (or found identical on) the control server.
policy.generatedobjectnoPolicyGenerated counts what the operator added to the base policy.
policy.generated.grantsintegeryes
policy.generated.routesintegeryes
policy.generated.tagsintegeryes
policy.renderedHashstringnosha256 of the canonical rendered policy.
userConflictsintegernoHeadscale user names that more than one user carries. Such a collision makes Headscale reject every policy write (“multiple users found”); condition UsersUnique reports it. Only the count is published, never a name. Format int32.
versionstringnoReported control-server version, for the operator log and the printcolumn.

TailnetEgress

tailnetegresses.tsunaro.io · scope Namespaced · short name tneg · v1alpha1

TailnetEgress is a per-identity egress into a Tailnet. It lives in the operator’s namespace; a NetworkPolicy decides which namespaces may use it.

Spec

TailnetEgressSpec is the desired egress into a tailnet for one identity.

FieldTypeRequiredDescription
consumerNamespaces[][]stringyesNamespaces whose pods may reach this proxy’s Services (NetworkPolicy ingress). Items ≥ 1.
hostnamestringnoExplicit tailnet hostname. Empty → rendered from the Tailnet’s hostnameTemplate.
imagestringnoOverride the Tailnet’s default agent image.
resourcesobjectnoOverride the Tailnet’s default agent resources.
resources.claims[][]objectnoClaims lists the names of resources, defined in spec.resourceClaims, that are used by this container. This field depends on the DynamicResourceAllocation feature gate. This field is immutable. It can only be set for containers.
resources.claims[].namestringyesName must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container.
resources.claims[].requeststringnoRequest is the name chosen for a request in the referenced claim. If empty, everything from the claim is made available, otherwise only the result of this request.
resources.limitsobjectnoLimits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
resources.requestsobjectnoRequests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
tagstringnoExplicit tailnet tag. Empty → rendered from the Tailnet’s tagTemplate. The tag is the authorization: its grants in the tailnet policy decide which routes this node receives.
tailnetRefobjectyesTailnetRef selects a cluster-scoped Tailnet by name.
tailnetRef.namestringyes
targets[][]objectyesItems 1–50.
targets[].addressstringyesIPv4/IPv6 address or lowercase MagicDNS name, not a CIDR, URL or host:port. Max length 253.
targets[].aliases[][]stringnoExtra DNS names that resolve to this target’s Service ClusterIP, rendered into the Tailnet’s dns.hostsConfigMap. Use them when the far end routes by name (TLS SNI, HTTP Host): a consumer dialling the alias sends the real hostname, and the L4 tunnel passes it through untouched.
targets[].namestringyesDNS-1035 label; used verbatim as the Service name, so consumers dial .:. Start with a letter for compatibility with Kubernetes 1.31 Service admission. Max length 63.
targets[].portintegeryesRange 1–65535. Format int32.

Status

TailnetEgressStatus is the observed state.

FieldTypeRequiredDescription
authKeyExpiresAtstringnoExpiry of the reusable pre-auth key currently in the agent’s Secret. Format date-time.
conditions[][]objectnoConditions: TagKnown, Registered, RoutesGranted, TargetsReachable, Ready.
conditions[].lastTransitionTimestringyeslastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. Format date-time.
conditions[].messagestringyesmessage is a human readable message indicating details about the transition. This may be an empty string. Max length 32768.
conditions[].observedGenerationintegernoobservedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. Range ≥ 0. Format int64.
conditions[].reasonstringyesreason contains a programmatic identifier indicating the reason for the condition’s last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. Max length 1024.
conditions[].statusstringyesstatus of the condition, one of True, False, Unknown. One of True, False, Unknown.
conditions[].typestringyestype of condition in CamelCase or in foo.example.com/CamelCase. Max length 316.
hostnamestringno
nodeIdstringno
observedGenerationintegernoFormat int64.
tagstringnoEffective tag (spec.tag or the Tailnet template) — what the policy and the node carry.
tailnetIps[][]stringno
targets[][]objectno
targets[].listenPortintegeryesLocal port the agent listens on and the Service forwards to. Format int32.
targets[].namestringyes
targets[].presentbooleanyesTrue once that route is present in the pod’s netmap.
targets[].reachablebooleanyesTrue once the agent last dialled the target successfully.
targets[].reasonstringnoWhen the route is missing: NotAdvertised, NotApproved or NoGrant. Empty when present.
targets[].routestringyesThe /32 or /128 this target needs in the netmap.

TailnetGateway

tailnetgateways.tsunaro.io · scope Cluster · short name tngw · v1alpha1

TailnetGateway describes a persistent kernel router in the operator namespace. Suspended by default; suspend=false explicitly opts into the fresh writer, not adoption of existing resources.

Spec

TailnetGatewaySpec is an experimental API contract, suspended by default. Explicit suspend=false opts into the fresh writer, not adoption. Deletion retains everything unless deletionPolicy=Delete explicitly consents to tearing down the operator’s own fresh resources; adoption never implies consent to replace a pod, reset state, rename nodes or remove stale nodes.

FieldTypeRequiredDescription
adoptionobjectnoGatewayAdoption binds the inventoried node and resources. Admission cannot establish that a Secret belongs to this node or that a live template is identical.
adoption.nodeIdstringyesExpected numeric Headscale node ID; never inferred from hostname alone. Max length 20.
adoption.objects[][]objectyesItems 2–16.
adoption.objects[].apiVersionstringyesMax length 64.
adoption.objects[].kindstringyesMax length 32.
adoption.objects[].namestringyesGatewayName is a namespace-local Kubernetes object name. Max length 253.
adoption.objects[].uidstringyesMax length 128.
authKeySecretRefobjectnoOmitted selects persistent key minting in a future controller, never an ephemeral key. An adopted StatefulSet must retain its existing auth source.
authKeySecretRef.keystringnoDefault TS_AUTHKEY. Max length 253.
authKeySecretRef.namestringyesGatewayName is a namespace-local Kubernetes object name. Max length 253.
deletionPolicystringnoRetain leaves every resource, node and key in place when the CR is deleted. Delete records consent through a finalizer: the operator removes only the fresh resources it created (matched by recorded UID or its own gateway-uid label), the Headscale node and the minted bootstrap key, in a fixed order and fail-closed. Adopted or user-supplied objects are never deleted. Default Retain. One of Retain, Delete.
dnsobjectyesGatewayDNS leaves the cluster CoreDNS Corefile and Headscale split-DNS config externally managed. Dynamic hosts remain owned by the existing DNS reconciler.
dns.extraZones[][]objectnoItems ≤ 16.
dns.extraZones[].upstreams[][]stringyesItems 1–8.
dns.extraZones[].zonestringyesGatewayName is a namespace-local Kubernetes object name. Max length 253.
dns.imagestringyesGatewayImage requires a version tag or immutable digest. Version tags permit identical adoption of existing pod templates; runtime must not rewrite them. Kernel Tailscale, CoreDNS and socat use separate images, not the agent image. Max length 512.
dns.serviceClusterIPstringyesGatewayIP is an unzoned unicast IP literal, not a hostname or a prefix. Max length 45.
dns.serviceNamestringyesExplicit name permits adoption of an existing tailnet-dns Service. Max length 63.
dns.staticRecords[][]objectnoItems ≤ 128.
dns.staticRecords[].addressstringnoGatewayIP is an unzoned unicast IP literal, not a hostname or a prefix. Max length 45.
dns.staticRecords[].namestringyesRelative single label below dns.zone. Max length 63.
dns.staticRecords[].targetstringnoTarget FQDN of a CNAME. Must be equal to dns.zone or end with “.<dns.zone>” and must differ from this record’s own FQDN. Never resolved by the operator; CoreDNS answers from the authoritative zone (CNAME + target RRset when known, bare CNAME when the target is not yet in the hosts source). Max length 253.
dns.wildcardAddressstringnoOptional file-zone fallback, never a template plugin that shadows hosts. Max length 45.
dns.zonestringyesGatewayName is a namespace-local Kubernetes object name. Max length 253.
forwardImagestringnoGatewayImage requires a version tag or immutable digest. Version tags permit identical adoption of existing pod templates; runtime must not rewrite them. Kernel Tailscale, CoreDNS and socat use separate images, not the agent image. Max length 512.
forwards[][]objectnoItems ≤ 16.
forwards[].namestringyesMax length 50.
forwards[].portintegeryesDNS, health, readiness and metrics ports are reserved by the sidecar. Range 1–65535. Format int32.
forwards[].targetAddressstringyesIP literal avoids DNS/SNI ambiguity and arbitrary socat address expressions. Max length 45.
forwards[].targetPortintegeryesRange 1–65535. Format int32.
hostnamestringyesExplicit until a cluster-name convention is introduced; no inferred identity. Max length 63.
imagestringyesGatewayImage requires a version tag or immutable digest. Version tags permit identical adoption of existing pod templates; runtime must not rewrite them. Kernel Tailscale, CoreDNS and socat use separate images, not the agent image. Max length 512.
replicasintegernoDefault 1. Format int32.
routesobjectyesGatewayRoutes adds literal-IP egress destinations from this Tailnet in the operator namespace. DNS targets are explicitly skipped, not resolved.
routes.advertiseEgressTargetsbooleannoDefault true.
routes.lan[][]stringyesItems 1–128.
serviceobjectyesGatewayService preserves the gateway’s stable split-DNS LoadBalancer address.
service.loadBalancerClassstringnoGatewayClass is a Kubernetes qualified load-balancer class name. Max length 317.
service.loadBalancerIPstringyesGatewayIP is an unzoned unicast IP literal, not a hostname or a prefix. Max length 45.
service.poolLabelsobjectnoLabels used by the LoadBalancer IP-pool selector, not pod selectors.
service.tailnetLBCIDRstringyesGatewayPrefix is a canonical, non-default subnet route. Network ownership and reachability still require runtime validation against the selected Tailnet. Max length 49.
stateSecretRefobjectyesGatewayLocalReference is always resolved in the operator namespace.
stateSecretRef.namestringyesGatewayName is a namespace-local Kubernetes object name. Max length 253.
suspendbooleannoExplicit false opts into fresh resource creation; adoption remains unsupported. Default true.
tags[][]stringnoBootstrap tags do not authorize dropping existing tags on an adopted node. Default [tag:fabric]. Items 1–16.
tailnetRefobjectyesGatewayLocalReference is always resolved in the operator namespace.
tailnetRef.namestringyesGatewayName is a namespace-local Kubernetes object name. Max length 253.

Status

TailnetGatewayStatus records observations, not desired-state success claims.

FieldTypeRequiredDescription
conditions[][]objectnoRegistered, RoutesInSync, DNSReady, StaleNodes, ClusterDNSForward and Ready; fresh gateways with a verified completion receipt also report WorkloadCurrent and ServiceReady.
conditions[].lastTransitionTimestringyeslastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. Format date-time.
conditions[].messagestringyesmessage is a human readable message indicating details about the transition. This may be an empty string. Max length 32768.
conditions[].observedGenerationintegernoobservedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. Range ≥ 0. Format int64.
conditions[].reasonstringyesreason contains a programmatic identifier indicating the reason for the condition’s last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. Max length 1024.
conditions[].statusstringyesstatus of the condition, one of True, False, Unknown. One of True, False, Unknown.
conditions[].typestringyestype of condition in CamelCase or in foo.example.com/CamelCase. Max length 316.
ipv4stringno
nodeIdstringno
observedGenerationintegernoFormat int64.
routePlanobjectnoGatewayRouteStatus persists the quiet-period planner without process-local timers.
routePlan.appliedHashstringno
routePlan.pendingHashstringno
routePlan.pendingSincestringnoUTC RFC3339Nano; malformed timestamps cause the planner to restart its wait.
routesAdvertised[][]stringno
routesApproved[][]stringno
serviceIPstringno
staleNodes[][]stringnoIDs of stale nodes to report only, never delete automatically.

TailnetIngress

tailnetingresses.tsunaro.io · scope Namespaced · short name tning · v1alpha1

TailnetIngress exposes in-cluster L4 services on the tailnet as one tsnet node with a MagicDNS name. It lives in the tenant namespace; the tag’s grants decide which tailnet peers may reach it.

Spec

TailnetIngressSpec exposes in-cluster services through one tsnet node. L4 forwards TCP; HTTP mode terminates TLS and proxies HTTP or explicitly verified HTTPS to the selected backend.

FieldTypeRequiredDescription
allowFrom[][]stringyesPeers allowed to reach this node (policy grant src), e.g. [“group:team-a”]. Only used when the Tailnet generates policy; every entry must match Tailnet.spec.policy.ingressSourceAllowlist, otherwise the CR gets PolicyDenied and no grant is written. Items ≥ 1.
exposes[][]objectyesItems 1–50.
exposes[].aliasbooleannoRequest a shorter DNS alias for this HTTP expose, rendered from the Tailnet’s ingressAliasTemplate with the effective label (dnsLabel or the CR name). Requires ingressAliasTemplate to be set; otherwise the CR reports InvalidDNSName.
exposes[].backendobjectyesBackend is the in-cluster Service an Expose forwards to. Namespace defaults to the CR’s namespace; an explicit namespace can reference a shared gateway or another backend.
exposes[].backend.namespacestringnoNamespace of the Service. Empty → the CR’s namespace. Set it to funnel HTTPS through a shared gateway (e.g. Envoy in envoy-gateway-system) so TLS terminates there while the tailnet identity stays per app. Max length 63.
exposes[].backend.portintegeryesRange 1–65535. Format int32.
exposes[].backend.servicestringyesService name; in the CR’s own namespace unless Namespace is set. Max length 63.
exposes[].backend.tlsobjectnoVerified HTTPS to the backend. Omission preserves plaintext HTTP.
exposes[].backend.tls.caConfigMapRefobjectyesPublic CA bundle in the ingress CR namespace, even if the backend is elsewhere.
exposes[].backend.tls.caConfigMapRef.keystringyesMax length 253.
exposes[].backend.tls.caConfigMapRef.namestringyesMax length 253.
exposes[].backend.tls.serverNamestringyesDNS name verified against the backend certificate; does not change the dial address or HTTP Host. Max length 253.
exposes[].dnsLabelstringnoHTTP DNS label substituted for {name} in the Tailnet’s ingressNameTemplate. Omitted or empty uses the ingress-level name; does not change the expose identifier. Max length 63.
exposes[].modestringnoListener mode. Omitted means L4 forwarding; HTTP requests TLS termination. Default l4. One of l4, http. Max length 4.
exposes[].namestringyesDNS label; identifies the expose in status and in the agent config. Max length 63.
exposes[].portintegeryesPort the tailnet node listens on — what a tailnet peer dials on this node’s MagicDNS name/IP. Range 1–65535. Format int32.
exposes[].timeoutsobjectnoHTTP-only timeout overrides.
exposes[].timeouts.backendRequeststringnoWait for backend response headers after sending the request body. Defaults to 30s. Max length 32.
exposes[].timeouts.requeststringnoWhole-response deadline. Omitted means no deadline. Max length 32.
hostnamestringnoExplicit tailnet (MagicDNS) hostname. Empty → rendered from the Tailnet’s ingressHostnameTemplate.
imagestringnoOverride the Tailnet’s default agent image.
resourcesobjectnoOverride the Tailnet’s default agent resources.
resources.claims[][]objectnoClaims lists the names of resources, defined in spec.resourceClaims, that are used by this container. This field depends on the DynamicResourceAllocation feature gate. This field is immutable. It can only be set for containers.
resources.claims[].namestringyesName must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container.
resources.claims[].requeststringnoRequest is the name chosen for a request in the referenced claim. If empty, everything from the claim is made available, otherwise only the result of this request.
resources.limitsobjectnoLimits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
resources.requestsobjectnoRequests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
tagstringnoExplicit tailnet tag. Empty → rendered from the Tailnet’s ingressTagTemplate. The tag is the authorization: policy grants with dst = this tag decide which peers may reach the node.
tailnetRefobjectyesTailnetRef selects a cluster-scoped Tailnet by name.
tailnetRef.namestringyes
tlsobjectnoTLS source shared by the HTTP exposes. An omitted or empty source may use Tailnet defaults.
tls.issuerRefobjectnoTLSIssuerReference identifies a cert-manager issuer without requiring its API dependency. Issuer references resolve in the ingress namespace; ClusterIssuer references are cluster-scoped.
tls.issuerRef.groupstringnoOnly cert-manager.io issuers are supported. Default cert-manager.io. One of cert-manager.io.
tls.issuerRef.kindstringnoDefault Issuer. One of Issuer, ClusterIssuer.
tls.issuerRef.namestringyesName of the issuer. Max length 253.
tls.secretNamestringnoExisting TLS Secret in the ingress namespace. Max length 253.

Status

TailnetIngressStatus is the observed state.

FieldTypeRequiredDescription
authKeyExpiresAtstringnoExpiry of the reusable pre-auth key currently in the agent’s Secret. Format date-time.
conditions[][]objectnoConditions: TagKnown, Registered, BackendsReachable, TLSReady, Ready.
conditions[].lastTransitionTimestringyeslastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. Format date-time.
conditions[].messagestringyesmessage is a human readable message indicating details about the transition. This may be an empty string. Max length 32768.
conditions[].observedGenerationintegernoobservedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. Range ≥ 0. Format int64.
conditions[].reasonstringyesreason contains a programmatic identifier indicating the reason for the condition’s last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. Max length 1024.
conditions[].statusstringyesstatus of the condition, one of True, False, Unknown. One of True, False, Unknown.
conditions[].typestringyestype of condition in CamelCase or in foo.example.com/CamelCase. Max length 316.
dnsNamestringnoDNS name rendered from the Tailnet’s dns.ingressNameTemplate; resolves to TailnetIPs via the hosts ConfigMap. Empty when no template is set.
exposes[][]objectno
exposes[].backendstringyesThe in-cluster backend the agent dials, as service:port.
exposes[].dnsAliases[][]stringnoAdditional resolved DNS aliases for an HTTP expose; empty unless alias is requested and won.
exposes[].dnsNamestringnoResolved DNS name for an HTTP expose; empty for L4.
exposes[].listenPortintegeryesPort the tailnet node listens on for this expose. Format int32.
exposes[].namestringyes
exposes[].reachablebooleanyesBackend reachability. With backend TLS this requires a fresh verified handshake bound to the current configuration, CA bundle and unexpired certificate.
exposes[].reasonstringnoBounded backend or frontend failure reason; empty when ready.
exposes[].tlsReadybooleannoLocal TLS component readiness for an HTTP expose; omitted for L4. This does not establish remote tailnet reachability or client CA trust.
hostnamestringno
hosts[][]stringnoNames the agent actually serves: the HTTP exposes’ DNS names and their aliases, sorted. For a CR without HTTP exposes it is the node’s DNS name. Unlike dnsName (the node identity and first certificate name), every entry here answers.
nodeIdstringno
observedGenerationintegernoFormat int64.
tagstringnoEffective tag (spec.tag or the Tailnet template) — what the policy and the node carry.
tailnetIps[][]stringno
tlsNotAfterstringnoExpiry of the serving TLS certificate. Format date-time.
tlsSecretNamestringnoName of the effective TLS Secret in the ingress namespace; never certificate or key content.

TailnetSite

tailnetsites.tsunaro.io · scope Cluster · short name tns · v1alpha1

TailnetSite is a cluster-scoped remote network advertised by tagged subnet routers.

Spec

TailnetSiteSpec declares a remote network (a branch, a management net, a single host as /32) that a tagged router advertises into the tailnet. The operator renders autoApprovers for the prefixes, replays approvals on the advertising nodes and reports whether the routes are advertised and approved. Who may reach the prefixes stays a grant in the base policy.

FieldTypeRequiredDescription
advertisedByobjectnoSiteAdvertiser names the subnet routers that carry a site’s prefixes by tag. A router is any Headscale node with one of these tags — an operator-managed TailnetGateway as well as any tagged router in another cluster. Empty → Tailnet.spec.policy.routeApprovers.
advertisedBy.tags[][]stringnoItems ≤ 16.
descriptionstringnoMax length 253.
ownerstringyesTeam or person accountable for the network; named in conflict messages. Max length 63.
prefixes[][]stringyesPrefixes of the site. A /32 is a single host. Overlap with Tailnet.spec.reservedNetworks or with an older TailnetSite blocks this CR (RouteConflict); nothing is rendered for it. Items 1–64.
tailnetRefobjectyesTailnetRef selects a cluster-scoped Tailnet by name.
tailnetRef.namestringyes

Status

TailnetSiteStatus records what the control server reports for the site’s prefixes.

FieldTypeRequiredDescription
conditions[][]objectnoConditions: RouteConflict, UnmanagedOverlap, Advertised, Approved, Ready.
conditions[].lastTransitionTimestringyeslastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. Format date-time.
conditions[].messagestringyesmessage is a human readable message indicating details about the transition. This may be an empty string. Max length 32768.
conditions[].observedGenerationintegernoobservedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. Range ≥ 0. Format int64.
conditions[].reasonstringyesreason contains a programmatic identifier indicating the reason for the condition’s last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. Max length 1024.
conditions[].statusstringyesstatus of the condition, one of True, False, Unknown. One of True, False, Unknown.
conditions[].typestringyestype of condition in CamelCase or in foo.example.com/CamelCase. Max length 316.
observedGenerationintegernoFormat int64.
routesAdvertised[][]stringnoPrefixes of this site some advertiser node currently offers.
routesApproved[][]stringnoPrefixes of this site approved on some advertiser node.

Source: docs/reference/crds.md at v0.18.0 — found a mistake? Tell us.