From 94e36086657baecc476b7c1fd99abe8ea4fe92b1 Mon Sep 17 00:00:00 2001 From: wheelz Date: Fri, 5 Jun 2026 16:33:09 +0000 Subject: [PATCH] feat: add ProxCenter swarm stack draft --- .gitignore | 4 + proxcenter-compose.yml | 163 +++++++++++++++++++++++++++++++++++++++++ proxcenter.env.example | 54 ++++++++++++++ 3 files changed, 221 insertions(+) create mode 100644 .gitignore create mode 100644 proxcenter-compose.yml create mode 100644 proxcenter.env.example diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4ad4eda --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +# Real deployment environment files can contain secrets. +*.env +!.env.example +!*.env.example diff --git a/proxcenter-compose.yml b/proxcenter-compose.yml new file mode 100644 index 0000000..23765c2 --- /dev/null +++ b/proxcenter-compose.yml @@ -0,0 +1,163 @@ +# ============================================ +# ProxCenter Community Edition — Docker Swarm Stack +# ============================================ +# Deploy (do NOT run until volumes/secrets exist): +# set -a; . ./proxcenter.env; set +a +# docker stack deploy -c proxcenter-compose.yml proxcenter +# +# Alternative if you prefer to render with docker compose first: +# docker compose --env-file proxcenter.env -f proxcenter-compose.yml config \ +# | docker stack deploy -c - proxcenter +# +# Prerequisites — create external volumes before first deploy: +# docker volume create proxcenter_data +# docker volume create postgres_data +# +# Named volumes in Swarm are node-local by default. Both services are +# pinned to a labeled node so proxcenter_data and postgres_data stay on +# the same host. Label the intended primary Swarm node before deploying: +# docker node update --label-add proxcenter.storage=true +# If you use a distributed volume driver (NFS/Longhorn/etc.), remove or +# change the placement constraints. +# +# Optional: use Docker Secrets instead of env vars for sensitive values. +# Create secrets before deploying, then uncomment the secrets: blocks: +# printf 'your-app-secret' | docker secret create app_secret - +# printf 'your-nextauth-secret' | docker secret create nextauth_secret - +# Then in the frontend service, swap the APP_SECRET / NEXTAUTH_SECRET +# env vars for APP_SECRET_FILE / NEXTAUTH_SECRET_FILE and uncomment +# the secrets: section on the service and at the bottom of this file. +# ============================================ + +services: + + # ======================================== + # Frontend (Next.js + WebSocket Proxy) + # ======================================== + frontend: + image: ghcr.io/adminsyspro/proxcenter-frontend:${VERSION:-latest} + ports: + - "3000:3000" + environment: + - NODE_ENV=production + # Build the Postgres connection string from individual vars so + # POSTGRES_PASSWORD only needs to be set once (not duplicated in DATABASE_URL). + - DATABASE_URL=postgresql://${POSTGRES_USER:-proxcenter}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-proxcenter}?schema=public + - APP_SECRET=${APP_SECRET} + - NEXTAUTH_SECRET=${NEXTAUTH_SECRET} + - NEXTAUTH_URL=${NEXTAUTH_URL:-http://localhost:3000} + - APP_URL=${APP_URL:-http://localhost:3000} + # Docker Secrets alternative — comment out APP_SECRET / NEXTAUTH_SECRET above + # and uncomment these if you prefer file-based secrets: + # - APP_SECRET_FILE=/run/secrets/app_secret + # - NEXTAUTH_SECRET_FILE=/run/secrets/nextauth_secret + volumes: + - proxcenter_data:/app/data + # secrets: + # - app_secret + # - nextauth_secret + networks: + - proxcenter + # depends_on is intentionally omitted: it is ignored by docker stack deploy. + # Swarm relies on healthcheck + restart_policy to handle startup ordering. + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:3000/api/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s + deploy: + replicas: 1 + placement: + constraints: + # Keep the node-local proxcenter_data volume on the intended primary node. + - node.labels.proxcenter.storage == true + restart_policy: + condition: on-failure + delay: 5s + max_attempts: 5 + window: 120s + update_config: + parallelism: 1 + delay: 10s + order: start-first + failure_action: rollback + rollback_config: + parallelism: 1 + delay: 5s + + # ======================================== + # Postgres (application database) + # ======================================== + postgres: + image: postgres:16-alpine + environment: + - POSTGRES_USER=${POSTGRES_USER:-proxcenter} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + - POSTGRES_DB=${POSTGRES_DB:-proxcenter} + - POSTGRES_INITDB_ARGS=--encoding=UTF8 --locale=C + volumes: + - postgres_data:/var/lib/postgresql/data + # Port is intentionally not published. Postgres is reachable only + # over the internal overlay network. Uncomment to expose for debugging: + # ports: + # - "127.0.0.1:5432:5432" + networks: + - proxcenter + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-proxcenter} -d ${POSTGRES_DB:-proxcenter}"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 20s + deploy: + replicas: 1 + restart_policy: + condition: on-failure + delay: 5s + max_attempts: 5 + window: 120s + update_config: + parallelism: 1 + delay: 10s + # stop-first so the old container releases its data dir before the new one starts + order: stop-first + failure_action: rollback + rollback_config: + parallelism: 1 + delay: 5s + placement: + constraints: + # Keep the node-local postgres_data volume on the intended primary node. + - node.labels.proxcenter.storage == true + +# ======================================== +# Networks +# ======================================== +networks: + proxcenter: + driver: overlay + attachable: true + +# ======================================== +# Volumes (must be created externally) +# ======================================== +# docker volume create proxcenter_data +# docker volume create postgres_data +volumes: + proxcenter_data: + external: true + postgres_data: + external: true + +# ======================================== +# Secrets (uncomment after creating them) +# ======================================== +# printf 'your-app-secret' | docker secret create app_secret - +# printf 'your-nextauth-secret' | docker secret create nextauth_secret - +# +# secrets: +# app_secret: +# external: true +# nextauth_secret: +# external: true diff --git a/proxcenter.env.example b/proxcenter.env.example new file mode 100644 index 0000000..136806f --- /dev/null +++ b/proxcenter.env.example @@ -0,0 +1,54 @@ +# ProxCenter Community Edition — Environment Variables +# Copy to proxcenter.env and fill in real values before deploying. +# Usage: +# cp proxcenter.env.example proxcenter.env +# edit proxcenter.env +# set -a; . ./proxcenter.env; set +a +# docker stack deploy -c proxcenter-compose.yml proxcenter +# +# Docker stack deploy does not support --env-file directly. If you do not +# want to source the env file, render the stack first: +# docker compose --env-file proxcenter.env -f proxcenter-compose.yml config \ +# | docker stack deploy -c - proxcenter +# +# NEVER commit the real proxcenter.env file. Only commit this example. + +# ======================================== +# Image version pinning (optional) +# ======================================== +# Leave commented to use the latest tag, or pin to a specific release: +# VERSION=1.2.3 +VERSION=latest + +# ======================================== +# Application URLs +# ======================================== +# Public-facing URL of your ProxCenter instance. +# Used for NextAuth callbacks, redirects, and link generation. +NEXTAUTH_URL=https://proxcenter.example.com +APP_URL=https://proxcenter.example.com + +# ======================================== +# Security secrets (required) +# ======================================== +# Generate strong random values: +# openssl rand -base64 32 +# +# Docker Secrets alternative: instead of setting these here, create +# Docker Swarm secrets and use APP_SECRET_FILE / NEXTAUTH_SECRET_FILE +# in the compose file. See comments in proxcenter-compose.yml. +APP_SECRET=change-me-generate-with-openssl-rand-base64-32 +NEXTAUTH_SECRET=change-me-generate-with-openssl-rand-base64-32 + +# ======================================== +# Postgres credentials (required) +# ======================================== +# These are used by both the postgres service (to initialise the DB) +# and the frontend service (to build the DATABASE_URL connection string). +POSTGRES_USER=proxcenter +POSTGRES_PASSWORD=change-me-use-a-strong-password +POSTGRES_DB=proxcenter + +# DATABASE_URL is built automatically from the variables above. +# Override only if pointing the frontend at an external Postgres instance: +# DATABASE_URL=postgresql://proxcenter:password@external-host:5432/proxcenter?schema=public