Skip to content

Fix Tailscale Ingress backends + split Crafty UI vs game ports exposure #171

@SRF-Audio

Description

@SRF-Audio

Goal

  • Make all Tailscale-managed Ingress resources valid for the Tailscale k8s operator (stop InvalidIngressBackend / NoValidBackends).

  • For Crafty specifically:

    • Crafty UI must be exposed via Ingress.
    • Crafty game ports/services must be exposed via Service port exposure (not via Ingress).

Context / Current Symptoms

  • Tailscale operator events on ingresses:

    • InvalidIngressBackend ... rule with host "...ts.net" ignored, unsupported
    • NoValidBackends ... no valid backends
  • Current Crafty Ingress uses spec.rules[].host: crafty-controller.rohu-shark.ts.net, which Tailscale operator treats as unsupported.

  • Crafty Service is already annotated for service exposure and has valid EndpointSlices.


Scope

Only touch:

  • argocd/apps/** manifests that define Tailscale ingresses and the Crafty service/ingress.
  • (If present) any shared “template” or generator used for these ingresses.

Do not change:

  • Cluster-wide Tailscale operator installation/helm chart unless explicitly required.
  • Storage, PVCs, or app deployments.

Requirements

1) Tailscale Ingress resources must be “hostless”

For any Ingress with spec.ingressClassName: tailscale:

  • Remove spec.rules[].host entirely.
  • Keep spec.rules[].http.paths[] intact.
  • Ensure spec.tls[0].hosts[0] is set to the desired shortname (e.g., crafty-controller, paperless, homepage, etc.). This is what Tailscale uses to create the tailnet hostname/service.

Acceptance criteria

  • kubectl describe ingress <name> no longer shows:

    • InvalidIngressBackend “host ignored, unsupported”
    • NoValidBackends
  • Tailscale operator logs show it successfully exposes the ingress.


2) Crafty: split UI ingress from game/service port exposure

2a) Crafty UI via Ingress

  • Crafty UI Ingress must:

    • ingressClassName: tailscale
    • Have no spec.rules[].host
    • Route / to the Crafty UI backend service on port 443 (targetPort 8443 already).
    • tls.hosts should contain exactly: crafty-controller (shortname)

Do not include tailscale.com/expose: "true" on the Ingress unless it is already required in your repo patterns; prefer only the tags/hostname config needed for ingress.

2b) Crafty game ports via Service (port exposure)

  • Keep Crafty Service exposing non-HTTP ports directly (Minecraft Java ports, Bedrock UDP, dynmap, voice chat, etc.).

  • Crafty Service should retain:

    • tailscale.com/expose: "true"
    • tailscale.com/hostname: crafty-controller (BUT: hostname collisions must be avoided; see below)

Important collision rule
You cannot have the Ingress and Service both trying to claim the same Tailscale hostname (crafty-controller) unless you intentionally want only one and the other fails/flaps.

So:

  • The Ingress should own the hostname crafty-controller for the UI.

  • The Service should use a distinct hostname for raw ports, e.g.:

    • crafty-controller-svc
    • crafty-controller-ports
    • crafty-mc
      Pick one and apply consistently.

Acceptance criteria

  • UI reachable at https://crafty-controller.<tailnet-domain> (or whatever tailnet DNS pattern you use).
  • Raw game ports reachable at <service-hostname>.<tailnet-domain>:25565 etc.
  • Operator no longer “unexposes” Crafty resources repeatedly.

Implementation Tasks (Copilot steps)

Task A — Fix all Tailscale ingresses

  1. Search repo for ingresses using Tailscale class:

    • ingressClassName: tailscale
  2. For each, remove spec.rules[].host keys.

  3. Ensure spec.rules[].http.paths[].backend.service.name and .port remain correct.

  4. Ensure spec.tls[0].hosts[0] exists and is a shortname (no .ts.net).

Task B — Crafty-specific split

  1. Update Crafty Ingress:

    • remove spec.rules[0].host
    • ensure backend points to service: crafty-controller port 443
    • ensure tls.hosts: [crafty-controller]
  2. Update Crafty Service annotations:

    • keep tailscale.com/expose: "true"
    • change tailscale.com/hostname to a non-conflicting name, e.g. crafty-controller-ports
    • keep tags as-is unless tags are failing in operator logs (see non-goal note below)

Task C — Validation commands documented in PR description

Add to the PR description (not as code comments in manifests):

  • kubectl -n apps-crafty-controller describe ingress crafty-controller
  • kubectl -n apps-crafty-controller get ingress crafty-controller -o yaml
  • kubectl -n apps-crafty-controller get svc crafty-controller -o yaml
  • kubectl -n tailscale logs deploy/operator -c operator --tail=200
  • Confirm no InvalidIngressBackend/NoValidBackends events.
  • Confirm both hostnames appear in tailnet DNS and are reachable.

Out of Scope (explicit)

  • Fixing Tailscale tag ACL policy errors like:

    • requested tags [...] are invalid or not permitted
      Those are separate (tailnet ACL/tagOwners) and should be handled in a different issue unless they block Crafty specifically.

Done Definition

  • Crafty UI works via Tailscale Ingress.
  • Crafty game ports work via Tailscale Service port exposure with a distinct hostname.
  • All Tailscale Ingresses no longer emit InvalidIngressBackend / NoValidBackends.
  • Operator logs show stable reconciliation (no repeated unexpose/re-expose flapping).

Metadata

Metadata

Labels

No labels
No labels

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions