Despliegue
Paso 4: Configurar los archivos de despliegue
Para que Coolify pueda construir y ejecutar el proyecto hay que crear dos archivos nuevos en la raíz del repositorio (Dockerfile y server-node.mjs) y ajustar vite.config.ts. Haz estos cambios en la copia local que clonaste en tu computadora.
Crear el archivo Dockerfile
Crea un archivo llamado Dockerfile (sin extensión) en la raíz del proyecto con este contenido:
# ─── Stage 1: Build ──────────────────────────────────────────────────────────
FROM node:23-alpine AS build
WORKDIR /app
# Public Supabase config — inlined into the client bundle by Vite at build
# time, so these must be passed as --build-arg (not read from a .env file,
# which is excluded from the build context via .dockerignore).
ARG VITE_SUPABASE_URL
ARG VITE_SUPABASE_PUBLISHABLE_KEY
ARG VITE_SUPABASE_PROJECT_ID
ENV VITE_SUPABASE_URL=$VITE_SUPABASE_URL
ENV VITE_SUPABASE_PUBLISHABLE_KEY=$VITE_SUPABASE_PUBLISHABLE_KEY
ENV VITE_SUPABASE_PROJECT_ID=$VITE_SUPABASE_PROJECT_ID
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
ENV NITRO_PRESET=node-server
RUN npm run build
# ─── Stage 2: Runtime ────────────────────────────────────────────────────────
FROM node:23-alpine AS runner
WORKDIR /app
ENV HOST=0.0.0.0
ENV PORT=3000
ENV NODE_ENV=production
# Reusa el MISMO node_modules que generó el bundle en Stage 1 — evita
# discrepancias de resolución/hoisting entre dos instalaciones separadas,
# que es lo que causaba "createCsrfMiddleware is not a function" en runtime.
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/.output ./.output
EXPOSE 3000
# El preset "node-server" de Nitro ya arranca su propio listener (usa
# HOST/PORT) y sirve los assets públicos al importarse — server-node.mjs
# también hace serve() en el mismo puerto y causa EADDRINUSE, así que se
# ejecuta el entrypoint de Nitro directamente.
CMD ["node", ".output/server/index.mjs"]Crear el archivo server-node.mjs
Crea un archivo llamado server-node.mjs en la raíz del proyecto con este contenido:
import { Hono } from "hono";
import { serve } from "@hono/node-server";
import { serveStatic } from "@hono/node-server/serve-static";
import server from "./.output/server/index.mjs";
const port = Number(process.env.PORT) || 3000;
const hostname = process.env.HOST || "0.0.0.0";
const app = new Hono();
const env = {
ASSETS: {
fetch: async () => new Response("Not Found", { status: 404 }),
},
env: undefined,
};
env.env = env;
app.use("*", serveStatic({ root: "./.output/public" }));
app.all("*", (c) =>
server.fetch(c.req.raw, env, { waitUntil: () => {}, passThroughOnException: () => {} }),
);
serve(
{
fetch: app.fetch,
port,
hostname,
},
(info) => {
console.log(`Listening on http://${info.address}:${info.port}/`);
},
);Ajustar vite.config.ts
En el archivo vite.config.ts agrega la configuración de Nitro con el preset "node-server", para que el build genere un servidor Node:
import { defineConfig } from "@lovable.dev/vite-tanstack-config";
export default defineConfig({
tanstackStart: {
server: { entry: "server" },
},
nitro: {
preset: "node-server",
},
});Detalles adicionales
- Si existe el archivo public/favicon.ico, elimínalo.
- Si en la carpeta src/assets hay archivos .json generados por Lovable (por ejemplo logo-ejemplo.png.asset.json), abre cada uno y completa el campo url con el dominio de las imágenes de los prototipos.


Subir los cambios al repositorio
Cuando los archivos estén listos, guarda los cambios y súbelos a GitHub desde la terminal, dentro de la carpeta del proyecto:
git status
git add .
git commit -m "Add config"
git push
De vuelta en Coolify, abre el menú "Actions" → "Deploy" para lanzar un despliegue manual con los archivos recién subidos.


