Add shared infrastructure base stack
Postgres, Zitadel and SeaweedFS were previously duplicated or fragilely cross-referenced across the Musify, Ping and docker-manager repos. This consolidates them into one place: compose.yml is the production shape (no published ports, joins infra-net), compose.dev.yml is a thin overlay that publishes ports for local development.
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
# Development configuration for compose.yml + compose.dev.yml.
|
||||
# Copy to .env and adjust.
|
||||
#
|
||||
# cp .env.example .env
|
||||
|
||||
# --- PostgreSQL (shared instance, one database per project) -----------------
|
||||
POSTGRES_USER=postgres
|
||||
POSTGRES_PASSWORD=postgres
|
||||
POSTGRES_HOST_PORT=5432
|
||||
|
||||
# --- Zitadel (OIDC) ----------------------------------------------------------
|
||||
# The masterkey MUST be exactly 32 characters. host.docker.internal resolves
|
||||
# to the same address from the browser, from apps on the host and from
|
||||
# inside every project's containers, which is what makes the OIDC issuer
|
||||
# valid everywhere without a shared Docker network in dev.
|
||||
ZITADEL_MASTERKEY=MasterkeyNeedsToHave32Characters
|
||||
ZITADEL_EXTERNAL_DOMAIN=host.docker.internal
|
||||
ZITADEL_EXTERNAL_PORT=8080
|
||||
ZITADEL_EXTERNAL_SECURE=false
|
||||
ZITADEL_DATABASE_PASSWORD=zitadel
|
||||
# Console login: <username>@zitadel.<external domain>
|
||||
ZITADEL_ADMIN_USERNAME=admin
|
||||
ZITADEL_ADMIN_PASSWORD=Password1!
|
||||
|
||||
# --- SeaweedFS (S3) ----------------------------------------------------------
|
||||
S3_ACCESS_KEY=admin_access_key
|
||||
S3_SECRET_KEY=admin_secret_key
|
||||
|
||||
# --- RabbitMQ -----------------------------------------------------------------
|
||||
RABBITMQ_DEFAULT_USER=guest
|
||||
RABBITMQ_DEFAULT_PASS=guest
|
||||
|
||||
# --- pgAdmin (optional, `--profile tools`) -----------------------------------
|
||||
PGADMIN_DEFAULT_EMAIL=admin@infra.dev
|
||||
PGADMIN_DEFAULT_PASSWORD=admin
|
||||
PGADMIN_HOST_PORT=5050
|
||||
@@ -0,0 +1,32 @@
|
||||
# Production configuration for compose.yml (no overlay needed — the base
|
||||
# file is already the production shape: no published ports, everything
|
||||
# reachable only on infra-net).
|
||||
#
|
||||
# cp .env.prod.example .env.prod
|
||||
# chmod 600 .env.prod
|
||||
# docker compose --env-file .env.prod -f compose.yml up -d
|
||||
#
|
||||
# Replace every CHANGE_ME with a strong random value (`openssl rand -hex 24`;
|
||||
# the Zitadel masterkey needs exactly 32 characters) and auth.example.com
|
||||
# with the real hostname the edge (e.g. Musify's nginx) terminates TLS for.
|
||||
|
||||
# --- PostgreSQL ---------------------------------------------------------------
|
||||
POSTGRES_USER=postgres
|
||||
POSTGRES_PASSWORD=CHANGE_ME
|
||||
|
||||
# --- Zitadel (OIDC) ----------------------------------------------------------
|
||||
ZITADEL_MASTERKEY=CHANGE_ME_32_CHARACTERS_EXACTLY_
|
||||
ZITADEL_EXTERNAL_DOMAIN=auth.example.com
|
||||
ZITADEL_EXTERNAL_PORT=443
|
||||
ZITADEL_EXTERNAL_SECURE=true
|
||||
ZITADEL_DATABASE_PASSWORD=CHANGE_ME
|
||||
ZITADEL_ADMIN_USERNAME=admin
|
||||
ZITADEL_ADMIN_PASSWORD=CHANGE_ME
|
||||
|
||||
# --- SeaweedFS (S3) ----------------------------------------------------------
|
||||
S3_ACCESS_KEY=CHANGE_ME
|
||||
S3_SECRET_KEY=CHANGE_ME
|
||||
|
||||
# --- RabbitMQ -----------------------------------------------------------------
|
||||
RABBITMQ_DEFAULT_USER=CHANGE_ME
|
||||
RABBITMQ_DEFAULT_PASS=CHANGE_ME
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
# Environment files (.env, .env.prod)
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.prod.example
|
||||
|
||||
# Runtime state written by the containers themselves
|
||||
zitadel/.output/
|
||||
|
||||
# OS
|
||||
Thumbs.db
|
||||
.DS_Store
|
||||
@@ -0,0 +1,54 @@
|
||||
name: infra-dev
|
||||
|
||||
# Development overlay: publishes every service's port to the host, so any
|
||||
# project's dev stack can reach them at host.docker.internal:<port> instead
|
||||
# of joining infra-net. Adds pgAdmin.
|
||||
#
|
||||
# docker compose -f compose.yml -f compose.dev.yml up -d
|
||||
|
||||
services:
|
||||
postgres:
|
||||
ports:
|
||||
- "${POSTGRES_HOST_PORT}:5432"
|
||||
|
||||
zitadel:
|
||||
ports:
|
||||
- "${ZITADEL_EXTERNAL_PORT}:8080"
|
||||
|
||||
seaweedfs:
|
||||
ports:
|
||||
- "9333:9333" # master / admin UI
|
||||
- "8888:8888" # filer
|
||||
- "8333:8333" # S3 API
|
||||
|
||||
rabbitmq:
|
||||
ports:
|
||||
- "5672:5672" # AMQP
|
||||
- "15672:15672" # management UI
|
||||
|
||||
jaeger:
|
||||
ports:
|
||||
- "16686:16686" # UI
|
||||
- "4317:4317" # OTLP gRPC
|
||||
- "4318:4318" # OTLP HTTP
|
||||
|
||||
# Optional Postgres UI: docker compose ... --profile tools up -d
|
||||
pgadmin:
|
||||
profiles: [tools]
|
||||
image: dpage/pgadmin4:latest
|
||||
container_name: infra-pgadmin
|
||||
environment:
|
||||
PGADMIN_DEFAULT_EMAIL: ${PGADMIN_DEFAULT_EMAIL}
|
||||
PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD}
|
||||
PGADMIN_CONFIG_SERVER_MODE: "False"
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
ports:
|
||||
- "${PGADMIN_HOST_PORT}:80"
|
||||
volumes:
|
||||
- pgadmin_data:/var/lib/pgadmin
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
pgadmin_data:
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
# Shared infrastructure — identity, storage, database, messaging and tracing
|
||||
# for every project under Documentos/ (Musify, Ping, docker-manager, ...).
|
||||
#
|
||||
# This file is the production shape: no published ports, no domains. Every
|
||||
# service is reachable only on the `infra-net` network. Development reaches
|
||||
# it through compose.dev.yml, which publishes the ports to the host instead
|
||||
# of joining a network — see README.md for why.
|
||||
#
|
||||
# dev docker compose -f compose.yml -f compose.dev.yml up -d
|
||||
# prod docker compose --env-file .env.prod -f compose.yml -f compose.prod.yml up -d
|
||||
#
|
||||
# A consuming project's own deploy stack does NOT get its own copy of any of
|
||||
# these services. It only needs this stack to already be up, and its own
|
||||
# one-shot bootstrap jobs (zitadel-init, seaweedfs-init) to create its own
|
||||
# OIDC apps / bucket against the shared instances below.
|
||||
|
||||
networks:
|
||||
infra-net:
|
||||
name: infra-net
|
||||
|
||||
services:
|
||||
# --- Database ---------------------------------------------------------
|
||||
# One instance, one database per consuming project (see postgres-init/).
|
||||
# Zitadel provisions its own `zitadel` database itself, from the admin
|
||||
# credentials below — it does not need an init script.
|
||||
postgres:
|
||||
image: postgres:17-alpine
|
||||
container_name: infra-postgres
|
||||
environment:
|
||||
POSTGRES_USER: ${POSTGRES_USER}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
- ./postgres-init:/docker-entrypoint-initdb.d:ro
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
networks: [infra-net]
|
||||
restart: unless-stopped
|
||||
|
||||
# --- Identity -----------------------------------------------------------
|
||||
# OIDC provider shared by every project. `start-from-init` runs init +
|
||||
# setup (both idempotent) and then starts, so the same command works on a
|
||||
# cold and on a warm volume.
|
||||
zitadel:
|
||||
image: ghcr.io/zitadel/zitadel:latest
|
||||
container_name: infra-zitadel
|
||||
command: start-from-init --masterkeyFromEnv --tlsMode disabled
|
||||
environment:
|
||||
ZITADEL_MASTERKEY: ${ZITADEL_MASTERKEY}
|
||||
# The external domain/port form the token issuer and must be identical
|
||||
# for the browser, every project's apps and the containers (Zitadel
|
||||
# routes by Host).
|
||||
ZITADEL_EXTERNALSECURE: ${ZITADEL_EXTERNAL_SECURE}
|
||||
ZITADEL_EXTERNALDOMAIN: ${ZITADEL_EXTERNAL_DOMAIN}
|
||||
ZITADEL_EXTERNALPORT: ${ZITADEL_EXTERNAL_PORT}
|
||||
ZITADEL_TLS_ENABLED: "false"
|
||||
ZITADEL_DATABASE_POSTGRES_HOST: postgres
|
||||
ZITADEL_DATABASE_POSTGRES_PORT: 5432
|
||||
ZITADEL_DATABASE_POSTGRES_DATABASE: zitadel
|
||||
ZITADEL_DATABASE_POSTGRES_USER_USERNAME: zitadel
|
||||
ZITADEL_DATABASE_POSTGRES_USER_PASSWORD: ${ZITADEL_DATABASE_PASSWORD}
|
||||
ZITADEL_DATABASE_POSTGRES_USER_SSL_MODE: disable
|
||||
ZITADEL_DATABASE_POSTGRES_ADMIN_USERNAME: ${POSTGRES_USER}
|
||||
ZITADEL_DATABASE_POSTGRES_ADMIN_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
ZITADEL_DATABASE_POSTGRES_ADMIN_SSL_MODE: disable
|
||||
# Initial console admin. Login: <username>@zitadel.<external domain>
|
||||
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_USERNAME: ${ZITADEL_ADMIN_USERNAME}
|
||||
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORD: ${ZITADEL_ADMIN_PASSWORD}
|
||||
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORDCHANGEREQUIRED: "false"
|
||||
# Service account + PAT written on init; each consuming project's own
|
||||
# zitadel-init one-shot uses it to create its own project/apps.
|
||||
ZITADEL_FIRSTINSTANCE_ORG_MACHINE_MACHINE_USERNAME: zitadel-admin-sa
|
||||
ZITADEL_FIRSTINSTANCE_ORG_MACHINE_MACHINE_NAME: Admin Service Account
|
||||
ZITADEL_FIRSTINSTANCE_ORG_MACHINE_PAT_EXPIRATIONDATE: "2099-01-01T00:00:00Z"
|
||||
ZITADEL_FIRSTINSTANCE_PATPATH: /output/admin-sa.pat
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ./zitadel/.output:/output
|
||||
networks: [infra-net]
|
||||
restart: unless-stopped
|
||||
|
||||
# --- Storage --------------------------------------------------------------
|
||||
# S3-compatible object store shared by every project. All-in-one: master +
|
||||
# volume + filer + S3 gateway. The S3 identity file is rendered from the
|
||||
# environment on start so the credentials can never drift from the ones
|
||||
# handed to consuming projects. No bucket is created here — each
|
||||
# consuming project's own seaweedfs-init one-shot creates its own.
|
||||
seaweedfs:
|
||||
image: chrislusf/seaweedfs:latest
|
||||
container_name: infra-seaweedfs
|
||||
entrypoint: ["/bin/sh", "-c"]
|
||||
command:
|
||||
- |
|
||||
cat > /tmp/s3.conf <<'JSON'
|
||||
{
|
||||
"identities": [
|
||||
{
|
||||
"name": "infra",
|
||||
"credentials": [{ "accessKey": "${S3_ACCESS_KEY}", "secretKey": "${S3_SECRET_KEY}" }],
|
||||
"actions": ["Admin", "Read", "Write"]
|
||||
}
|
||||
]
|
||||
}
|
||||
JSON
|
||||
exec weed server \
|
||||
-ip.bind=0.0.0.0 \
|
||||
-dir=/data \
|
||||
-master.volumeSizeLimitMB=1024 \
|
||||
-volume.max=0 \
|
||||
-filer \
|
||||
-s3 \
|
||||
-s3.config=/tmp/s3.conf
|
||||
volumes:
|
||||
- seaweedfs_data:/data
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "-q", "-O", "-", "http://localhost:9333/cluster/healthz"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
networks: [infra-net]
|
||||
restart: unless-stopped
|
||||
|
||||
# --- Messaging --------------------------------------------------------
|
||||
# Broker shared by every project. Each project's own consumers/exchanges
|
||||
# are project-specific; only the broker itself lives here.
|
||||
rabbitmq:
|
||||
image: rabbitmq:4-management-alpine
|
||||
container_name: infra-rabbitmq
|
||||
environment:
|
||||
RABBITMQ_DEFAULT_USER: ${RABBITMQ_DEFAULT_USER}
|
||||
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_DEFAULT_PASS}
|
||||
volumes:
|
||||
- rabbitmq_data:/var/lib/rabbitmq
|
||||
healthcheck:
|
||||
test: ["CMD", "rabbitmq-diagnostics", "-q", "ping"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
networks: [infra-net]
|
||||
restart: unless-stopped
|
||||
|
||||
# --- Tracing --------------------------------------------------------------
|
||||
# OTLP collector + trace UI, shared by any project's OpenTelemetry exporter.
|
||||
jaeger:
|
||||
image: jaegertracing/all-in-one:latest
|
||||
container_name: infra-jaeger
|
||||
environment:
|
||||
COLLECTOR_OTLP_ENABLED: "true"
|
||||
networks: [infra-net]
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
seaweedfs_data:
|
||||
rabbitmq_data:
|
||||
@@ -0,0 +1,9 @@
|
||||
-- Runs once, only on first init of the postgres_data volume.
|
||||
-- Zitadel provisions its own "zitadel" database itself, from the
|
||||
-- ZITADEL_DATABASE_POSTGRES_ADMIN_* credentials in compose.yml — it does not
|
||||
-- need to be created here. This script only creates the plain application
|
||||
-- databases each project's own connection string points at.
|
||||
|
||||
CREATE DATABASE musify_db;
|
||||
CREATE DATABASE ping_db;
|
||||
CREATE DATABASE dockermanager_db;
|
||||
Reference in New Issue
Block a user