# ███╗ ███╗ ██████╗ ███╗ ██╗ ██████╗ ███████╗██████╗ █████╗ ██████╗███████╗ # ████╗ ████║██╔═══██╗████╗ ██║██╔═══██╗██╔════╝██╔══██╗██╔══██╗██╔════╝██╔════╝ # ██╔████╔██║██║ ██║██╔██╗ ██║██║ ██║███████╗██████╔╝███████║██║ █████╗ # ██║╚██╔╝██║██║ ██║██║╚██╗██║██║ ██║╚════██║██╔═══╝ ██╔══██║██║ ██╔══╝ # ██║ ╚═╝ ██║╚██████╔╝██║ ╚████║╚██████╔╝███████║██║ ██║ ██║╚██████╗███████╗ # ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═════╝╚══════╝ # Monospace single-command quickstart # # Usage: # docker compose up -d # # The app is available at http://localhost:8100 once it is healthy. name: monospace services: # --------------------------------------------------------------------------- # PostgreSQL — system database # --------------------------------------------------------------------------- postgres: image: postgres:17 restart: unless-stopped command: postgres -c 'max_connections=200' environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: monospace POSTGRES_DB: monospace POSTGRES_HOST_AUTH_METHOD: md5 POSTGRES_INITDB_ARGS: --auth-host=md5 volumes: - postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres -d monospace"] interval: 5s timeout: 5s retries: 10 networks: - monospace # --------------------------------------------------------------------------- # RabbitMQ — event bus (required for email delivery & webhooks) # --------------------------------------------------------------------------- rabbitmq: image: rabbitmq:3.13-management restart: unless-stopped environment: RABBITMQ_DEFAULT_USER: monospace RABBITMQ_DEFAULT_PASS: monospace volumes: - rabbitmq_data:/var/lib/rabbitmq healthcheck: test: ["CMD", "rabbitmq-diagnostics", "-q", "ping"] interval: 10s timeout: 5s retries: 10 networks: - monospace # --------------------------------------------------------------------------- # Redis — query result cache # --------------------------------------------------------------------------- redis: image: redis:8.2 restart: unless-stopped healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 1s timeout: 3s retries: 5 networks: - monospace # --------------------------------------------------------------------------- # Key generator — ephemeral init container that writes JWT + KMS keys once # --------------------------------------------------------------------------- keygen: image: alpine:3.21 command: - sh - -c - | set -e mkdir -p /keys # Only generate if keys don't already exist if ! [ -f /keys/jwt_private.pem ] || ! [ -f /keys/jwt_public.pem ] || ! [ -f /keys/kms_master.key ]; then apk add --no-cache openssl > /dev/null openssl genpkey -algorithm ED25519 -out /keys/jwt_private.pem openssl pkey -in /keys/jwt_private.pem -pubout -out /keys/jwt_public.pem openssl rand -base64 32 | tr -d '\n' > /keys/kms_master.key echo "Keys generated." else echo "Keys already exist, skipping generation." fi chown 65532:65532 /keys/jwt_private.pem /keys/jwt_public.pem /keys/kms_master.key chmod 600 /keys/jwt_private.pem /keys/jwt_public.pem /keys/kms_master.key volumes: - keys_data:/keys # --------------------------------------------------------------------------- # Monospace — the application # --------------------------------------------------------------------------- monospace: image: monospace/monospace:v0.6.0 restart: unless-stopped depends_on: postgres: condition: service_healthy rabbitmq: condition: service_healthy redis: condition: service_healthy keygen: condition: service_completed_successfully ports: - "8100:8100" volumes: - keys_data:/keys:ro - uploads_data:/uploads environment: # Server MONOSPACE_HOST: "0.0.0.0" MONOSPACE_PORT: "8100" MONOSPACE_BASE_URL: "http://localhost:8100" # System database MONOSPACE_SYSTEM_DATABASE__PROVIDER: "postgres" MONOSPACE_SYSTEM_DATABASE__HOST: "postgres" MONOSPACE_SYSTEM_DATABASE__PORT: "5432" MONOSPACE_SYSTEM_DATABASE__USER: "postgres" MONOSPACE_SYSTEM_DATABASE__PASSWORD: "monospace" MONOSPACE_SYSTEM_DATABASE__DBNAME: "monospace" # File storage (local, inside the container) MONOSPACE_SYSTEM_FILE_STORAGE__PROVIDER: "local" MONOSPACE_SYSTEM_FILE_STORAGE__SHARED: "true" MONOSPACE_SYSTEM_FILE_STORAGE__ROOT_PATH: "/uploads" # Redis MONOSPACE_REDIS__URL: "redis://redis:6379" # RabbitMQ MONOSPACE_RABBITMQ__HOST: "rabbitmq" MONOSPACE_RABBITMQ__PORT: "5672" MONOSPACE_RABBITMQ__USER: "monospace" MONOSPACE_RABBITMQ__PASSWORD: "monospace" MONOSPACE_RABBITMQ__VHOST: "/" # JWT keys (generated by keygen init container) MONOSPACE_JWT_PRIVATE_KEY__FILE: "/keys/jwt_private.pem" MONOSPACE_JWT_PUBLIC_KEY__FILE: "/keys/jwt_public.pem" # KMS (generated by keygen init container) MONOSPACE_KMS__TYPE: "monospace" MONOSPACE_KMS__MASTER_KEY__FILE: "/keys/kms_master.key" # Auth tokens MONOSPACE_ACCESS_TOKEN_TTL: "15m" MONOSPACE_REFRESH_TOKEN_TTL: "7days" # Cookie — disable Secure flag so HTTP works out of the box MONOSPACE_COOKIE_SECURE: "false" networks: - monospace volumes: postgres_data: rabbitmq_data: keys_data: uploads_data: networks: monospace: