Problema
Los usuarios que quieren auto‑alojar una aplicación web con funcionalidades modernas (API REST, escaneo de códigos de barra, autenticación OIDC, integración AI) suelen tropezar con tres patrones recurrentes:
- Persistencia de datos: la base de datos se pierde al reiniciar el contenedor o al actualizar la imagen.
- Configuración de credenciales externas: APIs de terceros (OpenFoodFacts, Ollama) requieren claves que deben mantenerse seguras y accesibles para el contenedor.
- Escalado y orquestación: pasar de un entorno de desarrollo con Docker Compose a producción con Kubernetes genera incompatibilidades en volúmenes, variables de entorno y políticas de red.
Estos fallos aparecen tanto en entornos de homelab como en despliegues de pequeña empresa, y el síntoma típico es que la aplicación arranca pero no puede leer datos, se desconecta de servicios externos o se reinicia indefinidamente.
Causa
1. Volúmenes efímeros
Docker crea volúmenes anónimos si no se declara explícitamente -v. En Kubernetes, los emptyDir se destruyen al pod morir, mientras que los hostPath pueden no existir en todos los nodos.
2. Variables de entorno expuestas
Colocar claves API directamente en docker-compose.yml o en values.yaml sin usar secretos lleva a que queden en el historial de Git o en logs del clúster.
3. Diferencias en la red interna
Docker Compose usa una red bridge por defecto; Kubernetes usa ClusterIP y necesita Service y Ingress para exponer puertos. Los contenedores que esperan localhost para comunicarse con la base de datos fallan cuando se despliegan en pods separados.
4. Falta de health checks
Sin HEALTHCHECK en Docker o livenessProbe en Kubernetes, el orquestador no detecta que la aplicación está en estado de error y la reinicia sin parar el problema subyacente.
Solución
Adoptar un enfoque modular que separe persistencia, configuración segura y exposición de red. La solución funciona tanto con Docker Compose como con Helm, permitiendo una migración fluida.
Paso 1: Definir volúmenes nombrados
En Docker Compose:
services:
app:
image: ghcr.io/usuario/app:latest
volumes:
- app-data:/var/lib/app
volumes:
app-data:
En Helm (values.yaml):
persistence:
enabled: true
size: 5Gi
storageClass: standard
Esto garantiza que la base de datos y los archivos de usuario sobrevivan a reinicios y actualizaciones.
Paso 2: Utilizar secretos para credenciales
Docker Compose con archivo .env que no se versiona:
APP_OPENFOODFACTS_KEY=xxxxxxxx
APP_OLLAMA_API_KEY=yyyyyyyy
En Kubernetes, crear un Secret y referenciarlo:
kubectl create secret generic app-secrets \
--from-literal=OPENFOODFACTS_KEY=xxxxxxxx \
--from-literal=OLLAMA_API_KEY=yyyyyyyy
Y en el chart:
envFrom:
- secretRef:
name: app-secrets
Paso 3: Unificar la red con nombres DNS internos
En Docker Compose, declarar una red explícita:
networks:
appnet:
services:
app:
networks:
- appnet
db:
networks:
- appnet
En Kubernetes, usar el nombre del Service como host:
env:
- name: DATABASE_HOST
value: app-db
El contenedor app podrá resolver app-db sin depender de localhost.
Paso 4: Añadir health checks
Docker:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 5s
retries: 3
Kubernetes:
livenessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 15
periodSeconds: 30
readinessProbe:
httpGet:
path: /ready
port: http
initialDelaySeconds: 5
periodSeconds: 10
Los probes evitan reinicios innecesarios y permiten que el orquestador retire pods que no responden.
Paso 5: Opcional – Integración AI local
Si la aplicación usa Ollama para estimar calorías a partir de fotos, montar el modelo como un contenedor separado y comunicarlo vía http://ollama:11434. En Helm, habilitar un sub‑chart:
ollama:
enabled: true
image: ollama/ollama:latest
resources:
limits:
cpu: "1"
memory: "2Gi"
Esto mantiene la IA dentro del clúster y elimina la dependencia de claves externas.
Cuándo aplicar esta solución
- Entornos de homelab donde se desea migrar de Docker Compose a Kubernetes sin rehacer la configuración.
- Aplicaciones con datos críticos (tracking de métricas, historial de peso) que no pueden perderse entre despliegues.
- Servicios que consumen APIs externas y requieren manejo seguro de credenciales.
- Escenarios donde se planea escalar horizontalmente; los volúmenes y la red deben ser compatibles con múltiples réplicas.
No es necesario aplicar todo el stack si la aplicación se ejecuta en un único contenedor sin base de datos persistente ni integración AI. En esos casos, basta con un docker-compose.yml simple y variables de entorno locales.
Código
# docker-compose.yml básico
version: "3.8"
services:
app:
image: ghcr.io/usuario/app:latest
ports:
- "8080:8080"
env_file: .env
volumes:
- app-data:/var/lib/app
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 5s
retries: 3
networks:
- appnet
db:
image: postgres:15
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
volumes:
- db-data:/var/lib/postgresql/data
networks:
- appnet
volumes:
app-data:
db-data:
networks:
appnet:
# Helm install (asumiendo chart llamado selfhosted-app)
helm repo add myrepo https://example.com/charts
helm install myapp myrepo/selfhosted-app \
--set persistence.enabled=true \
--set secret.create=true \
--set secret.openFoodFactsKey=xxxxxxxx \
--set secret.ollamaApiKey=yyyyyyyy
Verificación
- Persistencia: Detén y elimina los contenedores/pods. Vuelve a levantar y verifica que los datos de usuarios siguen presentes (
SELECT * FROM users;en la DB). - Credenciales: Revisa que la aplicación pueda consultar OpenFoodFacts sin errores 401. En logs debe aparecer
Authenticated with OpenFoodFacts. - Health checks: Ejecuta
docker psokubectl get podsy confirma que el estado seahealthy/Ready. - Escalado: Incrementa la réplica a 3 (
docker-compose up --scale app=3okubectl scale deployment myapp --replicas=3) y verifica que todas respondan al endpoint/health.
Notas adicionales
- Cuando uses
hostPathen Kubernetes, asegúrate de que el nodo tenga suficiente espacio y que el path exista; de lo contrario el pod fallará al iniciar. - Mantén el archivo
.envfuera del repositorio y usa.gitignore. En entornos CI/CD, inyecta variables mediante secret managers (GitHub Secrets, GitLab CI variables). - Si la aplicación necesita acceso a la cámara del móvil a través de la API AI, considera exponer el contenedor Ollama solo dentro del clúster y usar un Ingress con TLS para evitar exposición pública.
- Las pruebas de extremo a extremo (E2E) pueden automatizarse con Cypress o Playwright dentro de un contenedor CI; esto ayuda a detectar roturas después de actualizar la base de datos o los modelos AI.