--- title: "Cómo configurar Ingress en Kubernetes con Traefik y TLS" description: "Instala Traefik como controlador de Ingress, enruta tráfico por dominio y por ruta a varios servicios y obtén certificados de Let's Encrypt con cert-manager." category: "Docker Kubernetes" language: es source: https://cubepath.com/es/docs/docker-kubernetes/configuracion-de-ingress-en-kubernetes updated: 2026-09-25 --- # Cómo configurar Ingress en Kubernetes con Traefik y TLS Un recurso Ingress define reglas HTTP y HTTPS (por nombre de dominio y por ruta) para llevar tráfico externo hasta los Services de un clúster de Kubernetes. Las reglas no hacen nada por sí solas: necesitan un controlador de Ingress que las lea y actúe como proxy inverso. En este tutorial instalarás Traefik como controlador, publicarás dos aplicaciones bajo el mismo punto de entrada con enrutamiento por dominio y por ruta, y activarás HTTPS con certificados de Let's Encrypt gestionados por cert-manager. ## Requisitos previos - Un clúster de Kubernetes 1.30 o posterior en el que los Services de tipo `LoadBalancer` reciban una IP pública (un clúster gestionado con balanceador integrado, o MetalLB en servidores propios). Si solo tienes NodePort, puedes seguir la guía usando la IP de un nodo y el puerto asignado. - `kubectl` configurado contra el clúster con permisos de administrador (hay que crear recursos a nivel de clúster, como IngressClass y ClusterIssuer). - Helm 3 o posterior en la máquina desde la que administras el clúster. En Ubuntu 24.04 puedes instalarlo con `sudo snap install helm --classic`. - Un dominio con acceso a su DNS. Los ejemplos usan `app.example.com` y `api.example.com`: sustitúyelos por los tuyos. Comprueba el acceso al clúster antes de empezar: ```bash kubectl get nodes helm version ``` > Nota: el proyecto comunitario ingress-nginx dejó de mantenerse en marzo de 2026 y ya no recibe parches de seguridad. Para instalaciones nuevas usa otro controlador, como Traefik (el de esta guía), o migra a Gateway API. Los recursos Ingress que escribas aquí son estándar y sirven con cualquier controlador que los soporte. ## Paso 1: Instalar Traefik como controlador de Ingress Añade el repositorio oficial de charts de Traefik e instálalo en su propio namespace: ```bash helm repo add traefik https://traefik.github.io/charts helm repo update helm install traefik traefik/traefik --namespace traefik --create-namespace ``` El chart crea un Deployment con Traefik, un Service de tipo `LoadBalancer` que escucha en los puertos 80 y 443, y una IngressClass llamada `traefik`. Comprueba que el pod está en marcha: ```bash kubectl get pods -n traefik ``` ```output NAME READY STATUS RESTARTS AGE traefik-6d8b9f7c5b-kx2lp 1/1 Running 0 45s ``` Comprueba que existe la IngressClass que referenciarán tus Ingress: ```bash kubectl get ingressclass ``` ```output NAME CONTROLLER PARAMETERS AGE traefik traefik.io/ingress-controller 50s ``` > Nota: si tu clúster es k3s, Traefik ya viene instalado en el namespace `kube-system` con la IngressClass `traefik`. Salta este paso y usa `kube-system` donde la guía dice `traefik`. ## Paso 2: Obtener la IP externa y configurar el DNS Consulta la IP que el balanceador ha asignado al Service de Traefik. Puede tardar uno o dos minutos en aparecer: ```bash kubectl get service traefik -n traefik ``` ```output NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE traefik LoadBalancer 10.43.112.45 203.0.113.25 80:31080/TCP,443:31443/TCP 2m ``` Si `EXTERNAL-IP` se queda en ``, tu clúster no tiene un balanceador de carga; revisa la solución de problemas al final. Crea en tu proveedor de DNS un registro `A` para cada dominio (`app.example.com` y `api.example.com`) apuntando a esa IP. Guárdala también en una variable para las pruebas: ```bash export INGRESS_IP=203.0.113.25 ``` ## Paso 3: Desplegar dos aplicaciones de ejemplo Para ver el enrutamiento en acción, despliega dos aplicaciones en un namespace `demo`: `web`, un Nginx normal, y `api`, con la imagen `traefik/whoami`, que responde con los datos de la petición que recibe. Crea el archivo `apps.yaml`: ```bash nano apps.yaml ``` ```yaml apiVersion: v1 kind: Namespace metadata: name: demo --- apiVersion: apps/v1 kind: Deployment metadata: name: web namespace: demo spec: replicas: 2 selector: matchLabels: app: web template: metadata: labels: app: web spec: containers: - name: nginx image: nginx:1.28 ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: web namespace: demo spec: selector: app: web ports: - port: 80 targetPort: 80 --- apiVersion: apps/v1 kind: Deployment metadata: name: api namespace: demo spec: replicas: 2 selector: matchLabels: app: api template: metadata: labels: app: api spec: containers: - name: whoami image: traefik/whoami ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: api namespace: demo spec: selector: app: api ports: - port: 80 targetPort: 80 ``` Aplícalo y comprueba que los Services tienen endpoints (pods detrás): ```bash kubectl apply -f apps.yaml kubectl get pods,endpointslices -n demo ``` Los dos Services son de tipo `ClusterIP`: no son accesibles desde fuera del clúster. El Ingress será la única puerta de entrada. ## Paso 4: Enrutar tráfico por nombre de dominio Crea un Ingress que envíe `app.example.com` al Service `web` y `api.example.com` al Service `api`: ```bash nano ingress-hosts.yaml ``` ```yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: demo namespace: demo spec: ingressClassName: traefik rules: - host: app.example.com http: paths: - path: / pathType: Prefix backend: service: name: web port: number: 80 - host: api.example.com http: paths: - path: / pathType: Prefix backend: service: name: api port: number: 80 ``` `ingressClassName` indica qué controlador debe atender este Ingress. Aplícalo: ```bash kubectl apply -f ingress-hosts.yaml kubectl get ingress -n demo ``` ```output NAME CLASS HOSTS ADDRESS PORTS AGE demo traefik app.example.com,api.example.com 203.0.113.25 80 10s ``` Prueba las dos reglas. La cabecera `Host` permite hacerlo aunque el DNS aún no se haya propagado: ```bash curl -s -H "Host: app.example.com" http://$INGRESS_IP/ | grep -o '.*' curl -s -H "Host: api.example.com" http://$INGRESS_IP/ | head -n 1 ``` ```output Welcome to nginx! Hostname: api-7b9c6d5f8-4jz2q ``` Una petición con un `Host` que no coincide con ninguna regla recibe un `404` de Traefik. ## Paso 5: Enrutar tráfico por ruta También puedes servir varias aplicaciones bajo un mismo dominio, separadas por ruta. Modifica la regla de `app.example.com` para que `/api` vaya al Service `api` y el resto a `web`. Edita `ingress-hosts.yaml` y deja la primera regla así: ```yaml - host: app.example.com http: paths: - path: /api pathType: Prefix backend: service: name: api port: number: 80 - path: / pathType: Prefix backend: service: name: web port: number: 80 ``` Aplica el cambio y pruébalo: ```bash kubectl apply -f ingress-hosts.yaml curl -s -H "Host: app.example.com" http://$INGRESS_IP/api/usuarios | grep GET ``` ```output GET /api/usuarios HTTP/1.1 ``` Ten en cuenta dos detalles: - Con `pathType: Prefix` la coincidencia es por segmentos completos: `/api` captura `/api` y `/api/usuarios`, pero no `/apiv2`. Con `pathType: Exact` solo coincide la ruta exacta. - El backend recibe la ruta original (`/api/usuarios`). La especificación de Ingress no incluye reescritura de rutas; si tu aplicación espera `/usuarios`, configúrala para servir bajo `/api` o usa un middleware `stripPrefix` de Traefik. ## Paso 6: Instalar cert-manager cert-manager solicita y renueva certificados TLS automáticamente y los guarda como Secrets de Kubernetes. Instálalo con Helm, incluyendo sus CRD: ```bash helm repo add jetstack https://charts.jetstack.io helm repo update helm install cert-manager jetstack/cert-manager --namespace cert-manager --create-namespace --set crds.enabled=true ``` Comprueba que sus tres componentes están en marcha: ```bash kubectl get pods -n cert-manager ``` ```output NAME READY STATUS RESTARTS AGE cert-manager-5c9d8879fd-q7x2m 1/1 Running 0 60s cert-manager-cainjector-6cc9b5f678-p4lbn 1/1 Running 0 60s cert-manager-webhook-7bb7b75848-9d2kc 1/1 Running 0 60s ``` ## Paso 7: Crear un emisor de Let's Encrypt Un `ClusterIssuer` indica a cert-manager cómo obtener certificados. Este usa el desafío HTTP-01 de Let's Encrypt: cert-manager crea temporalmente un Ingress con la clase `traefik` para responder al desafío en `/.well-known/acme-challenge/`. Sustituye `tu_correo@example.com` por tu dirección, a la que Let's Encrypt enviará avisos sobre tus certificados: ```bash nano cluster-issuer.yaml ``` ```yaml apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: letsencrypt-prod spec: acme: server: https://acme-v02.api.letsencrypt.org/directory email: tu_correo@example.com privateKeySecretRef: name: letsencrypt-prod-account-key solvers: - http01: ingress: ingressClassName: traefik ``` Aplícalo y comprueba que queda listo: ```bash kubectl apply -f cluster-issuer.yaml kubectl get clusterissuer ``` ```output NAME READY AGE letsencrypt-prod True 15s ``` > Consejo: mientras hagas pruebas, crea un segundo emisor igual con el servidor `https://acme-staging-v02.api.letsencrypt.org/directory`. Los certificados de staging no son de confianza, pero no cuentan para los límites de emisión de Let's Encrypt. ## Paso 8: Activar HTTPS en el Ingress El DNS de tus dominios debe apuntar ya a la IP del paso 2, porque Let's Encrypt validará el dominio conectándose a él. Compruébalo: ```bash dig +short app.example.com ``` Añade al Ingress la anotación que lo vincula al emisor y una sección `tls` con los dominios y el nombre del Secret donde se guardará el certificado. Edita `ingress-hosts.yaml` para que la cabecera quede así (las reglas no cambian): ```yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: demo namespace: demo annotations: cert-manager.io/cluster-issuer: letsencrypt-prod spec: ingressClassName: traefik tls: - hosts: - app.example.com - api.example.com secretName: demo-tls rules: ``` Aplica el cambio y sigue la emisión del certificado: ```bash kubectl apply -f ingress-hosts.yaml kubectl get certificate -n demo --watch ``` ```output NAME READY SECRET AGE demo-tls False demo-tls 5s demo-tls True demo-tls 48s ``` Cuando `READY` pase a `True`, pulsa `Ctrl+C` y prueba el acceso por HTTPS: ```bash curl -sI https://app.example.com ``` ```output HTTP/2 200 content-type: text/html server: nginx/1.28.0 ``` cert-manager renovará el certificado automáticamente antes de que caduque, sin que tengas que hacer nada. ## Paso 9: Redirigir HTTP a HTTPS (opcional) Por defecto Traefik sigue sirviendo también por HTTP. Para redirigir todo el tráfico del puerto 80 al 443, activa la redirección en el punto de entrada `web` con un archivo de valores del chart: ```bash nano traefik-values.yaml ``` ```yaml ports: web: redirections: entryPoint: to: websecure scheme: https permanent: true ``` Aplícalo sobre la instalación existente: ```bash helm upgrade traefik traefik/traefik --namespace traefik --reuse-values -f traefik-values.yaml ``` Comprueba la redirección: ```bash curl -sI http://app.example.com ``` ```output HTTP/1.1 301 Moved Permanently Location: https://app.example.com/ ``` > Nota: esta sintaxis corresponde a las versiones recientes del chart (34 o posteriores). Si usas una versión antigua, consulta el archivo `values.yaml` de tu versión con `helm show values traefik/traefik`. La renovación HTTP-01 sigue funcionando con la redirección activa, porque Let's Encrypt sigue redirecciones a HTTPS. ## Solución de problemas - **`EXTERNAL-IP` en ``**: el clúster no tiene balanceador de carga. Instala MetalLB con un rango de IPs disponibles, o accede por NodePort: `kubectl get svc traefik -n traefik` muestra el puerto (por ejemplo `80:31080`) y puedes probar con `curl -H "Host: app.example.com" http://ip_de_un_nodo:31080`. - **El Ingress no tiene `ADDRESS` o devuelve 404 para todo**: comprueba que `ingressClassName` coincide con `kubectl get ingressclass` y revisa los logs del controlador con `kubectl logs -n traefik deployment/traefik`. - **502 o 503 en un dominio concreto**: el Service no tiene pods listos o el puerto no coincide. Revisa `kubectl get endpointslices -n demo` y que `port` en el Ingress sea el puerto del Service, no el del contenedor. - **El certificado se queda en `READY False`**: sigue la cadena `kubectl describe certificate demo-tls -n demo`, luego `kubectl get challenges -n demo` y `kubectl describe challenge -n demo`. La causa habitual es que el DNS no apunta a la IP del balanceador o que el puerto 80 no es accesible desde Internet. ## Conclusión Has instalado Traefik como controlador de Ingress, has publicado dos aplicaciones detrás de una única IP con reglas por dominio y por ruta, y has automatizado los certificados HTTPS con cert-manager y Let's Encrypt. Como siguientes pasos, puedes aumentar las réplicas del controlador con `helm upgrade --set deployment.replicas=2` para que no sea un punto único de fallo, explorar los middlewares de Traefik (autenticación, límites de peticiones, cabeceras) o migrar tus rutas a Gateway API, el sucesor de Ingress en Kubernetes.