closes board-viewer-cron-deploy Two-service stack at /opt/stacks/board-viewer/ on VDS: - board-viewer-build (node:22-alpine): cron-loop `while true; do node cli.ts; sleep 300`, writes to shared docker volume `dist` - board-viewer-web (nginx:alpine): serves dist read-only, Traefik route Host(`board.kzntsv.site`) → letsEncrypt + basic-auth middleware Files: - deploy/Dockerfile.build — build image (registry.kzntsv.site/board-viewer-build) - deploy/docker-compose.yml — stack with traefik labels (`proxy` network, same as Gitea) - deploy/nginx.conf — static serving with Cache-Control - deploy/auth.toml.example — Gitea token + board_viewer_repos whitelist - deploy/.env.example — BOARD_VIEWER_USERS for basic-auth (with $$ escape note) - deploy/README.md — DNS setup + image build/push + bootstrap + smoke + DR steps Configs are declarative ([skip-tdd: visual]). Acceptance = deploy + curl (see README §4 'Verify'). Both auth.toml and .env are gitignored. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
66 lines
2.0 KiB
YAML
66 lines
2.0 KiB
YAML
# Deploy: board-viewer
|
|
# Target: /opt/stacks/board-viewer/docker-compose.yml on VDS
|
|
#
|
|
# Prereqs:
|
|
# 1. DNS A-record: board.kzntsv.site → VDS IP
|
|
# 2. Traefik network `proxy` exists (it does — used by Gitea et al)
|
|
# 3. ./auth.toml contains gitea_url, gitea_token, board_viewer_repos
|
|
# 4. ./.htpasswd built via: htpasswd -nbB <user> <pass> >> ./.htpasswd
|
|
#
|
|
# Apply:
|
|
# cd /opt/stacks/board-viewer && docker compose up -d
|
|
#
|
|
# First build is local: see deploy/README.md for image build & push to
|
|
# registry.kzntsv.site, or build on host via `docker compose build`.
|
|
|
|
services:
|
|
build:
|
|
image: registry.kzntsv.site/board-viewer-build:latest
|
|
container_name: board-viewer-build
|
|
restart: unless-stopped
|
|
environment:
|
|
BOARD_CONFIG_PATH: /etc/board-viewer/auth.toml
|
|
BOARD_DIST_DIR: /output
|
|
BOARD_TICK_SEC: "300"
|
|
volumes:
|
|
- ./auth.toml:/etc/board-viewer/auth.toml:ro
|
|
- dist:/output
|
|
networks:
|
|
- proxy
|
|
|
|
web:
|
|
image: nginx:alpine
|
|
container_name: board-viewer-web
|
|
restart: unless-stopped
|
|
env_file:
|
|
- .env
|
|
volumes:
|
|
- dist:/usr/share/nginx/html:ro
|
|
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
|
|
networks:
|
|
- proxy
|
|
depends_on:
|
|
- build
|
|
labels:
|
|
traefik.enable: "true"
|
|
traefik.docker.network: "proxy"
|
|
|
|
traefik.http.routers.board-viewer.entrypoints: "websecure"
|
|
traefik.http.routers.board-viewer.rule: "Host(`board.kzntsv.site`)"
|
|
traefik.http.routers.board-viewer.tls.certresolver: "letsEncrypt"
|
|
traefik.http.routers.board-viewer.middlewares: "board-viewer-auth"
|
|
|
|
# Basic-auth users live in .env as BOARD_VIEWER_USERS
|
|
# (one or more `user:bcrypt-hash` entries, comma-separated, with $$ for $).
|
|
# See deploy/README.md "One-time setup" step 3.
|
|
traefik.http.middlewares.board-viewer-auth.basicauth.users: "${BOARD_VIEWER_USERS}"
|
|
|
|
traefik.http.services.board-viewer.loadbalancer.server.port: "80"
|
|
|
|
volumes:
|
|
dist:
|
|
|
|
networks:
|
|
proxy:
|
|
external: true
|