Variables de entorno
Cada variable, qué hace, si es obligatoria y qué ocurre cuando falta.
Todas se validan con Zod al arrancar. La validación es perezosa: ocurre la
primera vez que el sistema necesita una variable, no al importar el módulo. Eso
es lo que permite que next build funcione en CI y en la etapa de construcción
de Docker, donde los secretos no existen.
Cuando falta algo obligatorio, el error dice qué falta y dónde ponerlo, en lugar de fallar más adelante con un mensaje que no lleva a ninguna parte.
Obligatorias
Ninguna de estas se commitea jamás
DATABASE_URL, AUTH_SECRET, R2_SECRET_ACCESS_KEY, R2_ACCESS_KEY_ID y
JOBS_SECRET viven sólo en el panel de Dokploy y en tu gestor de contraseñas.
El archivo .env.example del repositorio documenta las variables sin contener
un solo valor real.
DATABASE_URL
Cadena de conexión a PostgreSQL.
postgresql://royalclean:LA_CONTRASENA@royalclean-db:5432/royalclean?schema=publicEl host es el nombre interno del servicio en Dokploy, no localhost. El
puerto es el 5432: el 5433 que usa el entorno local es un mapeo del
anfitrión para no chocar con un PostgreSQL ya instalado, y dentro de Docker no
aplica.
Si falta: la aplicación no arranca.
Si es incorrecta: arranca, pero /api/health responde degraded con
"database":"unreachable".
AUTH_SECRET
Secreto con el que se firman las sesiones. Mínimo 32 caracteres.
openssl rand -base64 48Si falta o es corto: la aplicación no arranca, y lo dice.
Cambiarlo cierra todas las sesiones
No se pierde ningún dato, pero todo el mundo tendrá que volver a entrar. Cámbialo si sospechas que se filtró; no lo cambies por rutina.
APP_URL
URL pública, con protocolo y sin barra final.
APP_URL=https://crm.royalcleancr.comSe usa para construir enlaces absolutos. Si no coincide con el dominio real, algunos enlaces apuntarán al sitio equivocado.
Almacenamiento de fotografías
Las cuatro van juntas: o están todas, o el almacenamiento queda desactivado. La aplicación arranca igual, pero subir una fotografía devuelve un error claro en lugar de fallar de forma rara.
| Variable | Qué es |
|---|---|
R2_ACCOUNT_ID | Identificador de cuenta de Cloudflare |
R2_ACCESS_KEY_ID | Del token de API de R2 |
R2_SECRET_ACCESS_KEY | Del mismo token. No se vuelve a mostrar |
R2_BUCKET_NAME | royal-clean-evidence |
R2_ENDPOINT
Opcional. Sólo hace falta apuntando a un almacenamiento que no sea R2 —MinIO en
desarrollo, por ejemplo—. Sin ella se deduce del R2_ACCOUNT_ID.
Trabajos programados
JOBS_SECRET
Token que autoriza POST /api/jobs/run.
openssl rand -hex 32Si falta: el endpoint responde 503, no queda abierto. Es deliberado: un trabajo de mantenimiento sin protección es peor que un trabajo que no corre.
Con token incorrecto: responde 401, sin dar pistas.
Regionales
BUSINESS_TIMEZONE
Zona horaria del negocio. Por defecto America/Costa_Rica.
De ella depende qué día del calendario es un servicio. La zona se aplica explícitamente en el código, nunca se hereda del contenedor: un servicio de las 6 de la mañana en Costa Rica no puede caer en otro día según dónde corra el proceso.
NODE_ENV
production en producción. La semilla de datos de prueba se niega a
ejecutarse con este valor.
Opcionales
LOG_LEVEL
debug, info, warn o error. Por defecto info en producción.
RESEND_API_KEY y EMAIL_FROM
Reservadas para una futura integración de correo. Hoy no se usan: el sistema funciona íntegramente sin correo y los avisos son internos. Ver Reglas por confirmar.
Comprobar que están bien
curl https://crm.royalcleancr.com/api/health| Respuesta | Qué significa |
|---|---|
{"status":"ok","database":"ok"} | Todo correcto |
{"status":"degraded","database":"unreachable"} | Revisa DATABASE_URL |
| El contenedor no arranca | Falta una obligatoria. Mira los registros: lo dice |
Los secretos nunca salen en los registros
El registro redacta automáticamente contraseñas, tokens, secretos de almacenamiento, códigos de acceso a propiedades y las firmas de las URLs. No depende de que quien escribe el registro se acuerde de ocultarlos.