424 lines
11 KiB
YAML
424 lines
11 KiB
YAML
services:
|
|
# ============================================
|
|
# NEXUS APP SERVICES
|
|
# ============================================
|
|
|
|
# Vault Agent for nexus migrations (one-shot, runs before app)
|
|
vault-agent-migrate:
|
|
image: hashicorp/vault:1.18
|
|
container_name: nexus-vault-agent-migrate
|
|
network_mode: host
|
|
user: "0:0"
|
|
|
|
command: >
|
|
sh -c "rm -f /vault/secrets/.env;
|
|
vault agent -config=/vault/config/agent-config.hcl &
|
|
while [ ! -f /vault/secrets/.env ]; do sleep 1; done;
|
|
echo 'Secrets rendered, exiting'; exit 0"
|
|
|
|
cap_add:
|
|
- IPC_LOCK
|
|
|
|
volumes:
|
|
- ./vault/agent-config-migrate.hcl:/vault/config/agent-config.hcl:ro
|
|
- ./vault/templates:/vault/templates:ro
|
|
- ./secrets/migrate/role-id:/vault/role-id:ro
|
|
- ./secrets/migrate/secret-id:/vault/secret-id:ro
|
|
- ./run/migrate:/vault/secrets
|
|
|
|
environment:
|
|
- VAULT_ADDR=http://vault.example.local:8200
|
|
|
|
# Nexus migration runner (one-shot, runs before app)
|
|
migrate:
|
|
build:
|
|
context: .
|
|
dockerfile: Dockerfile.migrate
|
|
container_name: nexus-migrate
|
|
network_mode: host
|
|
|
|
depends_on:
|
|
vault-agent-migrate:
|
|
condition: service_completed_successfully
|
|
|
|
volumes:
|
|
- ./run/migrate:/vault/secrets:ro
|
|
- ./migrations:/app/migrations:ro
|
|
|
|
working_dir: /app
|
|
|
|
command:
|
|
- |
|
|
set -e
|
|
echo "Loading credentials from Vault..."
|
|
set -a
|
|
. /vault/secrets/.env
|
|
set +a
|
|
echo "Running migrations..."
|
|
sqlx migrate run
|
|
echo "Migrations complete!"
|
|
|
|
# Vault Agent for nexus app runtime (long-running)
|
|
vault-agent:
|
|
image: hashicorp/vault:1.18
|
|
container_name: nexus-vault-agent
|
|
restart: unless-stopped
|
|
network_mode: host
|
|
pid: host # Share PID namespace to signal nexus on credential refresh
|
|
user: "0:0"
|
|
|
|
command: ["vault", "agent", "-config=/vault/config/agent-config.hcl"]
|
|
|
|
cap_add:
|
|
- IPC_LOCK
|
|
- KILL # Required to send SIGHUP to nexus for credential refresh
|
|
|
|
volumes:
|
|
- ./vault/agent-config.hcl:/vault/config/agent-config.hcl:ro
|
|
- ./vault/templates:/vault/templates:ro
|
|
- ./secrets/app/role-id:/vault/role-id:ro
|
|
- ./secrets/app/secret-id:/vault/secret-id:ro
|
|
- ./run/app:/vault/secrets
|
|
|
|
environment:
|
|
- VAULT_ADDR=http://vault.example.local:8200
|
|
|
|
healthcheck:
|
|
test: ["CMD", "test", "-f", "/vault/secrets/.env"]
|
|
interval: 5s
|
|
timeout: 3s
|
|
retries: 30
|
|
start_period: 10s
|
|
|
|
# Main nexus application
|
|
nexus:
|
|
build:
|
|
context: .
|
|
dockerfile: Dockerfile
|
|
container_name: nexus
|
|
restart: unless-stopped
|
|
network_mode: host
|
|
pid: host # Share PID namespace so vault-agent can signal us
|
|
|
|
depends_on:
|
|
migrate:
|
|
condition: service_completed_successfully
|
|
vault-agent:
|
|
condition: service_healthy
|
|
|
|
volumes:
|
|
- ./run/app:/vault/secrets # Not read-only - app writes nexus.pid
|
|
|
|
environment:
|
|
- RUST_LOG=nexus=info,tower_http=info
|
|
|
|
healthcheck:
|
|
test: ["CMD", "curl", "-f", "http://localhost:5050/health/ready"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
start_period: 10s
|
|
|
|
logging:
|
|
driver: "json-file"
|
|
options:
|
|
max-size: "10m"
|
|
max-file: "3"
|
|
|
|
# ============================================
|
|
# PGBOUNCER SERVICE (for Kratos)
|
|
# ============================================
|
|
|
|
# PgBouncer with integrated Vault Agent
|
|
# Proxies Kratos DB connections with dynamic Vault credentials
|
|
pgbouncer:
|
|
build:
|
|
context: ./pgbouncer
|
|
dockerfile: Dockerfile
|
|
container_name: nexus-pgbouncer
|
|
restart: unless-stopped
|
|
network_mode: host
|
|
|
|
cap_add:
|
|
- IPC_LOCK
|
|
|
|
volumes:
|
|
- ./vault/agent-config-pgbouncer.hcl:/vault/config/agent-config.hcl:ro
|
|
- ./vault/templates:/vault/templates:ro
|
|
- ./secrets/kratos-app/role-id:/vault/role-id:ro
|
|
- ./secrets/kratos-app/secret-id:/vault/secret-id:ro
|
|
|
|
environment:
|
|
- VAULT_ADDR=http://vault.example.local:8200
|
|
|
|
healthcheck:
|
|
test: ["CMD", "pg_isready", "-h", "127.0.0.1", "-p", "6432", "-U", "kratos"]
|
|
interval: 5s
|
|
timeout: 3s
|
|
retries: 30
|
|
start_period: 15s
|
|
|
|
logging:
|
|
driver: "json-file"
|
|
options:
|
|
max-size: "10m"
|
|
max-file: "3"
|
|
|
|
# ============================================
|
|
# KRATOS SERVICES
|
|
# ============================================
|
|
|
|
# Vault Agent for Kratos migrations (one-shot)
|
|
vault-agent-kratos-migrate:
|
|
image: hashicorp/vault:1.18
|
|
container_name: nexus-vault-agent-kratos-migrate
|
|
network_mode: host
|
|
user: "0:0"
|
|
|
|
command: >
|
|
sh -c "rm -f /vault/secrets/.env;
|
|
vault agent -config=/vault/config/agent-config.hcl &
|
|
while [ ! -f /vault/secrets/.env ]; do sleep 1; done;
|
|
echo 'Secrets rendered, exiting'; exit 0"
|
|
|
|
cap_add:
|
|
- IPC_LOCK
|
|
|
|
volumes:
|
|
- ./vault/agent-config-kratos-migrate.hcl:/vault/config/agent-config.hcl:ro
|
|
- ./vault/templates:/vault/templates:ro
|
|
- ./secrets/kratos-migrate/role-id:/vault/role-id:ro
|
|
- ./secrets/kratos-migrate/secret-id:/vault/secret-id:ro
|
|
- ./run/kratos-migrate:/vault/secrets
|
|
|
|
environment:
|
|
- VAULT_ADDR=http://vault.example.local:8200
|
|
|
|
# Kratos migration runner (one-shot)
|
|
kratos-migrate:
|
|
image: oryd/kratos:v1.1.0
|
|
container_name: nexus-kratos-migrate
|
|
network_mode: host
|
|
|
|
depends_on:
|
|
vault-agent-kratos-migrate:
|
|
condition: service_completed_successfully
|
|
migrate:
|
|
condition: service_completed_successfully # Nexus migrations create kratos schema
|
|
|
|
volumes:
|
|
- ./kratos/config:/etc/kratos:ro
|
|
- ./run/kratos-migrate:/vault/secrets:ro
|
|
|
|
entrypoint: ["/bin/sh", "-c"]
|
|
command:
|
|
- |
|
|
export $(grep -v '^#' /vault/secrets/.env | xargs)
|
|
exec kratos migrate sql -e --yes
|
|
|
|
# Vault Agent for Kratos runtime (long-running)
|
|
vault-agent-kratos:
|
|
image: hashicorp/vault:1.18
|
|
container_name: nexus-vault-agent-kratos
|
|
restart: unless-stopped
|
|
network_mode: host
|
|
user: "0:0"
|
|
|
|
command: ["vault", "agent", "-config=/vault/config/agent-config.hcl"]
|
|
|
|
cap_add:
|
|
- IPC_LOCK
|
|
|
|
volumes:
|
|
- ./vault/agent-config-kratos.hcl:/vault/config/agent-config.hcl:ro
|
|
- ./vault/templates:/vault/templates:ro
|
|
- ./secrets/kratos-app/role-id:/vault/role-id:ro
|
|
- ./secrets/kratos-app/secret-id:/vault/secret-id:ro
|
|
- ./run/kratos:/vault/secrets
|
|
|
|
environment:
|
|
- VAULT_ADDR=http://vault.example.local:8200
|
|
|
|
healthcheck:
|
|
test: ["CMD", "test", "-f", "/vault/secrets/.env"]
|
|
interval: 5s
|
|
timeout: 3s
|
|
retries: 30
|
|
start_period: 10s
|
|
|
|
# Kratos identity server (long-running)
|
|
kratos:
|
|
image: oryd/kratos:v1.1.0
|
|
container_name: nexus-kratos
|
|
restart: unless-stopped
|
|
network_mode: host
|
|
|
|
depends_on:
|
|
kratos-migrate:
|
|
condition: service_completed_successfully
|
|
vault-agent-kratos:
|
|
condition: service_healthy
|
|
pgbouncer:
|
|
condition: service_healthy
|
|
|
|
volumes:
|
|
- ./kratos/config:/etc/kratos:ro
|
|
- ./run/kratos:/vault/secrets:ro
|
|
|
|
entrypoint: ["/bin/sh", "-c"]
|
|
command:
|
|
- |
|
|
export $(grep -v '^#' /vault/secrets/.env | xargs)
|
|
exec kratos serve --config /etc/kratos/kratos.yml
|
|
|
|
healthcheck:
|
|
test: ["CMD", "wget", "-q", "--spider", "http://localhost:6050/health/alive"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 5
|
|
start_period: 10s
|
|
|
|
logging:
|
|
driver: "json-file"
|
|
options:
|
|
max-size: "10m"
|
|
max-file: "3"
|
|
|
|
# ============================================
|
|
# OATHKEEPER SERVICE
|
|
# ============================================
|
|
|
|
# Vault Agent for Oathkeeper (long-running)
|
|
vault-agent-oathkeeper:
|
|
image: hashicorp/vault:1.18
|
|
container_name: nexus-vault-agent-oathkeeper
|
|
restart: unless-stopped
|
|
network_mode: host
|
|
user: "0:0"
|
|
|
|
command: ["vault", "agent", "-config=/vault/config/agent-config.hcl"]
|
|
|
|
cap_add:
|
|
- IPC_LOCK
|
|
|
|
volumes:
|
|
- ./vault/agent-config-oathkeeper.hcl:/vault/config/agent-config.hcl:ro
|
|
- ./vault/templates:/vault/templates:ro
|
|
- ./secrets/oathkeeper/role-id:/vault/role-id:ro
|
|
- ./secrets/oathkeeper/secret-id:/vault/secret-id:ro
|
|
- ./run/oathkeeper:/vault/secrets
|
|
|
|
environment:
|
|
- VAULT_ADDR=http://vault.example.local:8200
|
|
|
|
healthcheck:
|
|
test: ["CMD", "test", "-f", "/vault/secrets/.env"]
|
|
interval: 5s
|
|
timeout: 3s
|
|
retries: 30
|
|
start_period: 10s
|
|
|
|
# Oathkeeper API gateway (stateless, long-running)
|
|
oathkeeper:
|
|
build:
|
|
context: ./oathkeeper
|
|
dockerfile: Dockerfile
|
|
container_name: nexus-oathkeeper
|
|
restart: unless-stopped
|
|
network_mode: host
|
|
|
|
depends_on:
|
|
kratos:
|
|
condition: service_healthy
|
|
nexus:
|
|
condition: service_healthy
|
|
vault-agent-oathkeeper:
|
|
condition: service_healthy
|
|
|
|
volumes:
|
|
- ./run/oathkeeper:/vault/secrets:ro
|
|
|
|
healthcheck:
|
|
test: ["CMD", "wget", "-q", "--spider", "http://localhost:7250/health/alive"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 5
|
|
start_period: 10s
|
|
|
|
logging:
|
|
driver: "json-file"
|
|
options:
|
|
max-size: "10m"
|
|
max-file: "3"
|
|
|
|
# ============================================
|
|
# AUTH FRONTEND SERVICE
|
|
# ============================================
|
|
|
|
# Auth frontend (account.example.com)
|
|
auth-frontend:
|
|
build:
|
|
context: ./auth-frontend
|
|
dockerfile: Dockerfile
|
|
container_name: nexus-auth-frontend
|
|
restart: unless-stopped
|
|
network_mode: host
|
|
|
|
environment:
|
|
- KRATOS_SERVER_URL=http://localhost:6000
|
|
- ORIGIN=https://account.example.com
|
|
- ADMIN_USER_ID=00000000-0000-0000-0000-000000000000 # Replace with your admin user ID
|
|
|
|
depends_on:
|
|
kratos:
|
|
condition: service_healthy
|
|
oathkeeper:
|
|
condition: service_healthy
|
|
|
|
healthcheck:
|
|
test: ["CMD", "curl", "-f", "http://localhost:3000/"]
|
|
interval: 30s
|
|
timeout: 5s
|
|
retries: 3
|
|
start_period: 10s
|
|
|
|
logging:
|
|
driver: "json-file"
|
|
options:
|
|
max-size: "10m"
|
|
max-file: "3"
|
|
|
|
# ============================================
|
|
# MAIN FRONTEND SERVICE
|
|
# ============================================
|
|
|
|
# Main frontend (app.example.com)
|
|
frontend:
|
|
build:
|
|
context: ./frontend
|
|
dockerfile: Dockerfile
|
|
container_name: nexus-frontend
|
|
restart: unless-stopped
|
|
network_mode: host
|
|
|
|
environment:
|
|
- NODE_ENV=production
|
|
- ORIGIN=https://app.example.com
|
|
|
|
depends_on:
|
|
oathkeeper:
|
|
condition: service_healthy
|
|
|
|
healthcheck:
|
|
test: ["CMD", "curl", "-f", "http://localhost:5000/"]
|
|
interval: 30s
|
|
timeout: 5s
|
|
retries: 3
|
|
start_period: 10s
|
|
|
|
logging:
|
|
driver: "json-file"
|
|
options:
|
|
max-size: "10m"
|
|
max-file: "3"
|