Files
metabuilder/deployment/docker-compose.stack.yml
rw 45daa18bb1 fix(ci): add Verdaccio to stack and Gate 7 for @esbuild-kit registry
The base-node-deps Docker build failed because .npmrc routes @esbuild-kit
packages to localhost:4873 (Verdaccio), which is unreachable inside BuildKit.

- Add Verdaccio service to docker-compose.stack.yml with patched tarballs
- Start Verdaccio in Gate 7 Tier 1 before base-node-deps build
- Configure buildx with network=host so BuildKit can reach localhost:4873
- Update verdaccio.yaml storage path for container volume mount

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 22:38:17 +00:00

1152 lines
33 KiB
YAML

# MetaBuilder Full Stack
#
# Gateway: nginx on port 80 with welcome portal + subpath routing
# Apps: WorkflowUI, CodeForge, Pastebin, Postgres Dashboard,
# Email Client, Exploded Diagrams, Storybook, Frontend App
# Backend: DBAL C++, Email Service (Flask), Pastebin Backend (Flask)
# Infra: PostgreSQL, MySQL, MongoDB, Redis, Elasticsearch,
# phpMyAdmin, Mongo Express, RedisInsight, Kibana
# Mail: Postfix, Dovecot, SMTP Relay
#
# Optional profiles:
# --profile monitoring Prometheus, Grafana, Loki, Promtail, exporters, Alertmanager
# --profile media Media daemon (FFmpeg/radio/retro), native HTTP streaming, HLS
#
# Usage:
# docker compose -f docker-compose.stack.yml up -d
# docker compose -f docker-compose.stack.yml --profile monitoring up -d
# docker compose -f docker-compose.stack.yml --profile media up -d
# docker compose -f docker-compose.stack.yml --profile monitoring --profile media up -d
#
# Access:
# http://localhost Welcome portal
# http://localhost/workflowui WorkflowUI
# http://localhost/codegen CodeForge IDE
# http://localhost/pastebin Pastebin
# http://localhost/postgres Postgres Dashboard
# http://localhost/emailclient Email Client
# http://localhost/diagrams Exploded Diagrams
# http://localhost/storybook Storybook
# http://localhost/app Frontend App
# http://localhost/api DBAL API
# http://localhost/pastebin-api Pastebin Flask API
# http://localhost/phpmyadmin/ phpMyAdmin (MySQL admin)
# http://localhost/mongo-express/ Mongo Express (MongoDB admin)
# http://localhost/redis-insight/ RedisInsight (Redis admin)
# http://localhost/kibana/ Kibana (Elasticsearch admin)
services:
# ============================================================================
# NPM Registry (Verdaccio) — serves patched @esbuild-kit packages
# ============================================================================
verdaccio:
image: verdaccio/verdaccio:6
container_name: metabuilder-verdaccio
restart: unless-stopped
ports:
- "4873:4873"
volumes:
- ./verdaccio.yaml:/verdaccio/conf/config.yaml:ro
- ./npm-patches:/verdaccio/patches:ro
- verdaccio-storage:/verdaccio/storage
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://127.0.0.1:4873/-/ping"]
interval: 15s
timeout: 5s
retries: 3
start_period: 5s
networks:
- metabuilder
# ============================================================================
# Core Services
# ============================================================================
# PostgreSQL - Primary database
postgres:
image: postgres:15-alpine
container_name: metabuilder-postgres
restart: unless-stopped
ports:
- "5432:5432"
environment:
POSTGRES_USER: ${POSTGRES_USER:-metabuilder}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-metabuilder}
POSTGRES_DB: ${POSTGRES_DB:-metabuilder}
POSTGRES_INITDB_ARGS: "-E UTF8"
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-metabuilder}"]
interval: 10s
timeout: 5s
retries: 5
networks:
- metabuilder
# Redis - Cache layer
redis:
image: redis:7-alpine
container_name: metabuilder-redis
restart: unless-stopped
ports:
- "6379:6379"
command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru
volumes:
- redis-data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 5
networks:
- metabuilder
# Elasticsearch - Search layer
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.11.0
container_name: metabuilder-elasticsearch
restart: unless-stopped
ports:
- "9200:9200"
- "9300:9300"
environment:
- discovery.type=single-node
- xpack.security.enabled=false
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
- cluster.name=metabuilder
- node.name=metabuilder-node-1
volumes:
- elasticsearch-data:/usr/share/elasticsearch/data
healthcheck:
test: ["CMD-SHELL", "curl -f http://127.0.0.1:9200/_cluster/health || exit 1"]
interval: 30s
timeout: 10s
retries: 5
networks:
- metabuilder
# MySQL - Alternative SQL database
mysql:
image: mysql:8.0
container_name: metabuilder-mysql
restart: unless-stopped
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-metabuilder}
MYSQL_USER: ${MYSQL_USER:-metabuilder}
MYSQL_PASSWORD: ${MYSQL_PASSWORD:-metabuilder}
MYSQL_DATABASE: ${MYSQL_DATABASE:-metabuilder}
MYSQL_CHARSET: utf8mb4
volumes:
- mysql-data:/var/lib/mysql
command: --default-authentication-plugin=mysql_native_password --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-u", "root", "-p${MYSQL_ROOT_PASSWORD:-metabuilder}"]
interval: 10s
timeout: 5s
retries: 5
networks:
- metabuilder
# MongoDB - Document database
mongodb:
image: mongo:7.0
container_name: metabuilder-mongodb
restart: unless-stopped
ports:
- "27017:27017"
environment:
MONGO_INITDB_ROOT_USERNAME: ${MONGO_USER:-metabuilder}
MONGO_INITDB_ROOT_PASSWORD: ${MONGO_PASSWORD:-metabuilder}
MONGO_INITDB_DATABASE: ${MONGO_DB:-metabuilder}
volumes:
- mongodb-data:/data/db
healthcheck:
test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')", "--quiet"]
interval: 10s
timeout: 5s
retries: 5
networks:
- metabuilder
# phpMyAdmin - MySQL web admin
phpmyadmin:
image: phpmyadmin:latest
container_name: metabuilder-phpmyadmin
restart: unless-stopped
ports:
- "8081:80"
environment:
PMA_HOST: mysql
PMA_PORT: 3306
PMA_USER: ${MYSQL_USER:-metabuilder}
PMA_PASSWORD: ${MYSQL_PASSWORD:-metabuilder}
PMA_ABSOLUTE_URI: http://localhost/phpmyadmin/
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-metabuilder}
depends_on:
mysql:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://127.0.0.1:80/"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
networks:
- metabuilder
# Mongo Express - MongoDB web admin
mongo-express:
image: mongo-express:latest
container_name: metabuilder-mongo-express
restart: unless-stopped
ports:
- "8082:8081"
environment:
ME_CONFIG_MONGODB_ADMINUSERNAME: ${MONGO_USER:-metabuilder}
ME_CONFIG_MONGODB_ADMINPASSWORD: ${MONGO_PASSWORD:-metabuilder}
ME_CONFIG_MONGODB_URL: mongodb://${MONGO_USER:-metabuilder}:${MONGO_PASSWORD:-metabuilder}@mongodb:27017/?authSource=admin
ME_CONFIG_BASICAUTH: "false"
ME_CONFIG_SITE_BASEURL: /mongo-express
depends_on:
mongodb:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://127.0.0.1:8081/mongo-express/"]
interval: 30s
timeout: 5s
retries: 3
start_period: 15s
networks:
- metabuilder
# RedisInsight - Redis web admin
redisinsight:
image: redis/redisinsight:latest
container_name: metabuilder-redisinsight
restart: unless-stopped
ports:
- "8083:5540"
environment:
RI_PROXY_PATH: /redis-insight
volumes:
- redisinsight-data:/data
depends_on:
redis:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://127.0.0.1:5540/redis-insight/api/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
networks:
- metabuilder
# Kibana - Elasticsearch web admin
kibana:
image: docker.elastic.co/kibana/kibana:8.11.0
container_name: metabuilder-kibana
restart: unless-stopped
ports:
- "5601:5601"
environment:
ELASTICSEARCH_HOSTS: '["http://elasticsearch:9200"]'
SERVER_BASEPATH: /kibana
SERVER_REWRITEBASEPATH: "true"
XPACK_SECURITY_ENABLED: "false"
XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY: "metabuilder-kibana-encryption-key-32ch"
depends_on:
elasticsearch:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://127.0.0.1:5601/kibana/api/status"]
interval: 30s
timeout: 10s
retries: 5
start_period: 60s
networks:
- metabuilder
# DBAL config seeder — copies schemas and SQL templates into named volumes
dbal-init:
build:
context: ..
dockerfile: deployment/config/dbal/Dockerfile.init
container_name: metabuilder-dbal-init
volumes:
- dbal-schemas:/target/schemas/entities
- dbal-templates:/target/templates/sql
networks:
- metabuilder
# DBAL Daemon - C++ backend
dbal:
build:
context: ../dbal
dockerfile: production/build-config/Dockerfile
args:
BUILD_TYPE: Release
BUILDKIT_INLINE_CACHE: 1
container_name: metabuilder-dbal
restart: unless-stopped
ports:
- "8080:8080"
environment:
DBAL_ADAPTER: postgres
DATABASE_URL: postgresql://${POSTGRES_USER:-metabuilder}:${POSTGRES_PASSWORD:-metabuilder}@postgres:5432/${POSTGRES_DB:-metabuilder}
DBAL_CACHE_URL: redis://redis:6379/0?ttl=300&pattern=read-through
DBAL_SEARCH_URL: http://elasticsearch:9200?index=metabuilder&refresh=true
DBAL_SCHEMA_DIR: /app/schemas/entities
DBAL_TEMPLATE_DIR: /app/templates/sql
DBAL_SEED_DIR: /app/seeds/database
DBAL_SEED_ON_STARTUP: "true"
DBAL_BIND_ADDRESS: 0.0.0.0
DBAL_PORT: 8080
DBAL_MODE: production
DBAL_DAEMON: "true"
DBAL_LOG_LEVEL: trace
DBAL_LOG_FORMAT: json
DBAL_LOG_SQL_QUERIES: "true"
DBAL_LOG_PERFORMANCE: "true"
DBAL_AUTO_CREATE_TABLES: "true"
DBAL_ENABLE_METRICS: "true"
DBAL_ENABLE_HEALTH_CHECK: "true"
DBAL_MYSQL_URL: mysql://${MYSQL_USER:-metabuilder}:${MYSQL_PASSWORD:-metabuilder}@mysql:3306/${MYSQL_DATABASE:-metabuilder}
DBAL_MONGODB_URL: mongodb://${MONGO_USER:-metabuilder}:${MONGO_PASSWORD:-metabuilder}@mongodb:27017/${MONGO_DB:-metabuilder}?authSource=admin
DBAL_POOL_MIN_SIZE: 2
DBAL_POOL_MAX_SIZE: 10
DBAL_POOL_IDLE_TIMEOUT_SECONDS: 300
# Security: Admin API authentication and CORS
DBAL_ADMIN_TOKEN: ${DBAL_ADMIN_TOKEN:-069e6487a710300381cd52120eab95d56d7f53beee21479cbeba9128217cbea9}
DBAL_CORS_ORIGIN: ${DBAL_CORS_ORIGIN:-*}
# JWT entity auth — must match JWT_SECRET_KEY used by pastebin-backend Flask app
JWT_SECRET_KEY: "${JWT_SECRET_KEY:-changeme-in-production}"
# Blob storage — powers media service and package repo
DBAL_BLOB_BACKEND: ${DBAL_BLOB_BACKEND:-filesystem}
DBAL_BLOB_ROOT: /app/data/blobs
volumes:
- dbal-schemas:/app/schemas/entities:ro
- dbal-templates:/app/templates/sql:ro
- dbal-logs:/var/log/dbal
- dbal-blobs:/app/data/blobs
depends_on:
dbal-init:
condition: service_completed_successfully
postgres:
condition: service_healthy
mysql:
condition: service_healthy
mongodb:
condition: service_healthy
redis:
condition: service_healthy
elasticsearch:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://127.0.0.1:8080/health"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
networks:
- metabuilder
# WorkflowUI - Visual workflow editor (n8n-style)
workflowui:
build:
context: ..
dockerfile: frontends/workflowui/Dockerfile
args:
NODE_ENV: production
container_name: metabuilder-workflowui
restart: unless-stopped
ports:
- "3001:3000"
environment:
NEXT_PUBLIC_DBAL_API_URL: http://dbal:8080
NEXT_PUBLIC_API_BASE_URL: http://localhost:8080
NODE_ENV: production
PORT: 3000
DBAL_ENDPOINT: http://dbal:8080
depends_on:
dbal:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://127.0.0.1:3000/workflowui"]
interval: 15s
timeout: 5s
retries: 3
start_period: 40s
networks:
- metabuilder
# Postfix - SMTP server (submission on 587, relay on 25)
postfix:
image: boky/postfix:latest
container_name: metabuilder-postfix
restart: unless-stopped
ports:
- "1025:25"
- "1587:587"
environment:
ALLOWED_SENDER_DOMAINS: "localhost emailclient.local metabuilder.local"
POSTFIX_myhostname: "metabuilder.local"
POSTFIX_mynetworks: "127.0.0.0/8 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16"
POSTFIX_smtpd_tls_security_level: "may"
healthcheck:
test: ["CMD-SHELL", "postfix status || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 15s
networks:
- metabuilder
# Dovecot - IMAP/POP3 server with TLS
dovecot:
build:
context: ../frontends/emailclient/deployment/docker/dovecot
dockerfile: Dockerfile
container_name: metabuilder-dovecot
restart: unless-stopped
ports:
- "1143:143"
- "1993:993"
- "1110:110"
- "1995:995"
volumes:
- dovecot-data:/var/mail
healthcheck:
test: ["CMD-SHELL", "doveadm service status imap-login || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
networks:
- metabuilder
# SMTP Relay - Twisted relay pointing at local Postfix
smtp-relay:
build:
context: ../services/smtprelay
dockerfile: Dockerfile
container_name: metabuilder-smtp-relay
restart: unless-stopped
ports:
- "2525:2525"
- "8025:8080"
environment:
SMTP_LISTEN_HOST: "0.0.0.0"
SMTP_LISTEN_PORT: "2525"
HTTP_LISTEN_HOST: "0.0.0.0"
HTTP_LISTEN_PORT: "8080"
GMAIL_HOST: "postfix"
GMAIL_PORT: "25"
GMAIL_USERNAME: "relay@metabuilder.local"
GMAIL_APP_PASSWORD: "relay"
FORWARD_TO: "demo@localhost"
ALLOW_ANY_RCPT: "true"
ADD_X_HEADERS: "true"
MAX_STORE: "500"
depends_on:
postfix:
condition: service_healthy
healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8080/', timeout=3).read()"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
networks:
- metabuilder
# Email Service - Flask backend for IMAP/SMTP operations
email-service:
build:
context: ../frontends/emailclient
dockerfile: deployment/docker/email-service/Dockerfile
container_name: metabuilder-email-service
restart: unless-stopped
ports:
- "8500:5000"
environment:
POSTFIX_HOST: "postfix"
POSTFIX_PORT: "25"
DOVECOT_HOST: "dovecot"
DOVECOT_IMAP_PORT: "143"
DOVECOT_IMAP_SSL_PORT: "993"
DOVECOT_POP3_PORT: "110"
DOVECOT_POP3_SSL_PORT: "995"
DATABASE_URL: "postgresql://${POSTGRES_USER:-metabuilder}:${POSTGRES_PASSWORD:-metabuilder}@postgres:5432/${POSTGRES_DB:-metabuilder}"
REDIS_URL: "redis://redis:6379/1"
CELERY_BROKER_URL: "redis://redis:6379/1"
CELERY_RESULT_BACKEND: "redis://redis:6379/1"
FLASK_ENV: "development"
FLASK_HOST: "0.0.0.0"
FLASK_PORT: "5000"
IMAP_SYNC_INTERVAL: "300"
SMTP_TIMEOUT: "30"
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
postfix:
condition: service_healthy
dovecot:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://127.0.0.1:5000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 15s
networks:
- metabuilder
# ============================================================================
# Gateway
# ============================================================================
# nginx - Reverse proxy + welcome portal (config baked into image)
nginx:
build:
context: ..
dockerfile: deployment/config/nginx/Dockerfile
container_name: metabuilder-nginx
restart: unless-stopped
ports:
- "80:80"
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://127.0.0.1/health"]
interval: 30s
timeout: 5s
retries: 3
networks:
- metabuilder
# ============================================================================
# Web Applications
# ============================================================================
# CodeForge IDE - Next.js + Monaco code editor
codegen:
build:
context: ..
dockerfile: frontends/codegen/Dockerfile
container_name: metabuilder-codegen
restart: unless-stopped
ports:
- "3002:3000"
environment:
NODE_ENV: production
PORT: 3000
HOSTNAME: "0.0.0.0"
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://127.0.0.1:3000/codegen"]
interval: 15s
timeout: 5s
retries: 3
start_period: 20s
networks:
- metabuilder
# Pastebin - Code snippet sharing (dev mode: Turbopack + hot reload)
pastebin:
build:
context: ..
dockerfile: frontends/pastebin/Dockerfile.dev
container_name: metabuilder-pastebin
restart: unless-stopped
ports:
- "3003:3000"
environment:
NODE_ENV: development
PORT: 3000
HOSTNAME: "0.0.0.0"
NEXT_PUBLIC_DBAL_API_URL: /api/dbal
NEXT_PUBLIC_FLASK_BACKEND_URL: /pastebin-api
NEXT_TELEMETRY_DISABLED: "1"
depends_on:
pastebin-backend:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://127.0.0.1:3000/pastebin"]
interval: 15s
timeout: 10s
retries: 5
start_period: 120s
networks:
- metabuilder
# Pastebin Flask Backend - REST API for snippet storage
pastebin-backend:
build:
context: ..
dockerfile: frontends/pastebin/backend/Dockerfile
container_name: metabuilder-pastebin-backend
restart: unless-stopped
environment:
DATABASE_PATH: /app/data/snippets.db
CORS_ALLOWED_ORIGINS: "*"
PYTHON_RUNNER_IMAGE: "${PYTHON_RUNNER_IMAGE:-python:3.11-slim}"
PYTHON_RUN_TIMEOUT: "${PYTHON_RUN_TIMEOUT:-10}"
MYSQL_RUNNER_IMAGE: "${MYSQL_RUNNER_IMAGE:-mysql:8.0}"
POSTGRES_RUNNER_IMAGE: "${POSTGRES_RUNNER_IMAGE:-postgres:16-alpine}"
SQL_RUN_TIMEOUT: "${SQL_RUN_TIMEOUT:-120}"
JWT_SECRET_KEY: "${JWT_SECRET_KEY:-changeme-in-production}"
SMTP_HOST: "smtp-relay"
SMTP_PORT: "2525"
MAIL_FROM: "noreply@codesnippet.local"
APP_BASE_URL: "http://localhost/pastebin"
DBAL_BASE_URL: "http://dbal:8080"
DBAL_TENANT_ID: "pastebin"
DBAL_ADMIN_TOKEN: "069e6487a710300381cd52120eab95d56d7f53beee21479cbeba9128217cbea9"
volumes:
- pastebin-backend-data:/app/data
- /var/run/docker.sock:/var/run/docker.sock
healthcheck:
test: ["CMD", "python3", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:5000/health')"]
interval: 15s
timeout: 5s
retries: 3
start_period: 20s
networks:
- metabuilder
# Postgres Dashboard - Database admin
postgres-dashboard:
build:
context: ..
dockerfile: frontends/postgres/Dockerfile
container_name: metabuilder-postgres-dashboard
restart: unless-stopped
ports:
- "3004:3000"
environment:
NODE_ENV: production
PORT: 3000
DATABASE_URL: "postgresql://${POSTGRES_USER:-metabuilder}:${POSTGRES_PASSWORD:-metabuilder}@postgres:5432/${POSTGRES_DB:-metabuilder}"
depends_on:
postgres:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://127.0.0.1:3000/postgres/admin/dashboard"]
interval: 15s
timeout: 5s
retries: 3
start_period: 30s
networks:
- metabuilder
# Email Client - Email management UI
emailclient-app:
build:
context: ..
dockerfile: frontends/emailclient/Dockerfile
container_name: metabuilder-emailclient-app
restart: unless-stopped
ports:
- "3005:3000"
environment:
NODE_ENV: production
PORT: 3000
HOSTNAME: "0.0.0.0"
EMAIL_SERVICE_URL: http://email-service:5000
depends_on:
- email-service
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://127.0.0.1:3000/emailclient"]
interval: 15s
timeout: 5s
retries: 3
start_period: 30s
networks:
- metabuilder
# Exploded Diagrams - Interactive 3D viewer
exploded-diagrams:
build:
context: ..
dockerfile: frontends/exploded-diagrams/Dockerfile
container_name: metabuilder-exploded-diagrams
restart: unless-stopped
ports:
- "3006:3000"
environment:
NODE_ENV: production
PORT: 3000
HOSTNAME: "0.0.0.0"
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://127.0.0.1:3000/diagrams"]
interval: 15s
timeout: 5s
retries: 3
start_period: 30s
networks:
- metabuilder
# Storybook - Component library
storybook:
build:
context: ..
dockerfile: storybook/Dockerfile
container_name: metabuilder-storybook
restart: unless-stopped
ports:
- "3007:3000"
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://127.0.0.1:3000/storybook/"]
interval: 15s
timeout: 5s
retries: 3
start_period: 20s
networks:
- metabuilder
# Frontend App - Main Next.js application
frontend-app:
build:
context: ..
dockerfile: frontends/nextjs/Dockerfile
container_name: metabuilder-frontend-app
restart: unless-stopped
ports:
- "3008:3000"
environment:
NODE_ENV: production
PORT: 3000
HOSTNAME: "0.0.0.0"
DBAL_API_URL: http://dbal:8080
DBAL_WS_URL: ws://dbal:50051
depends_on:
dbal:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "-O", "/dev/null", "http://127.0.0.1:3000/app/api/health"]
interval: 15s
timeout: 5s
retries: 3
start_period: 40s
networks:
- metabuilder
# ============================================================================
# Monitoring (--profile monitoring)
# ============================================================================
# Prometheus - Metrics collection (config baked into image)
prometheus:
build:
context: ..
dockerfile: deployment/config/prometheus/Dockerfile
container_name: metabuilder-prometheus
restart: unless-stopped
profiles: [monitoring]
ports:
- "9090:9090"
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--storage.tsdb.retention.time=30d'
- '--web.enable-lifecycle'
volumes:
- prometheus-data:/prometheus
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://127.0.0.1:9090/-/healthy"]
interval: 30s
timeout: 5s
retries: 3
networks:
- metabuilder
deploy:
resources:
limits:
cpus: '1'
memory: 1G
# Grafana - Metrics visualization (config baked into image)
grafana:
build:
context: ..
dockerfile: deployment/config/grafana/Dockerfile
container_name: metabuilder-grafana
restart: unless-stopped
profiles: [monitoring]
ports:
- "3009:3000"
environment:
GF_SECURITY_ADMIN_USER: ${GRAFANA_ADMIN_USER:-admin}
GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD:-admin}
GF_INSTALL_PLUGINS: grafana-clock-panel,grafana-simple-json-datasource
GF_AUTH_ANONYMOUS_ENABLED: "false"
volumes:
- grafana-data:/var/lib/grafana
depends_on:
- prometheus
- loki
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://127.0.0.1:3000/api/health"]
interval: 30s
timeout: 5s
retries: 3
networks:
- metabuilder
deploy:
resources:
limits:
cpus: '1'
memory: 512M
# Loki - Log aggregation (config baked into image)
loki:
build:
context: ..
dockerfile: deployment/config/loki/Dockerfile
container_name: metabuilder-loki
restart: unless-stopped
profiles: [monitoring]
ports:
- "3100:3100"
command: -config.file=/etc/loki/local-config.yaml
volumes:
- loki-data:/loki
healthcheck:
# loki uses a scratch-based image with no shell tools — check via /proc
disable: true
networks:
- metabuilder
deploy:
resources:
limits:
cpus: '1'
memory: 1G
# Promtail - Log shipper (config baked into image; host log paths are system mounts)
promtail:
build:
context: ..
dockerfile: deployment/config/promtail/Dockerfile
container_name: metabuilder-promtail
restart: unless-stopped
profiles: [monitoring]
command: -config.file=/etc/promtail/config.yml
volumes:
- /var/log:/var/log:ro
- /var/lib/docker/containers:/var/lib/docker/containers:ro
depends_on:
- loki
healthcheck:
disable: true
networks:
- metabuilder
deploy:
resources:
limits:
cpus: '0.5'
memory: 256M
# Node Exporter - Host metrics
node-exporter:
image: prom/node-exporter:latest
container_name: metabuilder-node-exporter
restart: unless-stopped
profiles: [monitoring]
ports:
- "9100:9100"
command:
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'
- '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
healthcheck:
test: ["CMD-SHELL", "wget --quiet --tries=1 --spider http://127.0.0.1:9100/metrics || exit 1"]
interval: 30s
timeout: 5s
retries: 3
networks:
- metabuilder
deploy:
resources:
limits:
cpus: '0.5'
memory: 256M
# Postgres Exporter - Database metrics
postgres-exporter:
image: prometheuscommunity/postgres-exporter:latest
container_name: metabuilder-postgres-exporter
restart: unless-stopped
profiles: [monitoring]
ports:
- "9187:9187"
environment:
DATA_SOURCE_NAME: "postgresql://${POSTGRES_USER:-metabuilder}:${POSTGRES_PASSWORD:-metabuilder}@postgres:5432/${POSTGRES_DB:-metabuilder}?sslmode=disable"
depends_on:
postgres:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "wget --quiet --tries=1 --spider http://127.0.0.1:9187/metrics || exit 1"]
interval: 30s
timeout: 5s
retries: 3
start_period: 15s
networks:
- metabuilder
deploy:
resources:
limits:
cpus: '0.25'
memory: 128M
# Redis Exporter - Cache metrics
redis-exporter:
image: oliver006/redis_exporter:latest
container_name: metabuilder-redis-exporter
restart: unless-stopped
profiles: [monitoring]
ports:
- "9121:9121"
environment:
REDIS_ADDR: redis:6379
depends_on:
redis:
condition: service_healthy
healthcheck:
test: ["CMD", "/redis_exporter", "--version"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
networks:
- metabuilder
deploy:
resources:
limits:
cpus: '0.25'
memory: 128M
# cAdvisor - Container metrics
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
container_name: metabuilder-cadvisor
restart: unless-stopped
profiles: [monitoring]
privileged: true
ports:
- "8084:8080"
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
- /dev/disk/:/dev/disk:ro
healthcheck:
test: ["CMD-SHELL", "wget --quiet --tries=1 --spider http://127.0.0.1:8080/healthz || exit 1"]
interval: 30s
timeout: 5s
retries: 3
start_period: 15s
networks:
- metabuilder
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
# Alertmanager - Alert routing (config baked into image)
alertmanager:
build:
context: ..
dockerfile: deployment/config/alertmanager/Dockerfile
container_name: metabuilder-alertmanager
restart: unless-stopped
profiles: [monitoring]
ports:
- "9093:9093"
command:
- '--config.file=/etc/alertmanager/config.yml'
- '--storage.path=/alertmanager'
volumes:
- alertmanager-data:/alertmanager
healthcheck:
test: ["CMD-SHELL", "wget --quiet --tries=1 --spider http://127.0.0.1:9093/-/healthy || exit 1"]
interval: 30s
timeout: 5s
retries: 3
networks:
- metabuilder
deploy:
resources:
limits:
cpus: '0.5'
memory: 256M
# ============================================================================
# Media (--profile media)
# ============================================================================
# Media Processing Daemon - FFmpeg, Radio, Retro Gaming
media-daemon:
build:
context: ../services/media_daemon
dockerfile: Dockerfile
container_name: metabuilder-media-daemon
restart: unless-stopped
profiles: [media]
ports:
- "8090:8090"
- "8000:8000"
environment:
MEDIA_BIND_ADDRESS: 0.0.0.0
MEDIA_PORT: 8090
MEDIA_WORKERS: 4
DBAL_URL: http://dbal:8080
DBAL_API_KEY: ${DBAL_API_KEY:-}
MEDIA_VIDEO_WORKERS: 2
MEDIA_AUDIO_WORKERS: 4
MEDIA_DOC_WORKERS: 4
MEDIA_IMAGE_WORKERS: 8
MEDIA_RADIO_ENABLED: "true"
MEDIA_TV_ENABLED: "true"
MEDIA_RETRO_ENABLED: "true"
ICECAST_HOST: localhost
ICECAST_PASSWORD: ${ICECAST_PASSWORD:-hackme}
ICECAST_SOURCE_PASSWORD: ${ICECAST_PASSWORD:-hackme}
ICECAST_ADMIN_PASSWORD: ${ICECAST_ADMIN_PASSWORD:-hackme}
ICECAST_RELAY_PASSWORD: ${ICECAST_PASSWORD:-hackme}
ICECAST_HOSTNAME: ${ICECAST_HOSTNAME:-localhost}
ICECAST_MAX_CLIENTS: 1000
ICECAST_MAX_SOURCES: 20
LIBRETRO_CORES_DIR: /data/cores
LIBRETRO_SYSTEM_DIR: /data/system
LIBRETRO_SAVES_DIR: /data/saves
volumes:
- media-library:/data/media:ro
- media-output:/data/output
- media-cache:/data/cache
- hls-output:/data/hls
- media-temp:/data/temp
- retro-cores:/data/cores
- retro-system:/data/system
- retro-saves:/data/saves
- retro-roms:/data/roms:ro
depends_on:
dbal:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://127.0.0.1:8090/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
networks:
- metabuilder
deploy:
resources:
limits:
cpus: '4'
memory: 4G
reservations:
cpus: '1'
memory: 1G
# HLS/DASH Streaming Server (config baked into image)
nginx-stream:
build:
context: ..
dockerfile: deployment/config/nginx/Dockerfile.stream
container_name: metabuilder-nginx-stream
restart: unless-stopped
profiles: [media]
ports:
- "8088:80"
volumes:
- hls-output:/data/hls:ro
depends_on:
- media-daemon
healthcheck:
# 404 is fine — no HLS content yet; just verify nginx is accepting connections
test: ["CMD-SHELL", "wget --quiet --tries=1 --spider http://127.0.0.1:80/ 2>&1 | grep -qE 'connected|404|200' || exit 1"]
interval: 30s
timeout: 5s
retries: 3
networks:
- metabuilder
# ============================================================================
# Volumes
# ============================================================================
volumes:
# NPM registry
verdaccio-storage:
driver: local
# Core
postgres-data:
driver: local
redis-data:
driver: local
elasticsearch-data:
driver: local
mysql-data:
driver: local
mongodb-data:
driver: local
dbal-schemas:
driver: local
dbal-templates:
driver: local
dbal-logs:
driver: local
dbal-blobs:
driver: local
redisinsight-data:
driver: local
dovecot-data:
driver: local
# Monitoring
prometheus-data:
driver: local
grafana-data:
driver: local
loki-data:
driver: local
alertmanager-data:
driver: local
# Media
media-library:
driver: local
media-output:
driver: local
media-cache:
driver: local
media-temp:
driver: local
hls-output:
driver: local
retro-cores:
driver: local
retro-system:
driver: local
retro-saves:
driver: local
retro-roms:
driver: local
pastebin-backend-data:
driver: local
# ============================================================================
# Networks
# ============================================================================
networks:
metabuilder:
driver: bridge
name: metabuilder-network