Despliega el negocio de reservas
Pon en línea el sitio web, el panel y la API: Docker Compose en un servidor, una aplicación por host desde su Dockerfile, o Node.js sin más, y luego conecta tu procesador de pagos.
Para el paquete Full Stack
Tres formas de desplegar
| Forma | Ideal para | Qué necesitas |
|---|---|---|
| Docker Compose en un servidor | Un VPS que controlas, las tres aplicaciones juntas | El paquete Full Stack, Docker en el servidor, un proxy inverso para HTTPS |
| Una aplicación por host, desde su Dockerfile | Railway, Render, Fly.io y otros hosts que construyen desde un Dockerfile | La carpeta de cada aplicación; un volumen o MySQL y un bucket para la API |
| Node.js sin más | Cualquier host con Node | Node.js 22 o más reciente y Yarn 4 en el host |
Elijas la forma que elijas, el sitio web y el panel compilan la dirección de la API en su JavaScript cuando se construyen, así que constrúyelos con las direcciones públicas, nunca con las locales.
Docker Compose en un servidor
Copia la carpeta del paquete al servidor y pon un .env junto a docker-compose.yml con las tres direcciones públicas. Los orígenes permitidos de la API, la dirección del panel, la dirección del sitio web a la que un procesador de pagos devuelve al huésped, las direcciones de las fotografías y la dirección de la API de los frontends se construyen todas a partir de ellas.
SITE_URL=https://www.your-company.com
ADMIN_URL=https://admin.your-company.com
API_URL=https://api.your-company.com
MEDIA_HOSTNAME=api.your-company.comMEDIA_HOSTNAME es opcional, un host o varios separados por comas (añade el host de tu bucket cuando las subidas vayan a uno): Next.js redimensiona las fotografías de esos hosts https, y cualquier otra fotografía se carga tal cual. Luego construye e inicia en segundo plano:
docker compose up --build -d- Apunta cada dominio al servidor mediante un proxy inverso que sirva HTTPS (Caddy, Nginx o Traefik), reenviando a los puertos
3030,3031y8000. - Detrás de ese proxy, define
TRUST_PROXY=1enback-end/.env, para que los límites de inicio de sesión, reserva, pregunta y reseña cuenten a cada visitante en lugar de al proxy. El contenedor de la API leeback-end/.envcuando existe, y ahí también van las claves de pagos, IA, R2 y MCP. - Sin un
JWT_SECRETpropio, la API genera uno en su primer inicio y lo guarda en el volumenhollina-data. La base de datos y las subidas también viven en ese volumen, así que haz una copia de seguridad. - El primer inicio carga la empresa de demostración a menos que
SEED_DEMO_DATA=falseesté en el.env. Mantenlo en el primer inicio: la carga inicial es lo que crea las cuentas.
La API desde su Dockerfile
Apunta el host a la carpeta de la API (back-end/ en el Full Stack). La imagen escucha en PORT (8000 salvo que el host defina otro), responde a GET /api/health cuando su base de datos responde, y guarda SQLite y las subidas en /data.
- Monta un volumen persistente en
/data. Sin uno, cada despliegue empieza con una base de datos vacía. - Define
CORS_ORIGINcon las direcciones del sitio web y del panel (separadas por comas),FRONTEND_URLcon la del panel,SITE_URLcon la del sitio web, yPUBLIC_MEDIA_URLcon la dirección pública de la API seguida de/media. - Define
SEED_DEMO_DATA=truepara el primer inicio en un volumen nuevo: el valor por defecto de la imagen esfalse, que crea las tablas sin cuentas. Una base de datos existente nunca se vuelve a cargar. - Define tu propio
JWT_SECRET, o deja que el contenedor genere uno y lo guarde en/data/.jwt-secret. - Define
TRUST_PROXY=1: una plataforma de alojamiento pone un proxy delante del contenedor. - En MySQL (
DB_TYPE=mysqly las cinco variablesDB_*), el contenedor no crea las tablas. Ejecutanode dist/database/seeder.js(empresa de demostración) onode dist/database/sync-schema.js(tablas vacías) una vez con el mismo entorno. - Las subidas en el volumen sobreviven a un nuevo despliegue; para mantenerlas fuera del servidor, define las cinco variables
R2_*para un bucket.
El sitio web y el panel desde sus Dockerfiles
Apunta el host a la carpeta de la aplicación y dale los valores NEXT_PUBLIC_* como argumentos de construcción, no como variables de ejecución: se compilan dentro. Cada imagen sirve el servidor independiente de Next.js en PORT (3030 para el sitio web, 3031 para el panel, salvo que el host defina otro).
| Argumento de construcción | Aplicación | Valor |
|---|---|---|
NEXT_PUBLIC_API_BASE_URL | Ambos | La dirección pública de la API seguida de /api |
NEXT_PUBLIC_SITE_URL | Ambos | La dirección pública del sitio web: las URL canónicas y el mapa del sitio en el sitio web, "Volver al sitio web" y "Ver alojamiento" en el panel |
NEXT_PUBLIC_MEDIA_HOSTNAME | Ambos | Opcional. Los hosts https que sirven las fotografías (el dominio de la API, el de tu bucket, o ambos), separados por comas |
NEXT_PUBLIC_WEBSOCKET_BASE_URL | Panel | La dirección pública de la API sin /api, para las notificaciones en directo |
El servidor del sitio web también consulta la API mientras renderiza los títulos de página, el primer renderizado de las páginas de inicio y de alojamiento, y el mapa del sitio. Con una dirección pública de la API que funcione también desde el servidor, no hace falta nada más; API_INTERNAL_URL en tiempo de ejecución la sustituye cuando el servidor debe usar otra dirección.
Node.js sin más
Construye cada aplicación una vez en el host y luego inicia. Define el .env de los frontends con las direcciones públicas antes de yarn build.
| Aplicación | Construcción | Inicio |
|---|---|---|
| API | yarn build, y luego una vez yarn seed:prod (empresa de demostración) o yarn db:sync:prod (tablas vacías) | yarn start:prod |
| Sitio web de alojamientos | yarn build | yarn start (puerto 3030) |
| Panel de reservas | yarn build | yarn start (puerto 3031) |
La API se enlaza a 0.0.0.0 y lee PORT. Mantén el archivo SQLite y MEDIA_UPLOAD_DIR en un almacenamiento que sobreviva a un despliegue, o usa MySQL y un bucket.
Recibe pagos reales
Una instalación nueva liquida las reservas con tarjeta y PayPal con el simulador incluido, que no cobra nada, hasta que se configura un procesador. Configura aquellos con los que pagarán tus huéspedes: con Stripe solo, el sitio web ofrece tarjeta; con PayPal solo, PayPal; con ambos, los dos, y el pago a la llegada junto a ellos mientras esté activado. En el entorno de la API:
- Stripe:
STRIPE_SECRET_KEY, y un endpoint de webhook enhttps://<your API>/api/webhooks/payments/stripeparacheckout.session.completed,payment_intent.payment_failed,charge.refundedycharge.dispute.created, cuyo secreto de firma va enSTRIPE_WEBHOOK_SECRET. Mientras no esté definido, se rechaza todo webhook de Stripe y una reserva con tarjeta nunca se liquida. - PayPal:
PAYPAL_CLIENT_ID,PAYPAL_CLIENT_SECRET,PAYPAL_ENV=live, y un webhook enhttps://<your API>/api/webhooks/payments/paypalparaPAYMENT.CAPTURE.COMPLETED,PAYMENT.CAPTURE.DENIED,PAYMENT.CAPTURE.REFUNDEDyPAYMENT.CAPTURE.REVERSED, cuyo id va enPAYPAL_WEBHOOK_ID. SITE_URLcon la dirección pública del sitio web: el procesador devuelve allí al huésped a su propia página de reserva.- El pago a la llegada es un interruptor en la pestaña Pagos del panel, activado por defecto.
Tus primeras cuentas
No hay página de registro: las dos cuentas del panel vienen de la carga inicial. Empieza la primera vez con la empresa de demostración, inicia sesión y luego hazla tuya:
- Cambia ambas contraseñas en Ajustes, Seguridad, o crea tu propio Super Admin en Gestión de administradores y elimina las cuentas de la carga inicial.
- Devuelve los alojamientos de demostración a borrador para que el sitio web deje de ofrecerlos. El panel no tiene opción para eliminar un alojamiento, y la API solo elimina uno mientras ninguna de sus reservas ocupe noches, cosa que una reserva completada siempre hace.
- Define el nombre, el eslogan, la descripción, el correo electrónico, el teléfono y la dirección de la empresa en Ajustes, Empresa; el alojamiento principal y los alojamientos destacados en Ajustes, Página de inicio; el pago a la llegada y la política de cancelación en Ajustes, Pagos.
- Renombra o reemplaza los destinos de demostración en Ajustes, Destinos. Un destino que aún tiene alojamientos no se puede eliminar.
Pasar a producción
Antes de que el negocio de reservas sea accesible desde internet:
- En el entorno de la API,
NODE_ENV=productiony unJWT_SECRETlargo y aleatorio propio (Docker genera uno cuando no hay ninguno). En producción la API se niega a iniciar sinCORS_ORIGINy dice qué valor falta. CORS_ORIGINcon el sitio web y el panel,FRONTEND_URLcon el panel,SITE_URLcon el sitio web,PUBLIC_MEDIA_URLcon la API seguida de/media.- Stripe, PayPal o ambos configurados con sus webhooks. Mientras no lo estén, el simulador confirma todas las reservas con tarjeta y PayPal sin cobrar nada, y el registro de inicio de la API dice
Payment simulator active. - La base de datos y las subidas en almacenamiento persistente, con una copia de seguridad.
- La comprobación de estado del host en
/api/health, yTRUST_PROXY=1detrás de un proxy. - Ambos frontends construidos con los valores públicos de
NEXT_PUBLIC_*. - En el sitio web, tu marca en
src/config/brand.config.tsy la política de privacidad enmessages/privacyreescrita para tu empresa y tu país. - Las contraseñas de la carga inicial cambiadas, o las cuentas de la carga inicial eliminadas.
DEMO_MODE=falseen la API yNEXT_PUBLIC_DEMO_MODE=falseen ambos frontends, tal como se entrega.
No para una empresa con datos reales
yarn setup:fresh construye, elimina todas las tablas y carga los datos iniciales cada vez que se ejecuta, y yarn db:reset elimina todas las tablas. Ejecutar yarn seed de nuevo reescribe los alojamientos, reservas y ajustes de la empresa incluidos con sus valores originales, aunque conserva las filas que creaste. Sirven para un entorno completamente nuevo, nunca para un comando de inicio o de construcción.