--- title: "Cómo usar Tilt para desarrollo local en Kubernetes" description: "Instala Tilt en Ubuntu 24.04 con un clúster kind, escribe un Tiltfile con live update, coordina varios servicios con dependencias y revisa su interfaz web." category: "Ingeniería de Plataformas" language: es source: https://cubepath.com/es/docs/ingeniería-de-plataformas/tilt-desarrollo-local-de-kubernetes updated: 2026-09-25 --- # Cómo usar Tilt para desarrollo local en Kubernetes Tilt es una herramienta de desarrollo local para Kubernetes: lee un archivo `Tiltfile`, construye tus imágenes, las despliega en el clúster y mantiene todo actualizado mientras editas el código. Su punto fuerte es **live update**, que copia los archivos modificados al contenedor en ejecución sin reconstruir la imagen, y una interfaz web que muestra el estado y los logs de todos los servicios. En este tutorial instalarás Tilt en Ubuntu 24.04, lo usarás con un clúster kind y montarás un entorno con una API y una base de datos Redis. ## Requisitos previos - Ubuntu 24.04 LTS en tu equipo o en un VPS de CubePath, con un usuario no root con `sudo`. - Docker Engine instalado y tu usuario en el grupo `docker`. - `kubectl` instalado. - Al menos 4 GB de RAM libres para el clúster local. ## Paso 1: Crear un clúster local con kind Tilt funciona con cualquier clúster, pero por seguridad solo despliega sin preguntar en contextos locales conocidos (kind, k3d, minikube, Docker Desktop). Instala kind; consulta la última versión en `https://github.com/kubernetes-sigs/kind/releases` y ajusta la variable si hay una más reciente: ```bash KIND_VERSION=v0.27.0 curl -fsSLo kind "https://kind.sigs.k8s.io/dl/${KIND_VERSION}/kind-linux-amd64" sudo install -m 0755 kind /usr/local/bin/kind ``` Crea el clúster y comprueba que `kubectl` apunta a él: ```bash kind create cluster --name desarrollo kubectl config current-context ``` ```output kind-desarrollo ``` ## Paso 2: Instalar Tilt Tilt publica un script de instalación que detecta tu arquitectura, descarga el binario de la última versión y lo coloca en tu `PATH`. Descárgalo, revísalo y ejecútalo: ```bash curl -fsSLo install-tilt.sh https://raw.githubusercontent.com/tilt-dev/tilt/master/scripts/install.sh less install-tilt.sh bash install-tilt.sh ``` Comprueba la instalación: ```bash tilt version ``` ```output v0.35.0 ``` ## Paso 3: Preparar una aplicación de ejemplo Crea un proyecto con una API Flask que cuenta visitas en Redis: ```bash mkdir -p ~/hola-tilt/api ~/hola-tilt/k8s cd ~/hola-tilt nano api/app.py ``` ```python import os import redis from flask import Flask app = Flask(__name__) r = redis.Redis(host=os.environ.get("REDIS_HOST", "redis"), port=6379) @app.route("/") def index(): visitas = r.incr("visitas") return f"Hola desde Tilt. Visitas: {visitas}\n" ``` ```bash nano api/requirements.txt ``` ```text flask redis ``` El `Dockerfile` instala las dependencias en una capa propia y arranca Flask con `--debug`, que recarga el proceso cuando cambia un archivo `.py`: ```bash nano api/Dockerfile ``` ```dockerfile FROM python:3.12-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY app.py . CMD ["flask", "--app", "app", "run", "--host", "0.0.0.0", "--port", "8080", "--debug"] ``` Crea los manifiestos. Redis usa una imagen pública; la API usa la imagen `api` que construirá Tilt: ```bash nano k8s/redis.yaml ``` ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: redis spec: replicas: 1 selector: matchLabels: app: redis template: metadata: labels: app: redis spec: containers: - name: redis image: redis:7-alpine ports: - containerPort: 6379 --- apiVersion: v1 kind: Service metadata: name: redis spec: selector: app: redis ports: - port: 6379 ``` ```bash nano k8s/api.yaml ``` ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: api spec: replicas: 1 selector: matchLabels: app: api template: metadata: labels: app: api spec: containers: - name: api image: api ports: - containerPort: 8080 env: - name: REDIS_HOST value: redis --- apiVersion: v1 kind: Service metadata: name: api spec: selector: app: api ports: - port: 80 targetPort: 8080 ``` ## Paso 4: Escribir el Tiltfile El `Tiltfile` se escribe en Starlark, un dialecto de Python. Crea el archivo en la raíz del proyecto: ```bash nano Tiltfile ``` ```python # Manifiestos de Kubernetes que Tilt aplicará k8s_yaml(['k8s/redis.yaml', 'k8s/api.yaml']) # Imagen de la API con live update docker_build( 'api', 'api', live_update=[ fall_back_on(['api/Dockerfile']), sync('api/app.py', '/app/app.py'), sync('api/requirements.txt', '/app/requirements.txt'), run('pip install --no-cache-dir -r /app/requirements.txt', trigger=['api/requirements.txt']), ], ) # Recursos: nombre del workload, puertos y dependencias k8s_resource('redis', labels=['infraestructura']) k8s_resource( 'api', port_forwards='8080:8080', resource_deps=['redis'], labels=['backend'], ) ``` Cómo funciona: - `k8s_yaml` carga los manifiestos. Tilt agrupa cada Deployment con su Service en un **recurso** con el nombre del workload (`redis`, `api`). - `docker_build('api', 'api', ...)` construye la imagen `api` con el directorio `api/` como contexto y la sustituye en el manifiesto que la usa. Con kind, Tilt carga la imagen en el clúster sin necesidad de registro. - `live_update` define los pasos que se ejecutan cuando cambian archivos: `fall_back_on` fuerza un rebuild completo si cambia el `Dockerfile` (debe ir primero), `sync` copia archivos al contenedor y `run` ejecuta un comando solo cuando cambia un archivo de `trigger`. - `resource_deps=['redis']` hace que la API no se despliegue hasta que Redis esté listo. - `labels` agrupa los recursos en la interfaz web. ## Paso 5: Arrancar el entorno con tilt up Inicia Tilt desde el directorio del proyecto: ```bash tilt up ``` ```output Tilt started on http://localhost:10350/ (space) to open the browser (s) to stream logs (--stream=true) (t) to open legacy terminal mode (--legacy=true) (ctrl-c) to exit ``` Abre `http://localhost:10350` para ver la interfaz web: cada recurso aparece con su estado de build, de despliegue y sus logs. Si trabajas en un VPS remoto, abre un túnel SSH desde tu equipo y usa la misma URL en tu navegador local: ```bash ssh -L 10350:localhost:10350 -L 8080:localhost:8080 your_user@your_server_ip ``` Cuando `api` aparezca en verde, prueba el servicio: ```bash curl http://localhost:8080 ``` ```output Hola desde Tilt. Visitas: 1 ``` ## Paso 6: Comprobar el live update Edita `api/app.py` y cambia el texto a `Hola de nuevo`. En la interfaz web, el recurso `api` mostrará una actualización de tipo live update que tarda uno o dos segundos, sin reconstruir la imagen. Flask detecta el cambio y recarga: ```bash curl http://localhost:8080 ``` ```output Hola de nuevo. Visitas: 2 ``` El contador sigue subiendo porque Redis no se ha reiniciado. Si modificas `requirements.txt`, Tilt sincroniza el archivo y ejecuta `pip install` dentro del contenedor. Si modificas el `Dockerfile`, hace un build completo y vuelve a desplegar el pod. > Nota: los cambios de live update solo existen en el contenedor en ejecución. Si el pod se reinicia, Kubernetes arranca de nuevo la última imagen construida y Tilt vuelve a sincronizar en la siguiente actualización. ## Paso 7: Añadir tareas locales `local_resource` ejecuta comandos en tu máquina como parte del entorno, por ejemplo tests que se lanzan al cambiar el código. Añade al final del `Tiltfile`: ```python local_resource( 'tests-api', cmd=['python3', '-c', "import ast; ast.parse(open('api/app.py').read())"], deps=['api/app.py'], labels=['tests'], ) ``` El comando comprueba la sintaxis de `app.py` sin escribir archivos en `api/` (un `py_compile` generaría `__pycache__` dentro del contexto de build y forzaría un rebuild). Tilt recarga el `Tiltfile` automáticamente al guardarlo. El recurso `tests-api` aparece en la interfaz y se ejecuta cada vez que cambia `api/app.py`; si el archivo tiene un error de sintaxis, el recurso se marca en rojo con la salida del comando. En un proyecto real sustituye el comando por tu suite de tests (`pytest`, `go test ./...`, `npm test`). Para tareas costosas que no quieras lanzar en cada cambio, añade `trigger_mode=TRIGGER_MODE_MANUAL` y `auto_init=False`, y ejecútalas desde el botón de la interfaz o con `tilt trigger tests-api`. ## Paso 8: Usar la CLI y detener el entorno Con Tilt en marcha, puedes consultar su estado desde otra terminal: ```bash tilt get uiresources tilt logs api tilt trigger api ``` `tilt trigger` fuerza una actualización de un recurso. Para CI existe `tilt ci`, que levanta el entorno, espera a que todos los recursos estén sanos y termina con código de error si alguno falla. Para detener Tilt pulsa `Ctrl+C` en la terminal de `tilt up`. Los recursos siguen en el clúster hasta que los eliminas: ```bash tilt down ``` ## Solución de problemas **`Stop! staging might be production`**: Tilt se niega a desplegar en contextos que no reconoce como locales. Si el clúster es realmente de desarrollo, autorízalo al principio del `Tiltfile` con `allow_k8s_contexts('nombre-del-contexto')`. **Tilt no detecta cambios en archivos**: en proyectos grandes puedes agotar los watches de inotify. Aumenta el límite de forma persistente: ```bash echo 'fs.inotify.max_user_watches=524288' | sudo tee /etc/sysctl.d/60-inotify.conf sudo sysctl --system ``` **El pod queda en `ErrImagePull`**: el nombre de imagen de `docker_build` debe coincidir exactamente con el `image:` del manifiesto. Revisa también que el contexto de `kubectl` sea el de kind y no uno remoto sin registro configurado. **Live update falla y Tilt hace rebuild completo**: la ruta de destino de `sync` debe existir en el contenedor y el usuario del contenedor debe poder escribir en ella. ## Conclusión Has instalado Tilt, creado un clúster kind y configurado un entorno con dos servicios en el que los cambios de código llegan al pod en segundos gracias a live update, con dependencias entre recursos y tests locales integrados. Como siguientes pasos, puedes cargar manifiestos con `kustomize()` o `helm()` desde el `Tiltfile`, usar las extensiones de `load('ext://...')` para tareas habituales, o configurar un registro local con ctlptl para acelerar los builds en proyectos grandes.