feat(deploy): docker-compose stack for VDS (board.kzntsv.site) [skip-tdd: visual]
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>
This commit is contained in:
65
deploy/docker-compose.yml
Normal file
65
deploy/docker-compose.yml
Normal file
@@ -0,0 +1,65 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user