Saltar al contenido principal

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​

RequisitoDetalle
SistemaUbuntu 22.04 LTS, Debian 12 o similar
CPU / RAMMínimo 2 vCPU y 4 GB (compilar los frontends consume bastante)
DockerDocker Engine con el plugin compose
Puertos80 y 443 abiertos. 3306 NO debe estar abierto a Internet
DNSUn 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
El nombre de la red

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:

VariablePara qué sirve
VIRTUAL_HOSTEl dominio que servirá este contenedor.
VIRTUAL_PORTEl puerto dentro del contenedor (normalmente 80).
LETSENCRYPT_HOSTEl dominio para el que se pide el certificado. Casi siempre igual que VIRTUAL_HOST.
LETSENCRYPT_EMAILAvisos de caducidad. Opcional si DEFAULT_EMAIL ya está puesto en acme-companion.
VIRTUAL_PORT es el puerto interno, no uno del anfitrión

Es 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:

ServicioValorQué es
backendFRONTEND_URLURL pública del Panel de Administración.
backendCOMPTADOR_FRONTEND_URLURL pública del Contador.
backendGOOGLE_CALLBACK_URLDebe coincidir exactamente con la URI registrada en Google Cloud Console. En producción es la URL pública del backend, no la del Panel.
frontendAPI_URLURL pública de la API (argumento de compilación).
calculadoraAPI_URLIgual que el anterior.
smart-meterAPI_URLIgual que el anterior.
docsDOCS_URLURL 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.

Los API_URL se incrustan al compilar

Angular 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 falta

Sin é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 producción, no sirve la que genera el instalador

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
Antes de exponerlo a Internet
  • Los secretos deben ser propios. setup.sh genera DB_PASSWORD, JWT_SECRET y PK al azar: las dos primeras sirven tal cual, pero PK no —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 a https://.
  • El login funciona.
  • 3306 no 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.

  1. ¿El contenedor está en la misma red que nginx-proxy?
  2. ¿Tiene bien escrito VIRTUAL_HOST?
  3. ¿Está arrancado? — docker compose ps

Los registros del proxy suelen decir exactamente qué falta:

docker logs nginx-proxy