* notes / devops
Dokploy: cómo alojar múltiples proyectos en un mismo servidor
— Cómo pasé mis proyectos de DigitalOcean a un VPS con Dokploy: Docker Swarm por debajo, Traefik enrutando todo, y lo que aprendí para que un solo servidor aguante varias aplicaciones.
Contexto: ¿qué es Dokploy, en una frase?
Dokploy es una plataforma de despliegue open source que instalas en tu propio servidor. La experiencia se parece mucho a Vercel o Heroku, pero todo corre en tu máquina. Por debajo no hay magia: es una capa amable sobre Docker Swarm (con opción de Kubernetes si necesitas algo más grande), enrutamiento HTTP y TLS automático con Traefik, una interfaz web para manejar proyectos, dominios, variables de entorno y bases de datos, y build packs como Nixpacks que detectan el lenguaje del repo y construyen la imagen sin que escribas un Dockerfile.
La promesa es simple: conectas tu repositorio, indicas el dominio, pulsas Deploy y Dokploy clona el código, lo construye, crea un servicio en Swarm, registra las rutas en Traefik y pide el certificado a Let's Encrypt. Cuando todo sale bien, el ciclo completo tarda menos de un minuto.
Anatomía del montaje: un VPS, una entrada, muchos servicios
El esquema lo dice casi todo: el VPS expone los puertos 80 y 443 a través de un único Traefik; cada aplicación se publica como un servicio Docker con etiquetas que Traefik lee para crear sus routers; y los datos persistentes (Postgres, Redis) van en servicios separados con volúmenes nombrados. El aislamiento entre proyectos es lógico, no físico: comparten kernel y red, pero cada contenedor tiene su propio sistema de archivos y su propia configuración en Dokploy.
Implementación: del repositorio al dominio
Con cada proyecto nuevo sigo la misma secuencia. Primero, escribo un Dockerfile sencillo si hace falta; si es una app Next.js estándar, dejo que Nixpacks se encargue. Para Next.js con output: 'standalone', este Dockerfile mínimo me ha funcionado bien:
FROM node:20-alpine AS deps
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install --frozen-lockfile
FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ENV NEXT_TELEMETRY_DISABLED=1
RUN corepack enable && pnpm build
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
EXPOSE 3000
CMD ["node", "server.js"]Segundo, creo el proyecto en la interfaz de Dokploy, lo conecto al repositorio de GitHub (un webhook se encarga de los redeploys), configuro el dominio blog.dominio.com y añado las variables de entorno. Al pulsar Deploy, Dokploy genera y aplica un stack de Swarm con etiquetas de Traefik como estas:
services:
blog:
image: registry.dokploy.internal/blog:sha-abc123
networks: [dokploy-network]
deploy:
labels:
- "traefik.enable=true"
- "traefik.http.routers.blog.rule=Host(`blog.dominio.com`)"
- "traefik.http.routers.blog.entrypoints=websecure"
- "traefik.http.routers.blog.tls.certresolver=letsencrypt"
- "traefik.http.services.blog.loadbalancer.server.port=3000"Esas etiquetas son el idioma común del montaje: cualquier despliegue futuro, pase o no por Dokploy, solo tiene que respetarlas para integrarse en el mismo Traefik. Tercero, si el proyecto necesita base de datos, creo un servicio gestionado desde la interfaz (Postgres, MySQL, MongoDB o Redis). En realidad es otro contenedor con un volumen persistente, y me da una cadena de conexión interna tipo postgres://user:pass@blog-db:5432/blog que solo funciona dentro de la red Docker del proyecto.
Variables, secretos y la disciplina de los entornos
Dokploy separa las variables de entorno de las variables de construcción: las primeras se inyectan al ejecutar; las segundas, durante el docker build. Parece un detalle menor, pero evita que un secreto quede guardado en una capa de la imagen y termine publicado en un registro por error. Para cada proyecto con varios entornos (desarrollo, staging, producción) creo entornos separados con sus dominios y variables, y sigo una regla fija: los secretos viven en Dokploy, nunca en el repositorio, ni siquiera cifrados.
Copias de seguridad: la parte aburrida que te salva
El error más caro en este montaje es asumir que el VPS va a estar ahí para siempre. No es así. Para las bases de datos, Dokploy hace copias automáticas hacia almacenamiento compatible con S3 (yo uso Backblaze B2 por precio); las tengo diarias, con catorce días de retención. Para los volúmenes de aplicación (imágenes subidas por usuarios, archivos estáticos) tengo una tarea programada que sincroniza con rclone a otro bucket. Y una vez al mes hago el ejercicio incómodo de restaurar una copia en una máquina virtual desechable, solo para confirmar que las copias funcionan de verdad.
La trampa: meter demasiadas cosas en muy poco
Una vez que el primer proyecto funciona, es tentador seguir metiendo aplicaciones hasta saturar el VPS. Lo descubrí con la sexta: una de las apps empezó a tener picos de latencia raros, y docker stats mostró que un build de otra app se estaba comiendo toda la CPU y dejaba al resto peleando por lo que quedaba. Lo resolví por tres frentes:
- Limitar los recursos de cada servicio con
deploy.resources.limits, usando valores realistas decpusymemory. - Mover los builds pesados a una máquina aparte (un runner de GitHub Actions con
docker buildx) que publica la imagen en un registro; Dokploy solo hace pull and run. - Reservar el primer núcleo y al menos un giga de RAM para el sistema operativo y para Traefik, porque de él depende todo lo demás.
Hallazgos
Después de varios meses con esto en producción, esto es lo que aprendí:
- El ahorro es real, pero pide disciplina. Dejas de pagar facturas infladas y empiezas a pagar con tu tiempo si no automatizas copias, monitoreo y rollbacks.
- Traefik es el corazón de todo. Cuando algo no responde, la respuesta casi siempre está en sus logs: un certificado caducado, una etiqueta mal escrita, un router duplicado.
- Los builds largos no pueden vivir en la misma máquina que las apps sensibles a latencia. Separarlos, incluso en una infraestructura pequeña, se nota enseguida.
- Las plataformas gestionadas siguen teniendo sentido en algunos casos. Si un proyecto necesita despliegues preview por rama, edge functions y CDN global, pagarle a quien hace eso por oficio es razonable. No todo se mide en dólares al mes; también cuenta el tiempo que no gastas operando servidores.
Closure
Dokploy no es magia: es software que automatiza lo aburrido del self-hosting sin esconder las decisiones importantes. Para mí fue el punto medio que buscaba: la independencia de un VPS sin tener que configurar Traefik a mano cada vez que agrego un dominio. Si llevas tiempo pagando facturas que no van con tu tráfico real, pruébalo. La primera tarde es intensa; las siguientes, pura rutina.