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.
| Field | Type | Required | Description |
|---|---|---|---|
apiKeyLifetime | string | no | Lifetime of the self-minted working API key. Rotated at half-life. Default 720h. Max length 32. |
apiSecretRef | object | yes | Bootstrap 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.key | string | no | Default apiKey. |
apiSecretRef.name | string | yes | |
apiUrl | string | no | In-cluster URL for the REST API if it differs from Server. Empty → Server. |
authKeyLifetime | string | no | Lifetime of the reusable ephemeral pre-auth keys handed to agents. Rotated at half-life. Default 168h. |
defaults | object | no | Defaults inherited by ingress resources that do not select their own TLS source. |
defaults.tls | object | no | TLSDefaults 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.issuerRef | object | no | TLSIssuerReference 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.group | string | no | Only cert-manager.io issuers are supported. Default cert-manager.io. One of cert-manager.io. |
defaults.tls.issuerRef.kind | string | no | Default Issuer. One of Issuer, ClusterIssuer. |
defaults.tls.issuerRef.name | string | yes | Name of the issuer. Max length 253. |
dns | object | no | DNS records rendered from egress aliases and ingress names. Off when empty. |
dns.hostsConfigMap | string | no | Name 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.ingressAliasTemplate | string | no | Optional 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.ingressNameTemplate | string | no | Template 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. |
headscale | object | no | External Headscale metadata; compatibility is checked against the API, not the version hint. |
headscale.ref | string | no | Reserved for an operator-managed Headscale. Must remain empty until managed mode is supported. |
headscale.version | string | no | Optional version hint for diagnostics, e.g. “0.29.3”. Does not bypass capability checks. |
hostnameTemplate | string | no | Template for a TailnetEgress’s tailnet hostname when it sets none. {name} is the CR name. Default egress-{name}. |
ingressHostnameTemplate | string | no | Template for a TailnetIngress’s tailnet (MagicDNS) hostname when it sets none. {name} is the CR name. Default ingress-{name}. |
ingressTagTemplate | string | no | Template 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}. |
policy | object | no | Policy generation from CRs. Off when baseConfigMap is empty. |
policy.baseConfigMap | string | no | ConfigMap (operator namespace, key “policy”) holding the base policy in HuJSON. Empty → the feature is off and nothing is rendered or written. |
policy.ingressSourceAllowlist[] | []string | no | Glob 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.mode | string | no | DryRun 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.renderedConfigMap | string | no | ConfigMap (operator namespace, key “policy”) that receives the rendered policy for review. Empty → “ |
policy.routeApprovers[] | []string | no | Approvers 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[] | []string | no | Owners of the generated tags (tagOwners value), e.g. [“group:operator”]. Required when on. |
proxy | object | no | ProxySpec are the defaults for the per-CR agent pods this Tailnet spawns. A TailnetEgress may override image and resources. |
proxy.image | string | no | Container 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[] | []string | no | Names 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.mtu | integer | no | tsnet outer path MTU. 1200 leaves headroom for a ~1320-byte outer path (typical cloud CNI overlay). Default 1200. Range 576–1500. Format int32. |
proxy.probeInterval | string | no | How often the agent probes each target for reachability. “0s” disables probing. Default 5m. |
proxy.resources | object | no | ResourceRequirements describes the compute resource requirements. |
proxy.resources.claims[] | []object | no | Claims 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[].name | string | yes | Name 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[].request | string | no | Request 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.limits | object | no | Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ |
proxy.resources.requests | object | no | Requests 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[] | []object | no | Address 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[].name | string | yes | Max length 63. |
reservedNetworks[].prefixes[] | []string | yes | Items 1–32. |
server | string | yes | Login/control URL, becomes the agent’s ControlURL (e.g. https://ts.example.com ). |
tagTemplate | string | no | Template 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.
| Field | Type | Required | Description |
|---|---|---|---|
apiKeyExpiresAt | string | no | Expiry of the current working API key, drives the ApiKeyValid condition and its metric. Format date-time. |
conditions[] | []object | no | Conditions: ApiReachable, ApiKeyValid, HeadscaleCompatible, PolicyMode, SplitDNS, UsersUnique, PolicyRendered, PolicyApplied. |
conditions[].lastTransitionTime | string | yes | lastTransitionTime 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[].message | string | yes | message is a human readable message indicating details about the transition. This may be an empty string. Max length 32768. |
conditions[].observedGeneration | integer | no | observedGeneration 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[].reason | string | yes | reason 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[].status | string | yes | status of the condition, one of True, False, Unknown. One of True, False, Unknown. |
conditions[].type | string | yes | type of condition in CamelCase or in foo.example.com/CamelCase. Max length 316. |
observedGeneration | integer | no | Format int64. |
policy | object | no | Policy rendering/apply state; conditions PolicyRendered and PolicyApplied carry the reasons. |
policy.appliedAt | string | no | Format date-time. |
policy.appliedHash | string | no | sha256 of the policy last written to (or found identical on) the control server. |
policy.generated | object | no | PolicyGenerated counts what the operator added to the base policy. |
policy.generated.grants | integer | yes | |
policy.generated.routes | integer | yes | |
policy.generated.tags | integer | yes | |
policy.renderedHash | string | no | sha256 of the canonical rendered policy. |
userConflicts | integer | no | Headscale 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. |
version | string | no | Reported 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.
| Field | Type | Required | Description |
|---|---|---|---|
consumerNamespaces[] | []string | yes | Namespaces whose pods may reach this proxy’s Services (NetworkPolicy ingress). Items ≥ 1. |
hostname | string | no | Explicit tailnet hostname. Empty → rendered from the Tailnet’s hostnameTemplate. |
image | string | no | Override the Tailnet’s default agent image. |
resources | object | no | Override the Tailnet’s default agent resources. |
resources.claims[] | []object | no | Claims 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[].name | string | yes | Name 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[].request | string | no | Request 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.limits | object | no | Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ |
resources.requests | object | no | Requests 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/ |
tag | string | no | Explicit 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. |
tailnetRef | object | yes | TailnetRef selects a cluster-scoped Tailnet by name. |
tailnetRef.name | string | yes | |
targets[] | []object | yes | Items 1–50. |
targets[].address | string | yes | IPv4/IPv6 address or lowercase MagicDNS name, not a CIDR, URL or host:port. Max length 253. |
targets[].aliases[] | []string | no | Extra 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[].name | string | yes | DNS-1035 label; used verbatim as the Service name, so consumers dial |
targets[].port | integer | yes | Range 1–65535. Format int32. |
Status
TailnetEgressStatus is the observed state.
| Field | Type | Required | Description |
|---|---|---|---|
authKeyExpiresAt | string | no | Expiry of the reusable pre-auth key currently in the agent’s Secret. Format date-time. |
conditions[] | []object | no | Conditions: TagKnown, Registered, RoutesGranted, TargetsReachable, Ready. |
conditions[].lastTransitionTime | string | yes | lastTransitionTime 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[].message | string | yes | message is a human readable message indicating details about the transition. This may be an empty string. Max length 32768. |
conditions[].observedGeneration | integer | no | observedGeneration 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[].reason | string | yes | reason 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[].status | string | yes | status of the condition, one of True, False, Unknown. One of True, False, Unknown. |
conditions[].type | string | yes | type of condition in CamelCase or in foo.example.com/CamelCase. Max length 316. |
hostname | string | no | |
nodeId | string | no | |
observedGeneration | integer | no | Format int64. |
tag | string | no | Effective tag (spec.tag or the Tailnet template) — what the policy and the node carry. |
tailnetIps[] | []string | no | |
targets[] | []object | no | |
targets[].listenPort | integer | yes | Local port the agent listens on and the Service forwards to. Format int32. |
targets[].name | string | yes | |
targets[].present | boolean | yes | True once that route is present in the pod’s netmap. |
targets[].reachable | boolean | yes | True once the agent last dialled the target successfully. |
targets[].reason | string | no | When the route is missing: NotAdvertised, NotApproved or NoGrant. Empty when present. |
targets[].route | string | yes | The /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.
| Field | Type | Required | Description |
|---|---|---|---|
adoption | object | no | GatewayAdoption binds the inventoried node and resources. Admission cannot establish that a Secret belongs to this node or that a live template is identical. |
adoption.nodeId | string | yes | Expected numeric Headscale node ID; never inferred from hostname alone. Max length 20. |
adoption.objects[] | []object | yes | Items 2–16. |
adoption.objects[].apiVersion | string | yes | Max length 64. |
adoption.objects[].kind | string | yes | Max length 32. |
adoption.objects[].name | string | yes | GatewayName is a namespace-local Kubernetes object name. Max length 253. |
adoption.objects[].uid | string | yes | Max length 128. |
authKeySecretRef | object | no | Omitted selects persistent key minting in a future controller, never an ephemeral key. An adopted StatefulSet must retain its existing auth source. |
authKeySecretRef.key | string | no | Default TS_AUTHKEY. Max length 253. |
authKeySecretRef.name | string | yes | GatewayName is a namespace-local Kubernetes object name. Max length 253. |
deletionPolicy | string | no | Retain 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. |
dns | object | yes | GatewayDNS leaves the cluster CoreDNS Corefile and Headscale split-DNS config externally managed. Dynamic hosts remain owned by the existing DNS reconciler. |
dns.extraZones[] | []object | no | Items ≤ 16. |
dns.extraZones[].upstreams[] | []string | yes | Items 1–8. |
dns.extraZones[].zone | string | yes | GatewayName is a namespace-local Kubernetes object name. Max length 253. |
dns.image | string | yes | GatewayImage 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.serviceClusterIP | string | yes | GatewayIP is an unzoned unicast IP literal, not a hostname or a prefix. Max length 45. |
dns.serviceName | string | yes | Explicit name permits adoption of an existing tailnet-dns Service. Max length 63. |
dns.staticRecords[] | []object | no | Items ≤ 128. |
dns.staticRecords[].address | string | no | GatewayIP is an unzoned unicast IP literal, not a hostname or a prefix. Max length 45. |
dns.staticRecords[].name | string | yes | Relative single label below dns.zone. Max length 63. |
dns.staticRecords[].target | string | no | Target 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.wildcardAddress | string | no | Optional file-zone fallback, never a template plugin that shadows hosts. Max length 45. |
dns.zone | string | yes | GatewayName is a namespace-local Kubernetes object name. Max length 253. |
forwardImage | string | no | GatewayImage 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[] | []object | no | Items ≤ 16. |
forwards[].name | string | yes | Max length 50. |
forwards[].port | integer | yes | DNS, health, readiness and metrics ports are reserved by the sidecar. Range 1–65535. Format int32. |
forwards[].targetAddress | string | yes | IP literal avoids DNS/SNI ambiguity and arbitrary socat address expressions. Max length 45. |
forwards[].targetPort | integer | yes | Range 1–65535. Format int32. |
hostname | string | yes | Explicit until a cluster-name convention is introduced; no inferred identity. Max length 63. |
image | string | yes | GatewayImage 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. |
replicas | integer | no | Default 1. Format int32. |
routes | object | yes | GatewayRoutes adds literal-IP egress destinations from this Tailnet in the operator namespace. DNS targets are explicitly skipped, not resolved. |
routes.advertiseEgressTargets | boolean | no | Default true. |
routes.lan[] | []string | yes | Items 1–128. |
service | object | yes | GatewayService preserves the gateway’s stable split-DNS LoadBalancer address. |
service.loadBalancerClass | string | no | GatewayClass is a Kubernetes qualified load-balancer class name. Max length 317. |
service.loadBalancerIP | string | yes | GatewayIP is an unzoned unicast IP literal, not a hostname or a prefix. Max length 45. |
service.poolLabels | object | no | Labels used by the LoadBalancer IP-pool selector, not pod selectors. |
service.tailnetLBCIDR | string | yes | GatewayPrefix is a canonical, non-default subnet route. Network ownership and reachability still require runtime validation against the selected Tailnet. Max length 49. |
stateSecretRef | object | yes | GatewayLocalReference is always resolved in the operator namespace. |
stateSecretRef.name | string | yes | GatewayName is a namespace-local Kubernetes object name. Max length 253. |
suspend | boolean | no | Explicit false opts into fresh resource creation; adoption remains unsupported. Default true. |
tags[] | []string | no | Bootstrap tags do not authorize dropping existing tags on an adopted node. Default [tag:fabric]. Items 1–16. |
tailnetRef | object | yes | GatewayLocalReference is always resolved in the operator namespace. |
tailnetRef.name | string | yes | GatewayName is a namespace-local Kubernetes object name. Max length 253. |
Status
TailnetGatewayStatus records observations, not desired-state success claims.
| Field | Type | Required | Description |
|---|---|---|---|
conditions[] | []object | no | Registered, RoutesInSync, DNSReady, StaleNodes, ClusterDNSForward and Ready; fresh gateways with a verified completion receipt also report WorkloadCurrent and ServiceReady. |
conditions[].lastTransitionTime | string | yes | lastTransitionTime 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[].message | string | yes | message is a human readable message indicating details about the transition. This may be an empty string. Max length 32768. |
conditions[].observedGeneration | integer | no | observedGeneration 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[].reason | string | yes | reason 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[].status | string | yes | status of the condition, one of True, False, Unknown. One of True, False, Unknown. |
conditions[].type | string | yes | type of condition in CamelCase or in foo.example.com/CamelCase. Max length 316. |
ipv4 | string | no | |
nodeId | string | no | |
observedGeneration | integer | no | Format int64. |
routePlan | object | no | GatewayRouteStatus persists the quiet-period planner without process-local timers. |
routePlan.appliedHash | string | no | |
routePlan.pendingHash | string | no | |
routePlan.pendingSince | string | no | UTC RFC3339Nano; malformed timestamps cause the planner to restart its wait. |
routesAdvertised[] | []string | no | |
routesApproved[] | []string | no | |
serviceIP | string | no | |
staleNodes[] | []string | no | IDs 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.
| Field | Type | Required | Description |
|---|---|---|---|
allowFrom[] | []string | yes | Peers 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[] | []object | yes | Items 1–50. |
exposes[].alias | boolean | no | Request 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[].backend | object | yes | Backend 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.namespace | string | no | Namespace 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.port | integer | yes | Range 1–65535. Format int32. |
exposes[].backend.service | string | yes | Service name; in the CR’s own namespace unless Namespace is set. Max length 63. |
exposes[].backend.tls | object | no | Verified HTTPS to the backend. Omission preserves plaintext HTTP. |
exposes[].backend.tls.caConfigMapRef | object | yes | Public CA bundle in the ingress CR namespace, even if the backend is elsewhere. |
exposes[].backend.tls.caConfigMapRef.key | string | yes | Max length 253. |
exposes[].backend.tls.caConfigMapRef.name | string | yes | Max length 253. |
exposes[].backend.tls.serverName | string | yes | DNS name verified against the backend certificate; does not change the dial address or HTTP Host. Max length 253. |
exposes[].dnsLabel | string | no | HTTP 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[].mode | string | no | Listener mode. Omitted means L4 forwarding; HTTP requests TLS termination. Default l4. One of l4, http. Max length 4. |
exposes[].name | string | yes | DNS label; identifies the expose in status and in the agent config. Max length 63. |
exposes[].port | integer | yes | Port the tailnet node listens on — what a tailnet peer dials on this node’s MagicDNS name/IP. Range 1–65535. Format int32. |
exposes[].timeouts | object | no | HTTP-only timeout overrides. |
exposes[].timeouts.backendRequest | string | no | Wait for backend response headers after sending the request body. Defaults to 30s. Max length 32. |
exposes[].timeouts.request | string | no | Whole-response deadline. Omitted means no deadline. Max length 32. |
hostname | string | no | Explicit tailnet (MagicDNS) hostname. Empty → rendered from the Tailnet’s ingressHostnameTemplate. |
image | string | no | Override the Tailnet’s default agent image. |
resources | object | no | Override the Tailnet’s default agent resources. |
resources.claims[] | []object | no | Claims 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[].name | string | yes | Name 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[].request | string | no | Request 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.limits | object | no | Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ |
resources.requests | object | no | Requests 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/ |
tag | string | no | Explicit 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. |
tailnetRef | object | yes | TailnetRef selects a cluster-scoped Tailnet by name. |
tailnetRef.name | string | yes | |
tls | object | no | TLS source shared by the HTTP exposes. An omitted or empty source may use Tailnet defaults. |
tls.issuerRef | object | no | TLSIssuerReference identifies a cert-manager issuer without requiring its API dependency. Issuer references resolve in the ingress namespace; ClusterIssuer references are cluster-scoped. |
tls.issuerRef.group | string | no | Only cert-manager.io issuers are supported. Default cert-manager.io. One of cert-manager.io. |
tls.issuerRef.kind | string | no | Default Issuer. One of Issuer, ClusterIssuer. |
tls.issuerRef.name | string | yes | Name of the issuer. Max length 253. |
tls.secretName | string | no | Existing TLS Secret in the ingress namespace. Max length 253. |
Status
TailnetIngressStatus is the observed state.
| Field | Type | Required | Description |
|---|---|---|---|
authKeyExpiresAt | string | no | Expiry of the reusable pre-auth key currently in the agent’s Secret. Format date-time. |
conditions[] | []object | no | Conditions: TagKnown, Registered, BackendsReachable, TLSReady, Ready. |
conditions[].lastTransitionTime | string | yes | lastTransitionTime 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[].message | string | yes | message is a human readable message indicating details about the transition. This may be an empty string. Max length 32768. |
conditions[].observedGeneration | integer | no | observedGeneration 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[].reason | string | yes | reason 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[].status | string | yes | status of the condition, one of True, False, Unknown. One of True, False, Unknown. |
conditions[].type | string | yes | type of condition in CamelCase or in foo.example.com/CamelCase. Max length 316. |
dnsName | string | no | DNS name rendered from the Tailnet’s dns.ingressNameTemplate; resolves to TailnetIPs via the hosts ConfigMap. Empty when no template is set. |
exposes[] | []object | no | |
exposes[].backend | string | yes | The in-cluster backend the agent dials, as service:port. |
exposes[].dnsAliases[] | []string | no | Additional resolved DNS aliases for an HTTP expose; empty unless alias is requested and won. |
exposes[].dnsName | string | no | Resolved DNS name for an HTTP expose; empty for L4. |
exposes[].listenPort | integer | yes | Port the tailnet node listens on for this expose. Format int32. |
exposes[].name | string | yes | |
exposes[].reachable | boolean | yes | Backend reachability. With backend TLS this requires a fresh verified handshake bound to the current configuration, CA bundle and unexpired certificate. |
exposes[].reason | string | no | Bounded backend or frontend failure reason; empty when ready. |
exposes[].tlsReady | boolean | no | Local TLS component readiness for an HTTP expose; omitted for L4. This does not establish remote tailnet reachability or client CA trust. |
hostname | string | no | |
hosts[] | []string | no | Names 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. |
nodeId | string | no | |
observedGeneration | integer | no | Format int64. |
tag | string | no | Effective tag (spec.tag or the Tailnet template) — what the policy and the node carry. |
tailnetIps[] | []string | no | |
tlsNotAfter | string | no | Expiry of the serving TLS certificate. Format date-time. |
tlsSecretName | string | no | Name 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.
| Field | Type | Required | Description |
|---|---|---|---|
advertisedBy | object | no | SiteAdvertiser 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[] | []string | no | Items ≤ 16. |
description | string | no | Max length 253. |
owner | string | yes | Team or person accountable for the network; named in conflict messages. Max length 63. |
prefixes[] | []string | yes | Prefixes 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. |
tailnetRef | object | yes | TailnetRef selects a cluster-scoped Tailnet by name. |
tailnetRef.name | string | yes |
Status
TailnetSiteStatus records what the control server reports for the site’s prefixes.
| Field | Type | Required | Description |
|---|---|---|---|
conditions[] | []object | no | Conditions: RouteConflict, UnmanagedOverlap, Advertised, Approved, Ready. |
conditions[].lastTransitionTime | string | yes | lastTransitionTime 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[].message | string | yes | message is a human readable message indicating details about the transition. This may be an empty string. Max length 32768. |
conditions[].observedGeneration | integer | no | observedGeneration 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[].reason | string | yes | reason 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[].status | string | yes | status of the condition, one of True, False, Unknown. One of True, False, Unknown. |
conditions[].type | string | yes | type of condition in CamelCase or in foo.example.com/CamelCase. Max length 316. |
observedGeneration | integer | no | Format int64. |
routesAdvertised[] | []string | no | Prefixes of this site some advertiser node currently offers. |
routesApproved[] | []string | no | Prefixes of this site approved on some advertiser node. |
Source: docs/reference/crds.md at v0.18.0 — found a mistake? Tell us.