Vite (con build)
Objetivo: desplegar una app Vite + React desde GitLab, con build en el servidor, dominio, HTTPS y auto-deploy. Requiere: Conectar GitLab y Sitio estático. Resultado:
https://testvitehtml.inmersys.devsirviendo eldist/compilado por NGINX.ℹ️ Aquí el build ocurre en el servidor. Si el droplet se queda sin RAM (
exit code 137) o quieres deploys de segundos, usa Build local + Registry: misma imagen, pero compilada en tu máquina.
⚠️ Cambio de enfoque: Docker en vez de Nixpacks
Antes esta guía usaba Nixpacks. Ya no. Ahora el build se hace con un Dockerfile propio en el repo, sirviendo el dist/ con NGINX.
Por qué se cambió:
| Problema con Nixpacks | Con Docker + NGINX |
|---|---|
Elegía Node 18 por default → build roto (styleText) | Fijas la versión en el FROM (node:22) |
| Servía con Caddy → fallback SPA sin resolver (404 al recargar rutas) | NGINX con try_files ... /index.html → SPA resuelto |
| Contrato implícito (infería todo) | Control total y explícito del build y del serve |
| Config vive fuera del repo | Todo (Dockerfile, nginx.conf) commiteado → repo autosuficiente |
Diferencias con el estático
| Estático | Vite (Docker) | |
|---|---|---|
| Build Type | Static | Dockerfile |
| ¿Construye? | ❌ | ✅ npm ci + npm run build (dentro del contenedor) |
| Sirve con | NGINX (Dokploy) | NGINX (nuestro, con fallback SPA) |
| Versión de Node | irrelevante | fijada en el FROM del Dockerfile |
1. DNS
Registro A para testvitehtml → IP del droplet. Verificar con dig +short testvitehtml.inmersys.dev.
2. Archivos en el repositorio
Con el enfoque Docker, tres archivos viven en la raíz del repo. Este es el corazón de la guía.
⚠️
dist/NO se commitea (está en el.gitignorede Vite). El build lo genera el contenedor. Sidist/se coló al repo:git rm -r --cached dist && git commit -m "chore: ignore dist" && git push
Dockerfile
Build multi-stage: primero compila con Node, luego copia solo el dist/ a una imagen NGINX ligera.
# ---- Build stage ----
FROM node:22-alpine AS build
WORKDIR /app
# Instala dependencias con cache eficiente
COPY package.json package-lock.json ./
RUN npm ci
# Copia el resto y compila
COPY . .
RUN npm run build
# ---- Serve stage ----
FROM nginx:1.27-alpine AS serve
# Config de nginx con fallback SPA
COPY nginx.conf /etc/nginx/conf.d/default.conf
# Copia el build de Vite (carpeta dist)
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Claves:
node:22-alpine→ fija la versión de Node. Adiós al error de Node 18. (Ajusta el número a lo que pida tuengines.)- Multi-stage → la imagen final es solo NGINX +
dist/, sinnode_modules(mucho más liviana). COPY package*.jsonantes queCOPY . .→ aprovecha la cache de Docker: si no cambian las deps, no reinstala.
nginx.conf
Config de NGINX. Lo importante es el fallback SPA (try_files ... /index.html), que resuelve el 404 al recargar rutas internas de React Router.
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
# Fallback SPA: cualquier ruta que no sea un archivo real
# sirve index.html para que React Router maneje el enrutado.
# Equivalente al "_redirects /* /index.html 200" de Netlify.
location / {
try_files $uri $uri/ /index.html;
}
# Cache agresivo para los assets con hash de Vite (immutable)
location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
try_files $uri =404;
}
# No cachear el index.html para que siempre tome la versión nueva
location = /index.html {
add_header Cache-Control "no-cache";
}
# Compresión
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml;
gzip_min_length 256;
}
Claves:
try_files $uri $uri/ /index.html→ el fallback SPA. Sin esto,/mi-rutarecargada da 404./assets/con cacheimmutable→ los archivos de Vite llevan hash en el nombre, se pueden cachear un año.index.htmlconno-cache→ cada deploy toma la versión nueva sin recargas forzadas.
.dockerignore
Evita copiar basura al contexto de build (más rápido, imagen más limpia).
node_modules
dist
.git
.gitignore
Dockerfile
.dockerignore
*.log
.env.local
.vscode
README.md
3. Configuración en Dokploy
Create Service → Application (nombre: vitejs1).
General → Provider
| Campo | Valor |
|---|---|
| Provider | tab Gitlab 🦊 → Dokploy DEV |
| Repository / Branch | dokploy-test / build/vite1 |
| Build Path | / (donde vive el Dockerfile y el package.json) |
General → Build Type
Selecciona Dockerfile (ya no Nixpacks).
| Campo | Valor |
|---|---|
| Build Type | Dockerfile |
| Docker File | (vacío → usa Dockerfile de la raíz) |
| Docker Context Path | (vacío → usa .) |
| Docker Build Stage | (vacío → construye el último stage, serve) |
Los tres campos de abajo (Docker File, Context Path, Build Stage) se dejan vacíos: los defaults (
Dockerfile,., último stage) son justo lo que necesitamos. Solo se tocan en monorepos o Dockerfiles con stages nombrados que quieras targetear.
Domains
Host testvitehtml.inmersys.dev · Path / · Container Port 80 (⚠️ viene 3000; NGINX escucha en 80) · HTTPS ON · Let's Encrypt.
4. Auto-deploy
Procedimiento general en Auto-deploy con webhooks. Lo específico: el wildcard pattern del webhook es build/vite1 (la branch de esta Application), y el nombre en GitLab debe distinguirlo del resto (dokploy dev build vite 1).
Sin webhook: git push → General → botón Deploy (siempre clona el último commit de la branch y reconstruye la imagen).
Errores de esta fase
| Síntoma | Causa | Solución |
|---|---|---|
SyntaxError: ... 'styleText' | Node viejo en el FROM | Subir la versión: FROM node:22-alpine |
No encuentra package.json | Build Path en /dist o Dockerfile mal ubicado | Build Path = /, Dockerfile en la raíz |
| 404 al recargar rutas internas | Falta el fallback SPA | try_files $uri $uri/ /index.html; en nginx.conf |
| App no responde / 502 | Container Port mal | Ponerlo en 80 (NGINX escucha en 80) |
| Build lentísimo / imagen gigante | Sin .dockerignore | Agregar .dockerignore (excluir node_modules, dist, .git) |
| Cambios no se ven tras deploy | index.html cacheado | no-cache en location = /index.html |
Errores generales en Errores comunes.