TAK-OS Ingress administrator guide
Use this guide to install and operate the controller. You need cluster-level permissions for the Namespace, RBAC, DaemonSet, Service, and IngressClass resources. Application owners can then follow the user guide.
Deployment model
The shipped deployment runs a DaemonSet in namespace tak-os-ingress, including
tolerations for control-plane nodes. Each Pod runs the same routing controllers
and proxy. Backend requests go to Service DNS names and Service ports; Kubernetes
Service routing handles the backend endpoints. There is no per-application proxy
configuration file to reload.
The DaemonSet does not use host networking or host ports. Clients reach container
ports 80 and 443 through a Service or another explicitly configured network path.
Port 9090 serves /metrics and /healthz. The public Service exposes only HTTP
and HTTPS.
Prepare the following before installation:
- A cluster supporting
networking.k8s.io/v1Ingress resources and working Service DNS and networking. - An accessible controller image and registry credentials when needed.
- An address and Service exposure method suitable for the cluster, plus DNS records pointing at that address.
- cert-manager and an Issuer or ClusterIssuer if automatic Ingress certificate issuance is required. Existing TLS Secrets can be used without issuance.
- Gateway API v1 CRDs if GatewayClass, Gateway, and HTTPRoute support is required.
The manifests do not install cert-manager, its issuers, or Gateway API CRDs. Select dependency versions compatible with your cluster. The Go dependencies and build toolchain requirement are recorded in go.mod.
Install with Orbit Deploy
When Orbit Deploy is already installed, save and apply this resource:
apiVersion: k8s.tlspu.com/v1
kind: OrbitDeployment
metadata:
name: tak-os-ingress
namespace: kube-system
spec:
source: https://gitlab.com/tak-os-platform/tak-os-containers/tak-os-ingress.git
Orbit renders the manifests in default using its deployment
values and image mapping in deployment.json.
The image mapping uses latest; manage release selection through your deployment
process when you need a fixed version. Configure the external addresses used by
Orbit and inspect the rendered Service before relying on it for public access.
Orbit owns reconciliation of these resources. Persist configuration changes in the deployment source or values so that reconciliation does not overwrite them.
Install manually
The file default/020-daemonset.yaml is an Orbit template, so it cannot be applied
unchanged. Its image, image-hash annotation, and external addresses contain
<!...!> placeholders. The Service template also uses ExternalIPs rather than
the Kubernetes field externalIPs and includes externalTrafficPolicy on a
ClusterIP Service. Prepare a valid manifest before applying it.
The following renders a local manifest with an internal ClusterIP Service. It
requires Python 3, leaves the repository template intact, and removes the two
Service fields that need an environment-specific exposure decision. Replace
YOUR_RELEASE_TAG with the release you intend to deploy.
INGRESS_IMAGE=registry.gitlab.com/tak-os-platform/tak-os-containers/tak-os-ingress:YOUR_RELEASE_TAG \
python3 - <<'PY'
import os
from pathlib import Path
manifest = Path("default/020-daemonset.yaml").read_text()
manifest = manifest.replace("<!tak-os-ingress!>", os.environ["INGRESS_IMAGE"])
manifest = manifest.replace("<!tak-os-ingress-hash!>", '"manual"')
manifest = "\n".join(
line for line in manifest.splitlines()
if "ExternalIPs:" not in line and "externalTrafficPolicy:" not in line
) + "\n"
if "<!" in manifest:
raise SystemExit("Unresolved deployment placeholder")
Path("/tmp/tak-os-ingress.yaml").write_text(manifest)
PY
kubectl apply -f default/000-namespace.yaml
kubectl apply -f default/010-rbac.yaml
kubectl apply --dry-run=server -f /tmp/tak-os-ingress.yaml
kubectl apply -f /tmp/tak-os-ingress.yaml
kubectl -n tak-os-ingress rollout status daemonset/tak-os-ingress
The resulting Service is internal. To expose it, edit the rendered manifest for
your cluster before applying it: use a supported LoadBalancer or NodePort
Service, or configure valid externalIPs with the required network routing.
Declaring an external IP does not allocate or advertise it. The controller does
not provision an external address itself.
The template retains internalTrafficPolicy: Local. Internal Service traffic
must reach nodes with a local ready ingress Pod. If using externalTrafficPolicy: Local with a supported external Service configuration, ensure the load balancer
sends traffic only to nodes with a local ready ingress Pod. This can preserve
source addresses, depending on the upstream network path. Verify the observed
client IP before depending on per-client rate limits or forwarding headers.
Verify installation
kubectl -n tak-os-ingress get daemonset,pods,service -o wide
kubectl get ingressclass tak-os -o yaml
kubectl -n tak-os-ingress logs -l app=tak-os-ingress --tail=100 --prefix
kubectl -n tak-os-ingress port-forward daemonset/tak-os-ingress 9090:9090
In another terminal:
curl --fail http://127.0.0.1:9090/healthz
curl --fail http://127.0.0.1:9090/metrics
/healthz returns 200 OK; it is a process health endpoint, not a check of
backend availability, informer synchronization, or certificate readiness. Verify
the data path separately using a known application route from the
user guide. You can forward ports
8080:80 and 8443:443 from a controller Pod when public exposure is not ready.
Configure the controller
Set flags in the container args list. Keep --ingress-class=tak-os when using
the shipped IngressClass. Updating process flags requires a Pod rollout;
application route and Secret updates are applied live.
| Flag | Binary default | Meaning |
|---|---|---|
--ingress-class |
default |
Ingress class name to process; the shipped DaemonSet overrides this to tak-os |
--process-without-class |
false |
Also claim Ingresses without a class field or legacy class annotation |
--port |
80 |
HTTP listen port |
--ssl-port |
443 |
HTTPS listen port |
--metrics-port |
9090 |
Metrics and health listen port |
--max-request-body-size |
0 |
Bytes; 0 uses 50 MiB, positive values set a cap, -1 permits unlimited uploads |
--max-response-body-size |
0 |
Bytes buffered per response before streaming; 0 uses 50 MiB, -1 permits unlimited buffering |
--server-read-timeout |
60 |
Client request idle read timeout in seconds; 0 disables it |
--enable-gateway-api |
true |
Start Gateway API support when the required CRDs are discovered |
--gateway-controller-id |
k8s.tak-os.com/gateway-controller |
GatewayClass controller name to accept |
--kubeconfig |
empty | Outside the cluster, use this file or $HOME/.kube/config; inside the cluster, service-account configuration is used |
When changing listen ports, also update container ports, Service target ports, probes, and scrape configuration. Gateway listener validation currently accepts HTTP port 80 and HTTPS port 443; changing process ports does not extend that support.
The response limit is a buffering threshold, not a maximum download size. Larger responses stream with their original Content-Length. Unlimited buffering can consume unbounded memory. Request and response limits are global process flags, not per-Ingress annotations. The memory budget must account for concurrent requests, buffers, connections, and authentication work.
The client request read timeout measures inactivity while reading headers and bodies. Backend connect/read/send defaults are configured separately through route annotations. Keep-alive client connections also have a 60-second idle timeout.
Manage TLS and authentication
For automatic Ingress issuance, install cert-manager, create an issuer, and let
application owners select it with cert-manager.io/cluster-issuer or
cert-manager.io/issuer. ClusterIssuer takes precedence when both are set.
Without either annotation, TAK-OS selects ClusterIssuer letsencrypt-staging.
For ACME HTTP-01 issuance, ensure challenge traffic reaches the solver through
the configured ingress class and public port 80.
Only Ingress TLS blocks without secretName trigger TAK-OS Certificate creation.
For Gateway HTTPS listeners, provision a TLS Secret separately, for example with
a cert-manager Certificate. The Gateway controller does not request certificates.
See Gateway TLS.
The HTTPS server requires TLS 1.2 or later. An unknown SNI or unusable Ingress TLS Secret uses a self-signed fallback generated at process startup. Gateway HTTPS listeners with invalid certificate references do not install their routes.
Basic and OAuth configuration Secrets must live in the route's namespace.
Secret updates reconcile affected routes. OAuth signing keys must decode to
exactly 32 bytes; generate one with openssl rand -base64 32. Rotating that key
invalidates sessions signed by the old key. Check authentication Secret
formats before rotation or migration.
The runtime image contains the system CA bundle for backend and provider TLS validation. Private certificate authorities require an appropriate trust bundle in the container; there is no per-route CA Secret annotation. OAuth callback scheme and forwarding headers derive from the actual connection, so the expected edge deployment terminates TLS at this controller.
Operate with appropriate access controls
The shipped ClusterRole reads Services, Secrets, and Namespaces across the cluster; watches Ingresses, IngressClasses, Certificates, and Gateway resources; creates and updates Certificates; and writes Gateway resource statuses. It does not write Ingress load-balancer status.
Cluster-wide Secret access includes TLS private keys and authentication credentials. Adding narrower namespace Roles does not restrict an existing ClusterRole grant. Restricting this controller to namespace-scoped Secret access requires both RBAC changes and changes to its cluster-wide informer watches.
The shipped container runs as UID/GID 65532, with a read-only root filesystem,
privilege escalation disabled, RuntimeDefault seccomp, and all capabilities
dropped except NET_BIND_SERVICE. The scratch image has no shell. Use logs,
port forwarding, or an authorized diagnostic Pod for troubleshooting.
Port 9090 has no authentication. Restrict it to the monitoring and management networks. Allow ingress Pods to reach the Kubernetes API, cluster DNS, backend Service ports, and any authentication providers. Apply network policies that match your cluster networking and exposure method.
The proxy rebuilds X-Forwarded-For, X-Real-IP, X-Forwarded-Proto,
X-Forwarded-Host, and X-Forwarded-Port from the immediate connection and
request host, and removes supplied Forwarded headers. It strips supplied
X-Auth-Request-* headers before authentication. Deployments behind another proxy
will report that proxy's address when it is the TCP peer.
Monitor the controller
The Pod template includes Prometheus scrape annotations for port 9090. Configure your monitoring system to discover those Pods; annotations alone do not install a scraper. Import the JSON files in dashboards into Grafana and select the Prometheus data source.
| Metric | Operational use |
|---|---|
tak_os_ingress_processed_total |
Ingress reconciliation events by event type and class |
tak_os_ingress_active_routes |
Installed routes by class label |
tak_os_ingress_unsupported_backends_total |
Unsupported Ingress or Gateway backend/rule configuration |
tak_os_gateway_rules_processed_total |
Gateway rules processed by namespace and installed/unsupported result |
tak_os_gateway_api_available |
1 when Gateway support started, 0 when disabled or unavailable |
tak_os_ingress_requests_total |
Matched-route request counts by configured host, method, and status |
tak_os_ingress_request_duration_seconds |
End-to-end latency histogram |
tak_os_ingress_backend_request_duration_seconds |
Backend request latency histogram |
tak_os_ingress_route_lookup_duration_seconds |
Route lookup latency by whether a route matched |
tak_os_ingress_bytes_sent_total |
Response bytes by configured host |
tak_os_ingress_rate_limited_total |
Rejections from route token-bucket limits |
Request metrics use matched configuration hosts, so they do not count all unmatched requests. Authentication throttling is separate from the route limiter; the route rate-limit metric is not a count of all possible 429 responses. Each Pod exports its own counters and gauges; aggregate them with that scope in mind.
Monitor Pod readiness and restarts, CPU and memory, backend error rates, certificate expiry and issuance failures, and Gateway conditions. The shipped resource requests are 100m CPU and 128 MiB memory, with limits of 2 CPUs and 1 GiB memory. Tune them against representative traffic; the performance tests provide a test environment.
Upgrade and roll back
Record the current image and configuration before changing the release. Check that OAuth signing keys are 32 bytes and that HTTPRoutes use supported matches. Rules containing only unsupported method, query, or regex matches install no route. This behavior is especially relevant when migrating from older releases.
For Orbit-managed installations, update the deployment source or release through Orbit. For manually managed installations, update the desired manifest and apply it, then check rollout and a known route:
kubectl -n tak-os-ingress rollout status daemonset/tak-os-ingress
kubectl -n tak-os-ingress rollout history daemonset/tak-os-ingress
If a manually managed rollout must be reverted, reapply the saved manifest and
image, or use kubectl -n tak-os-ingress rollout undo daemonset/tak-os-ingress
when the desired revision is available. A mutable latest tag cannot reliably
identify the previous image for rollback.
The process handles SIGTERM/SIGINT with a shared 30-second graceful shutdown window. The shipped Pod termination grace period is 60 seconds. Long-lived streams may reconnect during a rollout. Route updates alone do not require Pod restarts.
Diagnose controller problems
Start with Pod events, logs, and the affected resource:
kubectl -n tak-os-ingress describe daemonset tak-os-ingress
kubectl -n tak-os-ingress get events --sort-by=.lastTimestamp
kubectl -n tak-os-ingress logs -l app=tak-os-ingress --tail=200 --prefix
kubectl -n apps describe ingress my-app
kubectl -n apps get service my-app -o yaml
kubectl -n apps get endpointslices -l kubernetes.io/service-name=my-app
| Problem | Action |
|---|---|
| Invalid manifest or image | Render Orbit placeholders; check Service field names, image tag, pull credentials, and Pod events |
| Ingress skipped | Compare its class with --ingress-class and the IngressClass controller k8s.tak-os.com/ingress |
| API watch errors or caches not syncing | Check service-account permissions, API connectivity, and required resource APIs |
| cert-manager watch errors | Check that Certificate CRDs are installed; certificate client creation alone does not prove API availability |
| Gateway support unavailable | Check --enable-gateway-api, discovery access, and all three v1 CRDs; restart Pods after installing CRDs that were absent at startup |
| Gateway route missing | Inspect HTTPRoute parent conditions and Gateway listener conditions using the Gateway guide |
| Backend 502 | Check DNS, Service ports, EndpointSlices, network policy, backend TLS verification, and auth endpoint availability |
| Incorrect rate-limit identity | Check the immediate peer IP and source-address preservation through Services, NAT, and upstream proxies |
| OOMKilled or high latency | Compare concurrency and body buffering with resource limits and backend latency |
For client-visible symptoms, use the application troubleshooting
table. /healthz can remain OK
while route reconciliation or backend access is failing.
Remove the installation
First migrate or remove application routes and DNS records, and stop Orbit
reconciliation if it owns this installation. For a manually managed deployment,
delete the resources from your rendered manifest, then remove the RBAC resources
from default/010-rbac.yaml once they are no longer used. Deleting namespace
tak-os-ingress also deletes all other resources in it, so inspect it first.
Application Secrets, cert-manager, and Gateway API CRDs have separate lifecycles;
retain shared dependencies and certificates that other applications still use.
