C
CULTIVA IA
Infraestructura Autoalojada
Cluster Activo — 3 máquinas

Runbook: Clúster Uncloud

Plataforma: Uncloud + uc CLI
Red overlay: 10.210.0.0/16 WireGuard
Proxy: Caddy + Let's Encrypt
Dominio: cultivaia.com
Topología del Clúster
machine-1
Frontend + Proxy
Madrid · 4 vCPU / 8 GB RAM
IP pública: 91.216.48.10 · WireGuard: 10.210.0.1
caddy cultivaia-web n8n redis
machine-2
API + Workers
Frankfurt · 4 vCPU / 8 GB RAM
IP pública: 5.161.102.30 · WireGuard: 10.210.0.2
caddy cultivaia-api cultivaia-app redis
db-machine
Solo DB
Madrid · 2 vCPU / 4 GB RAM
Sin IP pública · WireGuard: 10.210.0.3
postgres redis
WireGuard Mesh
Los 3 nodos se comunican cifrados en la red overlay 10.210.0.0/16. Postgres y Redis solo escuchan en la red interna — ningún puerto expuesto a Internet.
Bootstrapping del Clúster
1
Inicializar primera máquina
Crea el clúster y configura WireGuard + Caddy
2
Unir máquinas restantes
Incorpora Frankfurt y db-machine al mesh
3
Deploy inicial
Sube imágenes y despliega todos los servicios
setup.sh
bash
# 1. Bootstrapear clúster en machine-1 (Madrid) uc machine init root@91.216.48.10 \ --name machine-1 \ --network 10.210.0.0/16 \ --public-ip auto # 2. Añadir Frankfurt uc machine add root@5.161.102.30 \ --name machine-2 \ --public-ip auto # 3. DB-machine sin IP pública ni Caddy uc machine add root@10.10.0.5 \ --name db-machine \ --public-ip none \ --no-caddy # Verificar topología uc machine ls
DNS — Wildcard cultivaia.com
cultivaia.com
A
91.216.48.10
*.cultivaia.com
A
91.216.48.10
app.cultivaia.com
CNAME
cultivaia.com
api.cultivaia.com
CNAME
cultivaia.com
n8n.cultivaia.com
CNAME
cultivaia.com
Con el wildcard *.cultivaia.com → 91.216.48.10, cada nuevo subdominio funciona sin cambiar DNS. Caddy obtiene TLS de Let's Encrypt automáticamente.
compose.yaml — Despliegue completo CULTIVA IA
compose.yaml
yaml
# CULTIVA IA — compose.yaml para Uncloud services: cultivaia-web: build: ./apps/web image: ghcr.io/cultivaia/web:{{gitsha 7}}.{{gitdate "20060102"}} x-ports: - cultivaia.com:4321/https - www.cultivaia.com:4321/https x-machines: machine-1 environment: API_URL: http://cultivaia-api:3000 cultivaia-api: build: ./apps/api image: ghcr.io/cultivaia/api:{{gitsha 7}}.{{gitdate "20060102"}} x-ports: - api.cultivaia.com:3000/https x-machines: - machine-2 environment: DATABASE_URL: postgres://cultivaia:${DB_PASS}@postgres:5432/cultivaia_prod REDIS_URL: redis://redis:6379 NODE_ENV: production deploy: replicas: 2 cultivaia-app: build: ./apps/admin image: ghcr.io/cultivaia/app:{{gitsha 7}}.{{gitdate "20060102"}} x-ports: - app.cultivaia.com:3001/https x-machines: - machine-2 environment: NEXT_PUBLIC_API_URL: https://api.cultivaia.com NEXTAUTH_URL: https://app.cultivaia.com NEXTAUTH_SECRET: ${NEXTAUTH_SECRET} n8n: image: n8nio/n8n:latest x-caddy: | n8n.cultivaia.com { reverse_proxy {{upstreams 5678}} { import common_proxy } basicauth /webhook/* { webhook $2a$14$hashed_pass_here } } x-machines: machine-1 volumes: - n8n-data:/home/node/.n8n environment: DB_TYPE: postgresdb DB_POSTGRESDB_HOST: postgres DB_POSTGRESDB_DATABASE: n8n DB_POSTGRESDB_PASSWORD: ${DB_PASS} postgres: image: postgres:16 x-machines: db-machine # Pinado en máquina dedicada volumes: - pg-data:/var/lib/postgresql/data environment: POSTGRES_USER: cultivaia POSTGRES_PASSWORD: ${DB_PASS} POSTGRES_MULTIPLE_DATABASES: cultivaia_prod,n8n # Sin x-ports: DB solo accesible vía overlay WireGuard redis: image: redis:7-alpine x-machines: db-machine volumes: - redis-data:/data # Sin x-ports: Redis solo accesible vía overlay WireGuard volumes: pg-data: redis-data: n8n-data:
Servicios desplegados
Servicio URL pública DNS interno Estado
cultivaia-web cultivaia.com cultivaia-web Running
cultivaia-api api.cultivaia.com cultivaia-api ×2 Running
cultivaia-app app.cultivaia.com cultivaia-app Running
n8n n8n.cultivaia.com n8n Running
postgres — interno — postgres db-machine
redis — interno — redis db-machine
caddy global caddy Global
CI/CD — GitHub Actions
git push
main
build
Docker
uc build
--push
push images
uc deploy
--no-build
zero-downtime
.github/workflows/deploy.yml
yaml
name: Deploy CULTIVA IA on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install uc CLI run: | curl -sSL https://get.uncloud.run | sh - name: Build & Push images run: uc build --push env: UC_CLUSTER_TOKEN: ${{ secrets.UC_TOKEN }} - name: Deploy (zero-downtime) run: uc deploy --no-build env: UC_CLUSTER_TOKEN: ${{ secrets.UC_TOKEN }} DB_PASS: ${{ secrets.DB_PASS }} NEXTAUTH_SECRET: ${{ secrets.NEXTAUTH_SECRET }}
Comandos Operacionales
ops-cheatsheet.sh
bash
# ── ESTADO DEL CLUSTER ────────────────────────── uc machine ls # Listar máquinas y estado WG uc service ls # Servicios y réplicas uc ps # Todos los contenedores del cluster uc caddy config # Ver Caddyfile generado actual # ── LOGS Y DEBUG ──────────────────────────────── uc logs -f cultivaia-api uc logs --since 1h n8n uc service inspect postgres uc exec cultivaia-api # Abrir shell uc exec postgres psql -U cultivaia cultivaia_prod # ── ESCALADO ──────────────────────────────────── uc scale cultivaia-api 4 # Escalar a 4 réplicas uc scale cultivaia-api 2 # Reducir # ── ROLLBACK ──────────────────────────────────── uc deploy --recreate # Forzar recreación # ── VOLÚMENES ─────────────────────────────────── uc volume ls uc volume ls -m db-machine # ── CONTEXTOS ─────────────────────────────────── uc ctx ls uc ctx use prod
Errores Frecuentes y Soluciones
Error Solución
Editar Caddyfile directamente Usar x-caddy en compose o --caddyfile en uc service run
Proxy a HTTPS upstream con cert self-signed (ej. BMC) Añadir transport http { tls_insecure_skip_verify } en el bloque Caddy
uc caddy config sin bloques de usuario Socket admin de Caddy no alcanzable — revisar uc inspect caddy y uc logs caddy
Contenedor no alcanza IP LAN externa Verificar que el host de Caddy tiene ruta de red al target (iptables/routing del VPS)
Volúmenes perdidos tras uc service rm Los volúmenes nombrados persisten. Solo los anónimos se borran automáticamente
deploy falla: imágenes no encontradas Usar uc deploy (build+push+deploy) en lugar de --no-build en primer deploy
Exponer dispositivos externos vía Caddy (NAS, Panel Admin VPS)
Para exponer el panel web del proveedor de VPS (IP LAN interna) sin levantar un contenedor real, se usa un contenedor pause como ancla del servicio con Caddyfile personalizado.
nas.caddyfile + comando uc
bash
# 1. Crear el snippet Caddyfile para el NAS interno cat > ~/nas.caddyfile <<'EOF' https://nas.cultivaia.com { reverse_proxy https://192.168.1.100 { transport http { tls_insecure_skip_verify # cert self-signed del NAS } } log } EOF # 2. Registrar como servicio "pause" en Uncloud uc service run \ --name nas-proxy \ --caddyfile ~/nas.caddyfile \ --machines machine-1 \ registry.k8s.io/pause:3.9 # 3. Verificar que el bloque aparece en Caddy uc caddy config | grep nas.cultivaia.com