Files
Mauricio Barragan f6108d8f8f feat(deploy): automatic VPS deploy via GHCR on push to main
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>
2026-07-19 12:23:49 -06:00

58 lines
1.8 KiB
YAML

# Arb Pulse — VPS deployment.
#
# Default (`docker compose up -d --build`): runs ONLY the app container, bound to
# 127.0.0.1:8080. Put it behind your existing reverse proxy (nginx/Caddy/Traefik)
# on the host — see deploy/nginx/arbpulse.wayool.com.conf. This is the right mode
# when the host already owns ports 80/443 for other apps.
#
# Optional bundled Caddy (`docker compose --profile caddy up -d --build`): only for
# a FRESH server with nothing else on 80/443; Caddy then handles automatic HTTPS.
#
# Requires a ./.env file (copy from .env.vps.example and fill DOMAIN + secrets).
services:
app:
# Normal flow: the image is built by CI (.github/workflows/vps-deploy.yml)
# and pulled from GHCR. `build:` remains for local/manual builds only
# (docker compose build app).
build:
context: ..
dockerfile: Dockerfile
image: ghcr.io/mauricioabh/arbpulse:latest
container_name: arbpulse
restart: unless-stopped
env_file:
- ./.env
# Bind to loopback only; the host reverse proxy forwards public traffic here.
ports:
- "127.0.0.1:8080:8080"
healthcheck:
test: ["CMD", "wget", "-q", "-O", "/dev/null", "http://127.0.0.1:8080/api/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 15s
# Opt-in only via `--profile caddy`. Do NOT enable if the host already runs
# nginx/another proxy on 80/443 — the ports would collide.
caddy:
image: caddy:2-alpine
container_name: arbpulse-caddy
restart: unless-stopped
profiles: ["caddy"]
depends_on:
- app
ports:
- "80:80"
- "443:443"
environment:
- DOMAIN=${DOMAIN}
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
volumes:
caddy_data:
caddy_config: