Some checks failed
ci/woodpecker/push/web Pipeline failed
Co-authored-by: Jason Woltje <jason@diversecanvas.com> Co-committed-by: Jason Woltje <jason@diversecanvas.com>
111 lines
3.9 KiB
YAML
111 lines
3.9 KiB
YAML
# =============================================================================
|
|
# jasonwoltje.com — Docker Swarm / Portainer stack
|
|
# =============================================================================
|
|
#
|
|
# Deploy target: w-docker0 (10.1.1.45), Portainer endpoint 7.
|
|
# Routing:
|
|
# jasonwoltje.com / www.jasonwoltje.com -> web (Next.js + Payload CMS)
|
|
#
|
|
# Ingress pattern (mirrors MosaicStack):
|
|
# Edge Traefik (10.1.1.43) terminates TLS
|
|
# -> per-swarm Traefik on w-docker0 on entrypoint "web" (HTTP)
|
|
# -> web:3000
|
|
#
|
|
# Usage (Portainer):
|
|
# Stacks -> Add Stack -> Git repository
|
|
# URL: https://git.mosaicstack.dev/jason.woltje/professional-website
|
|
# Compose path: docker-compose.swarm.yml
|
|
# Env vars: see .env.example (all required unless marked optional)
|
|
# Deploy
|
|
#
|
|
# Image tag rule: WEB_IMAGE_TAG MUST be an immutable tag (sha-<8> or vX.Y.Z).
|
|
# Never point this stack at `latest`.
|
|
# =============================================================================
|
|
|
|
services:
|
|
web:
|
|
image: git.mosaicstack.dev/jason.woltje/professional-website:${WEB_IMAGE_TAG}
|
|
environment:
|
|
DATABASE_URI: postgresql://${PAYLOAD_POSTGRES_USER}:${PAYLOAD_POSTGRES_PASSWORD}@jasonwoltje_postgres:5432/${PAYLOAD_POSTGRES_DB}
|
|
PAYLOAD_SECRET: ${PAYLOAD_SECRET}
|
|
PAYLOAD_PUBLIC_SERVER_URL: https://${SITE_DOMAIN:-jasonwoltje.com}
|
|
NEXT_PUBLIC_SITE_URL: https://${SITE_DOMAIN:-jasonwoltje.com}
|
|
NEXT_PUBLIC_BUILD_SHA: ${WEB_IMAGE_TAG}
|
|
NEXT_PUBLIC_BUILD_REV: ${WEB_IMAGE_TAG}
|
|
TURNSTILE_SITE_KEY: ${TURNSTILE_SITE_KEY:-}
|
|
TURNSTILE_SECRET_KEY: ${TURNSTILE_SECRET_KEY:-}
|
|
NEXT_PUBLIC_UMAMI_SRC: ${NEXT_PUBLIC_UMAMI_SRC:-}
|
|
NEXT_PUBLIC_UMAMI_WEBSITE_ID: ${NEXT_PUBLIC_UMAMI_WEBSITE_ID:-}
|
|
RESEND_API_KEY: ${RESEND_API_KEY:-}
|
|
RESEND_FROM: ${RESEND_FROM:-no-reply@jasonwoltje.com}
|
|
RESEND_TO: ${RESEND_TO:-jason@diversecanvas.com}
|
|
MAUTIC_FORM_URL: ${MAUTIC_FORM_URL:-}
|
|
volumes:
|
|
- media:/app/media
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:3000/api/health || exit 1"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
start_period: 60s
|
|
networks:
|
|
- internal
|
|
- traefik-public
|
|
deploy:
|
|
replicas: 1
|
|
update_config:
|
|
parallelism: 1
|
|
delay: 30s
|
|
order: start-first
|
|
restart_policy:
|
|
condition: on-failure
|
|
delay: 5s
|
|
max_attempts: 5
|
|
window: 120s
|
|
labels:
|
|
- "traefik.enable=true"
|
|
- "traefik.http.routers.jasonwoltje.rule=Host(`${SITE_DOMAIN:-jasonwoltje.com}`) || Host(`www.${SITE_DOMAIN:-jasonwoltje.com}`)"
|
|
- "traefik.http.routers.jasonwoltje.entrypoints=web"
|
|
- "traefik.http.services.jasonwoltje.loadbalancer.server.port=3000"
|
|
# www -> apex 301
|
|
- "traefik.http.middlewares.jasonwoltje-www-redirect.redirectregex.regex=^https?://www\\.${SITE_DOMAIN:-jasonwoltje.com}/(.*)"
|
|
- "traefik.http.middlewares.jasonwoltje-www-redirect.redirectregex.replacement=https://${SITE_DOMAIN:-jasonwoltje.com}/$${1}"
|
|
- "traefik.http.middlewares.jasonwoltje-www-redirect.redirectregex.permanent=true"
|
|
- "traefik.http.routers.jasonwoltje.middlewares=jasonwoltje-www-redirect"
|
|
|
|
postgres:
|
|
image: postgres:17-alpine
|
|
environment:
|
|
POSTGRES_DB: ${PAYLOAD_POSTGRES_DB:-payload}
|
|
POSTGRES_USER: ${PAYLOAD_POSTGRES_USER:-payload}
|
|
POSTGRES_PASSWORD: ${PAYLOAD_POSTGRES_PASSWORD}
|
|
volumes:
|
|
- postgres-data:/var/lib/postgresql/data
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER}"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 5
|
|
start_period: 30s
|
|
networks:
|
|
- internal
|
|
deploy:
|
|
replicas: 1
|
|
restart_policy:
|
|
condition: on-failure
|
|
delay: 5s
|
|
max_attempts: 5
|
|
placement:
|
|
constraints:
|
|
- node.role == manager
|
|
|
|
volumes:
|
|
postgres-data:
|
|
media:
|
|
|
|
networks:
|
|
internal:
|
|
driver: overlay
|
|
traefik-public:
|
|
external: true
|