Installation
Instalar en un portátil local
Ejecuta QuoteNode localmente para evaluación sin exponerlo a clientes.
Instalar en un portátil local
Usa esta ruta cuando quieras probar QuoteNode en un único equipo. Es la forma más rápida de evaluar la aplicación, pero no es un despliegue orientado a clientes.
Cuándo usarlo
- Quieres una instancia de demo o prueba.
- Estás probando la creación de ofertas, PDFs, plantillas, productos y ajustes.
- No necesitas que los clientes abran enlaces públicos de oferta desde otro dispositivo.
Qué no funcionará
- Los enlaces públicos de oferta generados como
http://localhost/...funcionan solo en el mismo portátil. - Los enlaces de correo normalmente no son útiles porque los destinatarios no pueden abrir
localhostdesde sus equipos. - HTTPS normalmente no está configurado para esta topología.
- El portátil debe estar encendido y Docker debe estar en ejecución.
Requisitos previos
- Docker Desktop o Docker Engine con Docker Compose v2.
- Al menos 2,5 GB de RAM disponibles para el stack.
- Al menos 10 GB de espacio libre en disco.
Comprueba tu configuración:
docker --version # Should show 24.0+
docker compose version # Should show v2.x
Paso 1 — Crear un directorio de proyecto
mkdir quotenode && cd quotenode
Paso 2 — Crear el archivo de configuración
Crea un archivo llamado .env con los ajustes mínimos requeridos. Genera tus propios secretos — no uses los valores de ejemplo en producción:
# Database
DB_URL=jdbc:postgresql://postgres:5432/quotenode
DB_USERNAME=quotenode
DB_PASSWORD=change-me-to-something-random-32-chars
DB_NAME=quotenode
# ============================================================
# SECURITY SECRETS — READ THIS BEFORE CONTINUING
# These values are UNIQUE to your installation.
# If you lose this file, you CANNOT regenerate the same secrets.
# SAVE THIS FILE and keep a secure backup.
# ============================================================
DB_ENCRYPTION_KEY=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
TIMING_TOKEN_SECRET=change-me-timing-secret-min-32-chars
PUBLIC_LINK_PASSWORD_SESSION_SECRET=change-me-session-secret-min-32
# Domain (for CORS and Caddy)
CORS_ALLOWED_ORIGINS=http://localhost
DOMAIN=localhost
# La IP del cliente se detecta automáticamente tras proxies internos y CDNs (incluido Cloudflare) — déjalo vacío.
# Defínelo solo para un proxy inusual cuyo par de transporte sea una dirección PÚBLICA a confiar (IP/CIDR o nombre de host).
SECURITY_TRUSTED_PROXIES=
# Application
LOG_LEVEL=INFO
SPRING_PROFILES_ACTIVE=prod
Guarda tu archivo
.envinmediatamente. ElDB_ENCRYPTION_KEYcifra datos sensibles en reposo. Si lo pierdes, los campos cifrados (códigos MFA, contraseña SMTP, claves API) quedarán permanentemente ilegibles. Copia este archivo en un lugar seguro.
Paso 3 — Crear el archivo Docker Compose
Crea un archivo llamado docker-compose.yml:
services:
postgres:
image: postgres:18-alpine
cpus: 2.0
mem_limit: 384m
environment:
POSTGRES_DB: ${DB_NAME}
POSTGRES_USER: ${DB_USERNAME}
POSTGRES_PASSWORD: ${DB_PASSWORD}
PGDATA: /var/lib/postgresql/data # required for the postgres:18 image
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USERNAME}"]
interval: 10s
timeout: 5s
retries: 10
restart: unless-stopped
gotenberg:
image: gotenberg/gotenberg:8
cpus: 2.0
mem_limit: 384m
environment:
LOG_LEVEL: info
healthcheck:
test: ["CMD", "curl", "-fsS", "http://localhost:3000/health"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
backend:
image: ghcr.io/lesisty7/quotenode/quotenode-api:latest
cpus: 2.0
mem_limit: 1280m
depends_on:
postgres: { condition: service_healthy }
gotenberg: { condition: service_healthy }
env_file: .env
environment:
SPRING_PROFILES_ACTIVE: prod
# Single-container eval (WEB_MAINTENANCE backup profile): the backend runs
# scheduled backups in-process, so no separate backup-worker container is needed.
# For production, split into a web backend + a dedicated backup-only worker.
JOBS_MODE: all
BACKUP_RUNTIME_PROFILE: WEB_MAINTENANCE
PDF_ENABLED: "true"
PDF_GOTENBERG_URL: http://gotenberg:3000
volumes:
- backend_uploads:/app/data/uploads
- backend_pdfs:/app/data/pdfs
healthcheck:
test: ["CMD", "curl", "-fsS", "http://localhost:8080/health"]
interval: 15s
timeout: 10s
retries: 5
start_period: 45s
restart: unless-stopped
frontend:
image: ghcr.io/lesisty7/quotenode/quotenode-frontend:latest
cpus: 1.0
mem_limit: 128m
depends_on:
backend: { condition: service_healthy }
ports:
- "80:80"
- "443:443"
healthcheck:
test: ["CMD", "curl", "-fsS", "http://localhost:80/"]
interval: 15s
timeout: 5s
retries: 3
restart: unless-stopped
volumes:
postgres_data:
backend_uploads:
backend_pdfs:
Paso 4 — Iniciar QuoteNode
docker compose up -d
Docker descargará las imágenes de contenedor (esto tarda unos minutos en el primer arranque). Observa el progreso del inicio:
docker compose logs -f backend
Espera hasta ver Started QuoteNodeApplication — eso significa que el backend está listo.
Paso 5 — Abrir QuoteNode
Navega a http://localhost en tu navegador.
Credenciales de acceso por defecto:
| Campo | Valor |
|---|---|
[email protected] | |
| Contraseña | Admin123! |
Cambia la contraseña por defecto inmediatamente tras iniciar sesión (Configuración > Mi perfil > Cambiar contraseña).
¿Qué está ejecutándose?
Tras docker compose up, tienes 4 contenedores:
| Contenedor | Qué hace | Uso de RAM |
|---|---|---|
postgres | Almacena todos tus datos | ~256 MB |
backend | Servidor API Java, lógica de negocio | ~512 MB – 1,5 GB |
gotenberg | Convierte HTML a PDF (basado en Chromium) | ~200 MB |
frontend | Sirve la interfaz web + reverse proxy | ~20 MB |
Total: aproximadamente 1,5 – 2,5 GB de RAM.
Comandos habituales
# Check if all services are running
docker compose ps
# View logs (all services)
docker compose logs -f
# View logs (backend only)
docker compose logs -f backend
# Stop QuoteNode
docker compose down
# Update to the latest version
docker compose pull && docker compose up -d
Mantener seguros tus secretos
Tu archivo .env contiene secretos criptográficos únicos para tu instalación:
| Secreto | Qué protege | Si se pierde |
|---|---|---|
DB_PASSWORD | Acceso a la base de datos | Recuperable — reiniciar vía herramientas de administración PostgreSQL |
DB_ENCRYPTION_KEY | Códigos MFA, contraseña SMTP, clave API FX y todos los campos PII si ENCRYPT_PII=true | Permanentemente ilegible — no se puede descifrar sin la clave original |
TIMING_TOKEN_SECRET | Tokens anti-timing-attack | Regenerar — tokens existentes invalidados, usuarios se re-autentican |
PUBLIC_LINK_PASSWORD_SESSION_SECRET | Sesiones de enlaces públicos | Regenerar — sesiones activas expiran, los enlaces siguen funcionando |
El secreto crítico es
DB_ENCRYPTION_KEY. Todos los demás secretos se pueden regenerar con una molestia menor. PeroDB_ENCRYPTION_KEYcifra datos en reposo — perderlo significa que esos campos desaparecen para siempre.
Recuperación si se pierde .env
Si pierdes tu archivo .env pero tienes una copia de seguridad de base de datos no cifrada (el modo de copia por defecto):
- Crea un nuevo
.envcon nuevos secretos. - Restaura la base de datos desde tu copia de seguridad.
- Todos los datos de negocio (clientes, ofertas, productos, usuarios) serán completamente accesibles.
- Solo se pierden unos pocos campos: códigos MFA (los usuarios se vuelven a registrar), contraseña SMTP (volver a introducir en ajustes), clave API FX (volver a introducir en ajustes). Si
ENCRYPT_PII=trueestaba habilitado, los datos de clientes cifrados también se pierden.
Copias de seguridad
Esta configuración de un solo contenedor ejecuta las copias de seguridad programadas dentro del proceso del backend (JOBS_MODE: all), por lo que no necesitas un contenedor backup-worker independiente. La copia en sí se ejecuta como una tubería de shell (pg_dump → compresión → cifrado) fuera de la JVM, así que su coste de memoria es pequeño — por eso el backend en el portátil está dimensionado en 1280m en lugar de 1g.
Esto es aceptable para un portátil o una instancia de evaluación. Para un despliegue en producción, ejecuta un contenedor worker dedicado backup-only (JOBS_MODE: backup-only) junto a un backend web (JOBS_MODE: web) para que las copias de seguridad nunca compitan por la memoria con el tráfico web. Consulta Copia de seguridad y recuperación.
Migrar de portátil a servidor
No publiques enlaces de portátil a clientes. Cuando estés listo para un despliegue compartido o público, elige una de estas rutas: