Despliegue
Cómo ejecutar NexoSaaS: stack de Docker, proxy inverso del host, dominio base y TLS.
Lo que estás implementando
| Component | Role |
|---|---|
| app (PHP-FPM) | Laravel control plane |
| nginx (compose) | Serves control plane + generated tenant vhosts |
| horizon | Queue workers (provision, backups, mail, etc.) |
| scheduler | schedule:work (billing lifecycle, backup waves, …) |
| mysql | Control plane + per-install tenant databases (Docker) |
| redis | Cache, sessions, queues |
| Host agent | Creates tenant paths, nginx snippets, SSL, DBs |
Los sitios de NexoPOS del cliente no son servicios Compose separados. El agente escribe archivos en HOST_AGENT_HOME_BASE, y nginx incluye sus vhosts.
1. Instalación de Docker
Requisitos previos
- Motor de Docker + Docker Compose v2
- Puertos libres (predeterminados): 8080 (HTTP), 3307 (host de MySQL), 6380 (host de Redis)
Pasos
# From the repository root
cp deploy/.env.docker.example .env
# Optional: set a stable public URL early (production example)
# APP_URL=https://app.yourdomain.com
# APP_ENV=production
# APP_DEBUG=false
docker compose -f deploy/docker-compose.yml up -d --build
Los puntos de entrada de Compose normalmente generan APP_KEY y ejecutan migraciones cuando está configurado. Sembrar el catálogo + el administrador predeterminado una vez:
docker compose -f deploy/docker-compose.yml exec app php artisan db:seed --force
Cuentas presembradas predeterminadas (solo para desarrollo):
| Role | Password | |
|---|---|---|
| Platform admin | [email protected] | password |
| Test user | [email protected] | password |
Abre el plano de control: http://localhost:8080 (o tu APP_URL).
Preparación:
docker compose -f deploy/docker-compose.yml exec app php artisan platform:launch-check
Variables de entorno importantes
Los secretos del producto (Stripe, SMTP, GitHub PAT, S3, política de dominio) están en Admin → Settings, no como variables de entorno en tiempo de ejecución.
| Variable | Typical Docker | Notes |
|---|---|---|
| APP_URL | http://localhost:8080 | Must match the URL browsers use (payment return URLs) |
| APP_ENV / APP_DEBUG | local / true | Production: production / false |
| PLATFORM_BASE_DOMAIN | localhost | Seed-only default for Admin Domain base_domain |
| HOST_AGENT_DRIVER | local | Docker/CI. Production VPS isolation: script |
| HOST_AGENT_HOME_BASE | /home | Tenant homes + .platform/nginx-enabled |
| HOST_AGENT_LOCAL_REAL_CLONE | false | true = real NexoPOS tarball (needs network + GitHub) |
| HOST_AGENT_TENANT_DB_* | MySQL service | Per-install DB creation in Docker |
| QUEUE_CONNECTION | redis | Required for Horizon |
Modos del agente anfitrión
| Driver | Use when |
|---|---|
| local | Docker / evaluation. Same path layout as production, shared FPM, no real useradd. |
| script | Real Linux VPS: OS users, per-user FPM, system nginx, true isolation. |
Compose no es, por sí solo, una historia completa de aislamiento de producción multi-tenant. Usa un script en un VPS cuando necesites una separación de tenants a nivel de producción.
2. Aloja Nginx delante del stack
Forma de producción recomendada:
Internet
│
▼
Host Nginx / Caddy (TLS termination, :443)
│ proxy_pass → 127.0.0.1:8080
▼
Compose nginx (control plane + tenant includes)
│
▼
Compose app (PHP-FPM) + tenant docroots on volume
Ejemplo de host Nginx (plano de control)
Reemplaza los nombres de host y el puerto upstream por los tuyos.
# /etc/nginx/sites-available/nexosaas-control.conf
server {
listen 80;
server_name app.yourdomain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name app.yourdomain.com;
# ssl_certificate /etc/letsencrypt/live/app.yourdomain.com/fullchain.pem;
# ssl_certificate_key /etc/letsencrypt/live/app.yourdomain.com/privkey.pem;
client_max_body_size 64M;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 120s;
}
}
Conjunto:
APP_URL=https://app.yourdomain.com
Laravel confía en los encabezados reenviados cuando se configura para tu configuración de proxy; asegúrate de que la aplicación vea HTTPS correctamente para que las URL firmadas y las cookies se mantengan seguras.
Marcador de captura de pantalla: Navegador abierto en el inicio de sesión del plano de control en tu URL HTTPS pública
Nombres de host del inquilino
- Docker / agente local: las configuraciones de nginx del tenant se escriben en {HOST_AGENT_HOME_BASE}/.platform/nginx-enabled/*.conf y se incluyen mediante el nginx de Compose (deploy/docker/nginx/default.conf).
- Navegador local: http://{install-subdomain}.localhost:8080 a menudo funciona sin /etc/hosts.
- Producción: El DNS para *.base_domain (y los dominios personalizados) debe llegar al mismo borde que puede enrutar a la plataforma nginx (o al nginx del VPS administrado por el agente del script).
Si el proxy inverso del host solo reenvía app.yourdomain.com, los subdominios del inquilino necesitan, ya sea:
- el mismo proxy con un comodín server_name / separar bloques de servidor, o
- exposición directa de la plataforma nginx / vhosts gestionados por el agente.
Planifica DNS y TLS para los tenants junto con el dominio base (siguiente sección).
3. Dominio base
Las instalaciones obtienen un nombre de host de plataforma gratuito:
{slug}.{base_domain}
Ejemplo: dominio base saas.example.com → acme-store.saas.example.com.
Configurar en el administrador
- Inicia sesión como administrador de la plataforma (2FA fuera de la red local).
- Abrir Admin → Configuración → Dominio.
- Conjunto:
DNS
| Record | Name | Target |
|---|---|---|
| A | app.yourdomain.com (control plane) | VPS public IP |
| A (or CNAME) | *.saas.example.com | Same IP (or load balancer) |
| Optional AAAA | same hosts | Public IPv6 |
PLATFORM_BASE_DOMAIN en .env solo inicializa el valor de Admin en configuraciones vacías; en tiempo de ejecución, usa Admin → Domain.
4. SSL / Let’s Encrypt
Plano de control (proxy inverso del host)
Usando Certbot (ejemplo):
# After DNS for app.yourdomain.com points at the VPS
sudo certbot --nginx -d app.yourdomain.com
# or: certbot certonly --webroot ... then point ssl_certificate paths
Usar Caddy a menudo es más sencillo (HTTPS automático); reverse_proxy a 127.0.0.1:8080.
Certificados del inquilino
En la provisión y verificación de dominios personalizados, el agente host es responsable del TLS para los nombres de host del inquilino (flujo tipo Let’s Encrypt en la ruta del VPS / agente).
Los operadores deben:
- Asegúrate de que el VPS pueda completar HTTP-01 (o el desafío elegido por el agente) para los nombres de host de instalación.
- Establece razonablemente los cambios de dominio verificado por día (predeterminado 3) para que los clientes no agoten los límites de velocidad de LE.
- Mantenga la precisión de la IPv4 pública para que las instrucciones de DNS que vean los clientes sean correctas.
Nota: El Docker local con *.localhost no necesita un LE público para las pruebas de la interfaz de usuario del día a día.
Webhooks de pago
Las puertas de enlace de producción deben llegar a puntos finales HTTPS, por ejemplo:
- https://app.yourdomain.com/webhooks/stripe
- https://app.yourdomain.com/webhooks/paddle
- https://app.yourdomain.com/webhooks/mollie
Configura los secretos de firma en Admin → Configuración → Configuración de facturación.
5. Lista de verificación posterior al despliegue
| Step | Command / action |
|---|---|
| Stack healthy | docker compose -f deploy/docker-compose.yml ps |
| Seed catalog / admin (once) | exec app php artisan db:seed --force |
| Launch check | exec app php artisan platform:launch-check |
| Horizon running | Compose horizon service Up |
| Scheduler running | Compose scheduler service Up |
| First admin usable | Verify email + 2FA |
| Product config | SMTP, payments, GitHub, S3, domain |
| Fake payments | Not allowed in production launch-check |
Comandos útiles de operaciones
# Shell into app
docker compose -f deploy/docker-compose.yml exec app bash
# Logs
docker compose -f deploy/docker-compose.yml logs -f app horizon nginx
# Rebuild frontend assets
docker compose -f deploy/docker-compose.yml run --rm -e DOCKER_FORCE_BUILD_ASSETS=true assets
# Wipe evaluation installs (keeps users/catalog/settings)
docker compose -f deploy/docker-compose.yml exec app \
php artisan platform:reset-evaluation --force