Desplegar en un servidor
Es el mismo procedimiento que en local, más un proxy que se encarga de los dominios y los certificados HTTPS.
1. Requisitos
| Requisito | Detalle |
|---|---|
| Sistema | Ubuntu 22.04 LTS, Debian 12 o similar |
| CPU / RAM | Mínimo 2 vCPU y 4 GB (compilar los frontends consume bastante) |
| Docker | Docker Engine con el plugin compose |
| Puertos | 80 y 443 abiertos. 3306 NO debe estar abierto a Internet |
| DNS | Un registro A por cada dominio, apuntando a la IP del servidor |
Instalar Docker:
curl -fsSL https://get.docker.com | sudo sh
2. Levantar el proxy (una sola vez por servidor)
nginx-proxy y acme-companion se encargan de todo: detectan los contenedores que
arrancan, generan la configuración de Nginx y piden los certificados de Let's
Encrypt automáticamente. No hay que editar ningún archivo de Nginx ni ejecutar
certbot a mano.
Este proxy da servicio a todas las aplicaciones del servidor, así que se levanta una sola vez, en su propia carpeta:
services:
nginx-proxy:
container_name: nginx-proxy
image: nginxproxy/nginx-proxy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ./nginx-proxy/volumes/vhost.d/:/etc/nginx/vhost.d
- ./nginx-proxy/volumes/conf.d:/etc/nginx/conf.d
- ./nginx-proxy/volumes/certs:/etc/nginx/certs
- ./nginx-proxy/volumes/html:/usr/share/nginx/html
logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "3"
networks:
- dockers_default
nginx-proxy-acme:
container_name: nginx-proxy-acme
image: nginxproxy/acme-companion
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./nginx-proxy/volumes/vhost.d/:/etc/nginx/vhost.d
- ./nginx-proxy/volumes/conf.d:/etc/nginx/conf.d
- ./nginx-proxy/volumes/certs:/etc/nginx/certs
- ./nginx-proxy/volumes/html:/usr/share/nginx/html
- ./nginx-proxy/volumes/acme/:/etc/acme.sh
environment:
- NGINX_PROXY_CONTAINER=nginx-proxy
- DEBUG=true
- DEFAULT_EMAIL=name@domain.com
networks:
dockers_default:
external: true
docker compose up -d
Los dos contenedores y las aplicaciones tienen que compartir la misma red de Docker. Comprueba cómo se llama la tuya con:
docker network ls
En el servidor actual es docker_default (la red del stack docker). Usa ese nombre
en el networks: de arriba y en el de cada aplicación.
Cambia DEFAULT_EMAIL por un correo real: es donde Let's Encrypt avisa si un
certificado no se renueva.
3. Publicar una aplicación
A partir de aquí, publicar cualquier aplicación es añadirle unas variables de entorno. El proxy la detecta sola y le pide el certificado.
services:
mi-aplicacion:
build: .
restart: unless-stopped
environment:
VIRTUAL_HOST: mi-dominio.ejemplo.com
VIRTUAL_PORT: 80
LETSENCRYPT_HOST: mi-dominio.ejemplo.com
LETSENCRYPT_EMAIL: name@domain.com
networks:
- proxy
networks:
proxy:
external: true
name: docker_default
Qué significa cada una:
| Variable | Para qué sirve |
|---|---|
VIRTUAL_HOST | El dominio que servirá este contenedor. |
VIRTUAL_PORT | El puerto dentro del contenedor (normalmente 80). |
LETSENCRYPT_HOST | El dominio para el que se pide el certificado. Casi siempre igual que VIRTUAL_HOST. |
LETSENCRYPT_EMAIL | Avisos de caducidad. Opcional si DEFAULT_EMAIL ya está puesto en acme-companion. |
VIRTUAL_PORT es el puerto interno, no uno del anfitriónEs 80 porque es donde escucha Nginx dentro del contenedor. El proxy conecta por
la red interna de Docker, así que la aplicación no debe publicar puertos:
ports: sobra y sólo serviría para exponerla por fuera del proxy.
4. Desplegar Zertipower
Clona el repositorio:
git clone <url-del-repositorio> ris3cat && cd ris3cat
4.1 Poner los dominios reales
Toda la configuración está en el docker-compose.yml de la raíz. Ábrelo y busca
los bloques marcados con [CAMBIAR EN PRODUCCIÓN]: por defecto apuntan a
localhost. Son estos siete:
| Servicio | Valor | Qué es |
|---|---|---|
backend | FRONTEND_URL | URL pública del Panel de Administración. |
backend | COMPTADOR_FRONTEND_URL | URL pública del Contador. |
backend | GOOGLE_CALLBACK_URL | Debe coincidir exactamente con la URI registrada en Google Cloud Console. En producción es la URL pública del backend, no la del Panel. |
frontend | API_URL | URL pública de la API (argumento de compilación). |
calculadora | API_URL | Igual que el anterior. |
smart-meter | API_URL | Igual que el anterior. |
docs | DOCS_URL | URL pública de esta documentación. |
Aprovecha para revisar también el bloque de correo (SMTP_*) del servicio
backend: con los valores de ejemplo el sistema arranca, pero no envía correo.
API_URL se incrustan al compilarAngular mete la URL de la API dentro del bundle. Después de cambiarla hay que
reconstruir: docker compose up -d --build. Recargar el navegador no basta.
4.2 Poner los secretos
Los dos valores que el compose exige no tienen defecto. Si falta alguno, el stack no arranca y dice cuál.
Con Portainer (recomendado), se ponen en «Environment variables» del stack. A
mano, en un .env junto al compose:
cp .env.production.example .env
DB_PASSWORD=<contraseña-fuerte>
JWT_SECRET=<cadena-larga-y-aleatoria>
Para generar los dos primeros:
openssl rand -base64 32 && openssl rand -base64 48
Además de esos dos, conviene poner estos cinco. No detienen el arranque, pero sin ellos se pierde lo que dependa de cada uno:
GOOGLE_CLIENT_ID=<...apps.googleusercontent.com>
GOOGLE_CLIENT_SECRET=<de Google Cloud Console>
ZERTIAUTH_APP_SECRET=<columna `secret` de la tabla `apps`>
PK=<clave privada, 64 hex>
STRIPE_SECRET_KEY=<sk_live_… o sk_test_…>
ZERTIAUTH_APP_SECRET aquí sí hace faltaSin él, el primer acceso con Google de cada usuario falla. Y con un valor
equivocado es peor que sin él: las carteras se derivan distintas de las
reales, en silencio, y los usuarios pierden el acceso a lo que tengan on-chain.
Tiene que ser el valor exacto de la columna secret.
GOOGLE_CLIENT_ID no es secreto. STRIPE_SECRET_KEY sí, y sólo hace falta para
la compra de EKW desde el Contador.
PK también, y con más motivo: quien la tenga controla la cartera y sus fondos.
En una instalación local el instalador genera una PK al azar para no tener que
preguntarla. Esa clave corresponde a una cartera nueva y vacía: sin gas y sin
permisos sobre los contratos.
Aquí tiene que ir la clave de la cartera que ya los posee. Si dejas la generada,
el despliegue parecerá configurado y fallará en la primera transacción, dentro de
un try/catch que sólo deja una línea en el registro.
El cliente de producción es otro, con su propia URI de retorno: la de la
instalación local apunta a localhost, y fuera de localhost Google sólo acepta
https://. Cómo crearlo, en
Acceso con Google, paso a paso.
4.3 Arrancar
docker compose up -d --build
- Los secretos deben ser propios.
setup.shgeneraDB_PASSWORD,JWT_SECRETyPKal azar: las dos primeras sirven tal cual, peroPKno —es una cartera vacía— y el resto de claves hay que ponerlas a mano. - Cambia la contraseña del usuario
admin, o bórralo. - No publiques el puerto
3306.
5. Comprobaciones
-
https://<dominio>carga y el candado del navegador es válido. -
http://<dominio>redirige ahttps://. - El login funciona.
-
3306no responde desde fuera del servidor. - Hay copias de seguridad programadas de la base de datos.
Si sale 503
Es el error más habitual y casi siempre significa lo mismo: el proxy no encuentra ningún contenedor que reclame ese dominio.
- ¿El contenedor está en la misma red que
nginx-proxy? - ¿Tiene bien escrito
VIRTUAL_HOST? - ¿Está arrancado? —
docker compose ps
Los registros del proxy suelen decir exactamente qué falta:
docker logs nginx-proxy