Desplegar en Node.js

En esta página

EmDash se ejecuta en Node.js 22.16 o posterior. Esta guía usa SQLite con almacenamiento local o compatible con S3; libSQL y PostgreSQL funcionan de la misma manera en Node.js — consulta Opciones de base de datos.

Prerrequisitos

  • Node.js v22.16.0 o superior
  • Un proveedor de hosting Node.js o VPS

Configuración

Configura EmDash para despliegue en Node.js:

import { defineConfig } from "astro/config";
import node from "@astrojs/node";
import emdash, { local, s3 } from "emdash/astro";
import { sqlite } from "emdash/db";

export default defineConfig({
	output: "server",
	adapter: node({ mode: "standalone" }),
	integrations: [
		emdash({
			database: sqlite({ url: "file:./data/emdash.db" }),
			storage: local({
				directory: "./data/uploads",
				baseUrl: "/_emdash/api/media/file",
			}),
		}),
	],
});

Compilar y ejecutar

  1. Compilar el proyecto:

    npm run build
  2. Iniciar el servidor:

    node ./dist/server/entry.mjs

El servidor se ejecuta en http://localhost:4321 por defecto. Las migraciones se aplican en la primera solicitud. Si la base de datos está vacía y la configuración no se ha completado, tu archivo seed (o el predeterminado integrado si no tienes uno) también se aplica en esa primera solicitud.

Tareas programadas

El programador integrado solo se ejecuta mientras un proceso Node.js está en ejecución. Maneja la publicación programada, tareas de plugins y mantenimiento general.

Mantén al menos un proceso Node.js ejecutándose continuamente en producción. Las tareas programadas se pausan cuando todos los procesos se detienen o duermen.

Sandbox de plugins

Los plugins del marketplace y los listados bajo sandboxed: [] necesitan un runner de sandbox. En Node.js, el runner es @emdash-cms/sandbox-workerd, que ejecuta plugins en un proceso hijo workerd. Plugin Sandbox cubre la instalación, cómo se ejecuta el proceso workerd y sus modos de fallo.

Almacenamiento de producción

Para producción, usa almacenamiento compatible con S3 en lugar del sistema de archivos local:

import emdash, { s3 } from "emdash/astro";

export default defineConfig({
	integrations: [
		emdash({
			database: sqlite({ url: `file:${process.env.DATABASE_PATH}` }),
			storage: s3({
				endpoint: process.env.S3_ENDPOINT,
				bucket: process.env.S3_BUCKET,
				accessKeyId: process.env.S3_ACCESS_KEY_ID,
				secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
				publicUrl: process.env.S3_PUBLIC_URL, // URL CDN opcional
			}),
		}),
	],
});

Docker

Añade un .dockerignore para mantener pequeño el contexto de compilación:

node_modules
dist
.git

Crea un Dockerfile:

FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:22-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./

RUN mkdir -p data

ENV HOST=0.0.0.0
ENV PORT=4321

EXPOSE 4321
CMD ["node", "./dist/server/entry.mjs"]

El archivo seed se lee en tiempo de compilación y se incorpora en el bundle, por lo que no necesita copiarse en la imagen de runtime. Las migraciones se ejecutan en la primera solicitud después del despliegue; el seed se aplica solo cuando la base de datos no tiene colecciones y la configuración no se ha completado — los datos existentes nunca se sobrescriben.

Construye la imagen y ejecuta el contenedor:

docker build -t my-emdash-site .
docker run -p 4321:4321 -v emdash-data:/app/data my-emdash-site

Un archivo Docker Compose gestiona el mismo contenedor con un volumen nombrado:

services:
  emdash:
    build: .
    ports:
      - "4321:4321"
    volumes:
      - emdash-data:/app/data
    restart: unless-stopped

volumes:
  emdash-data:

Inicia el stack en segundo plano:

docker compose up -d

Variables de entorno

Recomendado: clave de cifrado

EMDASH_ENCRYPTION_KEY es la clave para cifrar los secretos de plugins en reposo. La clave se valida al inicio; el cifrado de secretos de plugins la usa una vez habilitada. Establécela en cada despliegue para que los secretos estén protegidos sin un cambio de configuración posterior.

Genera una clave y añade el resultado a tu entorno:

npx emdash secrets generate  # añade el resultado a tu entorno

La clave es proporcionada por ti y nunca se almacena en la base de datos; solo se almacena texto cifrado. Respalda en un lugar duradero (un gestor de contraseñas, KMS o el almacén de secretos de tu equipo) — perderla significa perder cada secreto cifrado con ella.

Opcional: overrides de valor estable

EmDash auto-genera el secreto HMAC de preview y el salt de hash de IP del comentarista y los persiste en la base de datos en el primer uso. Las variables de entorno a continuación los fijan a un valor que tú controlas — útil cuando un proceso separado necesita compartir un secreto con tu sitio principal.

VariableDescripción
EMDASH_PREVIEW_SECRETOverride para el secreto HMAC de preview auto-generado.
EMDASH_IP_SALTOverride para el salt de hash de IP del comentarista auto-generado.
EMDASH_AUTH_SECRETOpcional. Si se establece, se usa como fuente del salt de IP (a menos que EMDASH_IP_SALT también esté establecido, que tiene precedencia), manteniendo los hashes de IP estables para instalaciones que ya dependen de él. Déjalo sin establecer para un nuevo despliegue.

Base de datos y almacenamiento

VariableDescripciónEjemplo
DATABASE_PATHRuta a base de datos SQLite/data/emdash.db
HOSTHost del servidor0.0.0.0
PORTPuerto del servidor4321
S3_ENDPOINTURL del endpoint S3https://xxx.r2.cloudflarestorage.com
S3_BUCKETNombre del bucket S3my-media-bucket
S3_ACCESS_KEY_IDClave de acceso S3AKIA...
S3_SECRET_ACCESS_KEYClave secreta S3...
S3_PUBLIC_URLURL pública para medioshttps://cdn.example.com

Almacenamiento persistente

SQLite requiere almacenamiento de disco persistente. Asegúrate de que tu plataforma de hosting proporcione:

  • Un volumen montado o disco persistente
  • Acceso de escritura al directorio de la base de datos
  • Mecanismos de respaldo para el archivo de base de datos

Verificaciones de salud

Añade un endpoint de verificación de salud para balanceadores de carga:

export const GET = () => {
  return new Response("OK", { status: 200 });
};

Configura tu plataforma para verificar /health para sondeos de actividad.