Registry + Dockerfile (GitLab)
La solución real al problema de los builds y a la gestión de disco: en vez de compilar en el droplet (Nixpacks), se compila en la máquina local y el servidor solo hace
pullde una imagen lista. Se evalúa y descarta DOCR a favor de GitLab.
1. El problema
Hoy Dokploy usa Nixpacks: el droplet descarga el código y compila ahí mismo. La doc advierte que un build puede pedir picos de RAM/CPU y tumbar el servidor completo, incluidas las apps sanas.
El servidor de producción no debería compilar nunca. Solo debería encender cosas ya compiladas.
2. Vocabulario: 3 palabras que separar
Imagen ≠ Contenedor:
| Qué es | Dónde vive | |
|---|---|---|
| Imagen | El paquete congelado: app + Node + dependencias | En el registry |
| Contenedor | Esa imagen corriendo | En el droplet |
Al registry subes imágenes, no contenedores.
Registry ≠ Repositorio ≠ Tag:
registry.gitlab.com / inmersys/clickupapi-back : 1.0.0
└──── registry ────┘ └───── repositorio ─────┘ └tag┘
- Registry → el servidor, el "Drive" de imágenes.
- Repositorio → el nombre de UNA imagen (no una carpeta con varias cosas — es la imagen).
- Tag → las versiones (
1.0.0,latest).
⚠️ registro/clickup_back y registro/disney_app no son un repo con dos cosas — son dos repositorios distintos. Nombre distinto = repo distinto.
3. Por qué se descarta DOCR
| Plan | Costo | Repos | Storage |
|---|---|---|---|
| Starter | $0 | 1 | 500 MiB |
| Basic | $5/mes | 5 | 5 GiB |
| Professional | $20/mes | Ilimitados | 100 GiB |
Una imagen de Node/Express pesa 200–400 MB. Con el Starter solo cabe 1 proyecto, y 500 MiB se llenan con una sola app. Meter todo en un repo por tag funciona técnicamente, pero ensucia el historial y no esquiva el límite de 500 MiB.
Conclusión: DOCR gratis no da. DOCR útil cuesta ≥ $5/mes. La restricción es $0 adicionales. (La config paso a paso sigue en Registry DigitalOcean por si se paga.)
4. La solución: GitLab Container Registry
| GitLab Registry | DOCR Starter | |
|---|---|---|
| Costo | $0 | $0 |
| Repositorios | Ilimitados | 1 |
| Storage | No cuenta contra el límite de 10 GiB | 500 MiB |
| Cuenta nueva | Ninguna — ya usas GitLab | Sí |
El punto crítico — minutos de CI: docker build + docker push desde tu máquina no es un pipeline. Es tu laptop hablando por HTTPS con el registry, igual que subir a un Drive. No consume ni un minuto de los 400 gratis.
Cada proyecto de GitLab trae su registry incluido, mapeo 1:1 con tus repos:
registry.gitlab.com/inmersys/inmersys-clickupapi-back:1.0.0
registry.gitlab.com/inmersys/disney-spiderman-memorama-web:1.0.0
5. Dockerfile ≠ solución (leer con cuidado)
En Dokploy hay tres configuraciones, y solo una resuelve el problema:
| Configuración | ¿Quién compila? | ¿Sirve? |
|---|---|---|
| Git + Build Type Nixpacks | El droplet | ❌ |
| Git + Build Type Dockerfile | El droplet (solo cambia el builder) | ❌ |
| Source Type: Docker (imagen ya construida) | Tu máquina — el droplet solo hace pull | ✅ |
Cambiar a Dockerfile por sí solo NO te salva — el servidor sigue compilando. Lo que salva es pasar a
Source Type: Dockerapuntando a una imagen del registry. El Dockerfile es el medio, no el fin.
6. El flujo completo
| Paso | Dónde | Consumo del droplet |
|---|---|---|
docker build | Tu máquina | — |
docker push | Red | — |
docker pull | Droplet | Casi nada |
| Encender el contenedor | Droplet | Lo normal de la app |
Beneficio extra: lo que probaste localmente es exactamente lo que corre en producción, bit por bit. Hoy con Nixpacks el droplet construye algo que tú nunca viste.
7. Implementación
a) Dockerfile en el repo (ejemplo Express)
FROM node:22-alpine AS base
WORKDIR /app
FROM base AS deps
COPY package*.json ./
RUN npm ci --omit=dev
FROM base AS runner
ENV NODE_ENV=production
COPY --from=deps /app/node_modules ./node_modules
COPY . .
EXPOSE 5000
CMD ["node", "src/index.js"]
Node fijo en 22 (no >=22), para no repetir el problema de reproducibilidad de Nixpacks.
b) .dockerignore (obligatorio)
node_modules
.env
.git
dist
⚠️ Nunca metas el .env en la imagen.
c) Token en GitLab
Proyecto → Settings → Access Tokens (o Deploy Tokens). Scopes: read_registry + write_registry.
d) Build + push (desde tu máquina)
docker login registry.gitlab.com -u USUARIO -p TOKEN
docker build \
-t registry.gitlab.com/inmersys/queruva-back:1.0.0 \
-t registry.gitlab.com/inmersys/queruva-back:latest .
docker push --all-tags registry.gitlab.com/inmersys/queruva-back
Doble tag siempre. Si todo se llama latest, la versión anterior queda huérfana y no hay rollback.
e) Registrar el registry en Dokploy
Settings → Registry → URL registry.gitlab.com, tu usuario, y el token (con read_registry basta para el pull). Se hace una vez para todas las apps.
f) Configurar la app
Source Type: Docker- Imagen:
registry.gitlab.com/inmersys/queruva-back:latest - Container Port: el real (
5000Express,80Vite+nginx) - Environment: igual que ahora — las env vars las inyecta Dokploy, no van en la imagen
- Deploy
g) Rollback
Cambias la imagen a :1.0.0 → Redeploy. Eso es todo.
8. Detalles que muerden
- Env vars: viven en Dokploy, no en la imagen. La imagen es idéntica en dev y prod; lo que cambia es lo que se le inyecta.
- Arquitectura: un compañero con Mac M1/M2 genera imágenes
arm64que no arrancan en el droplet. Debe usardocker build --platform linux/amd64 .... - Dokploy no se entera solo: tras el
push, avísale con el botón Deploy o disparando el Webhook URL concurl(ver Auto-deploy). - Vite es distinto: las
VITE_*se hornean en build, no se inyectan al encender. Van como--build-argen eldocker build, no como Environment. Puerto80.
9. El trade-off honesto
Vale $0, pero:
Los deploys dependen de tu máquina. Si estás en junta o de vacaciones y hay un hotfix, alguien más necesita Docker, el token y hacer el build a mano.
Evolución natural (para después): mover estos comandos a un .gitlab-ci.yml — el pendiente de API Express. Como Inmersys usa 2 de 400 minutos/mes, sigue siendo $0 y desaparece la dependencia de la laptop. Pero primero hazlo a mano, para dominar cada pieza.
📖 Relacionado: El problema de los builds · Gestión de disco · Registry DigitalOcean · Providers