mirror of
https://github.com/mauricioabh/arbpulse.git
synced 2026-07-27 15:47:43 +00:00
f6108d8f8f
Build the Docker image in GitHub Actions, publish to ghcr.io/mauricioabh/arbpulse (latest + sha tags), then SSH into the Hetzner VPS to docker compose pull + up -d with a health-check gate. The VPS no longer builds images, keeping CPU/RAM free for the running apps and making rollbacks a matter of pulling a previous sha tag. - .github/workflows/vps-deploy.yml: build-push (GHCR) + deploy (SSH) jobs - deploy/docker-compose.yml: app image now ghcr.io/mauricioabh/arbpulse - deploy/deploy.sh: pulls from GHCR by default (BUILD=1 for local build), default APP_DIR aligned to /root/projects/arbpulse - docs: deploy/README.md CI/CD section + paths, README deploy section Co-authored-by: Cursor <cursoragent@cursor.com>
131 lines
5.3 KiB
Markdown
131 lines
5.3 KiB
Markdown
# Deploy en VPS (Hetzner) — Arb Pulse
|
|
|
|
Despliegue con Docker Compose para correr 24/7 desde la rama `main`. El contenedor
|
|
`app` (Node + tsx) queda en `127.0.0.1:8080` y un **reverse proxy del host** expone
|
|
HTTPS.
|
|
|
|
## Deploy automático (CI/CD con GHCR) — flujo normal
|
|
|
|
Cada push/merge a `main` dispara `.github/workflows/vps-deploy.yml`:
|
|
|
|
1. **Build en GitHub Actions** → imagen publicada en `ghcr.io/mauricioabh/arbpulse`
|
|
con tags `latest` + `sha-<commit>` (la VPS nunca buildea).
|
|
2. **Deploy por SSH** → en la VPS: `git reset --hard origin/main` (en
|
|
`/root/projects/arbpulse`), `docker compose pull app`, `docker compose up -d app`
|
|
y espera del health check.
|
|
|
|
Secrets del repo: `VPS_SSH_KEY` (deploy key dedicada, solo para esto),
|
|
`VPS_HOST`, `VPS_USER`. Rollback: re-ejecutar el workflow desde un commit
|
|
anterior (`workflow_dispatch`) o en la VPS hacer `docker compose pull` de un
|
|
tag `sha-<commit>` previo.
|
|
|
|
> No editar archivos del repo directamente en la VPS: el deploy hace
|
|
> `git reset --hard` y los pisará. La config local vive solo en `deploy/.env`
|
|
> (no trackeado).
|
|
|
|
Lo que sigue abajo es el **camino manual/bootstrap** (primera instalación o
|
|
fallback). Dos modos:
|
|
|
|
- **Opción A — detrás de nginx existente (recomendado en este VPS).** El servidor ya
|
|
corre nginx en 80/443 con certbot para otras apps (p. ej. `consumet.wayool.com`,
|
|
`openclaw.wayool.com`). Se agrega un vhost para `arbpulse.wayool.com` y punto.
|
|
- **Opción B — Caddy bundleado.** Solo para un server **fresco** sin nada en 80/443.
|
|
|
|
## Prerrequisitos
|
|
|
|
1. VPS Ubuntu/Debian (recomendado 2 vCPU / 2 GB RAM — p. ej. Hetzner CX22).
|
|
2. Subdominio `arbpulse.wayool.com` con registro **A → IP del VPS** (ya creado en
|
|
Cloudflare, ver abajo).
|
|
3. Docker + plugin compose (el `deploy.sh` los instala si faltan).
|
|
|
|
## DNS en Cloudflare (zona wayool.com)
|
|
|
|
- Registro **A**, nombre `arbpulse`, contenido = IP del VPS, **"DNS only" (nube gris)**.
|
|
- Motivo: certbot/Caddy emiten el cert vía HTTP-01 (reto directo al origen) y el
|
|
SSE de `/api/stream` fluye sin buffering del proxy de Cloudflare.
|
|
- Verificar: `dig +short arbpulse.wayool.com` debe devolver la IP del VPS.
|
|
|
|
---
|
|
|
|
## Opción A — detrás del nginx existente (recomendado)
|
|
|
|
No toca tus otras apps ni Caddy. Solo levanta el contenedor `app` en loopback y le
|
|
pone un vhost de nginx con certbot (mismo patrón que consumet/openclaw).
|
|
|
|
```bash
|
|
# 1) Traer y correr el script (1ra vez: crea .env y se detiene). App-only por default.
|
|
curl -fsSL https://raw.githubusercontent.com/mauricioabh/arbpulse/main/deploy/deploy.sh -o /tmp/arbpulse-deploy.sh
|
|
bash /tmp/arbpulse-deploy.sh
|
|
|
|
# 2) Editar el .env (DOMAIN + secretos opcionales)
|
|
nano /root/projects/arbpulse/deploy/.env # DOMAIN=arbpulse.wayool.com ; SENTRY_TRACING=false ; ...
|
|
|
|
# 3) Volver a correr: build + up del contenedor app (127.0.0.1:8080)
|
|
bash /tmp/arbpulse-deploy.sh
|
|
|
|
# 4) Instalar el vhost de nginx y emitir el cert
|
|
sudo cp /root/projects/arbpulse/deploy/nginx/arbpulse.wayool.com.conf /etc/nginx/sites-available/arbpulse.wayool.com
|
|
sudo ln -s /etc/nginx/sites-available/arbpulse.wayool.com /etc/nginx/sites-enabled/
|
|
sudo nginx -t && sudo systemctl reload nginx
|
|
sudo certbot --nginx -d arbpulse.wayool.com
|
|
```
|
|
|
|
El vhost (`deploy/nginx/arbpulse.wayool.com.conf`) trae `proxy_buffering off` y
|
|
timeouts largos para que el SSE funcione. certbot copia el bloque `location` al
|
|
server TLS que genera, así que los ajustes SSE se mantienen en HTTPS.
|
|
|
|
## Opción B — Caddy bundleado (server fresco, sin nginx)
|
|
|
|
Solo si nada más usa 80/443:
|
|
|
|
```bash
|
|
cd /root/projects/arbpulse/deploy
|
|
cp .env.vps.example .env && nano .env # DOMAIN=arbpulse.wayool.com ...
|
|
WITH_CADDY=1 bash /tmp/arbpulse-deploy.sh # o: docker compose --profile caddy up -d --build
|
|
```
|
|
|
|
---
|
|
|
|
## Verificar
|
|
|
|
```bash
|
|
docker compose -f /root/projects/arbpulse/deploy/docker-compose.yml ps # app healthy/running
|
|
curl -s http://127.0.0.1:8080/api/health # local
|
|
curl -s https://arbpulse.wayool.com/api/health # público (HTTPS)
|
|
```
|
|
|
|
Dashboard: `https://arbpulse.wayool.com` → badge **LIVE** y los 4 exchanges.
|
|
|
|
## Variables de entorno
|
|
|
|
Ver `.env.vps.example`. Claves:
|
|
|
|
- `DOMAIN` — `arbpulse.wayool.com` (usado por Caddy en Opción B; informativo en A).
|
|
- `SENTRY_DSN` — opcional; activa error monitoring.
|
|
- `SENTRY_TRACING` — dejar en `false` (default) para no consumir la cuota free-tier
|
|
de spans en operación 24/7.
|
|
- `UPSTASH_REDIS_REST_URL` / `UPSTASH_REDIS_REST_TOKEN` — opcionales (rate limit +
|
|
cache). Vacío = desactivado.
|
|
|
|
> Nunca comitees el `.env` real. Solo `.env.vps.example` vive en el repo.
|
|
|
|
## Operación
|
|
|
|
```bash
|
|
cd /root/projects/arbpulse/deploy
|
|
docker compose logs -f app # logs de la app
|
|
docker compose restart app # reiniciar
|
|
docker compose down # detener (app; agrega --profile caddy si aplica)
|
|
bash /tmp/arbpulse-deploy.sh # actualizar manualmente (git pull main + pull GHCR)
|
|
BUILD=1 bash /tmp/arbpulse-deploy.sh # fallback: build local en la VPS
|
|
```
|
|
|
|
## Notas
|
|
|
|
- El puerto 8080 se publica solo en `127.0.0.1` (no expuesto a Internet); el tráfico
|
|
público entra por el reverse proxy del host (nginx o Caddy).
|
|
- La app ya envía `X-Accel-Buffering: no` en el SSE, que nginx respeta para no
|
|
bufferear ese response.
|
|
- Región: Hetzner no tiene Asia; para menor latencia a Binance/Bybit/OKX evalúa un
|
|
VPS en Singapur. Para Kraken (EU) Falkenstein/Helsinki va bien.
|