Ctrl K
ComunidadGuías…

docker-compose.yml

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
services:
  # Database (with auto-generated TLS)
  postgres:
    build:
      context: ./scripts
      dockerfile: Dockerfile.postgres
    container_name: ninerlog-db
    restart: unless-stopped
    environment:
      POSTGRES_DB: ${POSTGRES_DB:-ninerlog}
      POSTGRES_USER: ${POSTGRES_USER:-ninerlog}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_INITDB_ARGS: "-E UTF8"
    volumes:
      - postgres_data:/var/lib/postgresql
    expose:
      - "5432"
    command:
      - "postgres"
      - "-c"
      - "ssl=on"
      - "-c"
      - "ssl_cert_file=/var/lib/postgresql/ssl/server.crt"
      - "-c"
      - "ssl_key_file=/var/lib/postgresql/ssl/server.key"
      - "-c"
      - "log_connections=on"
      - "-c"
      - "log_disconnections=on"
      - "-c"
      - "log_statement=ddl"
      - "-c"
      - "password_encryption=scram-sha-256"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-ninerlog}"]
      interval: 10s
      timeout: 5s
      retries: 10
      start_period: 30s
    networks:
      - ninerlog-network

  # Backend API
  api:
    image: ghcr.io/fjaeckel/ninerlog-api:latest
    container_name: ninerlog-api
    restart: unless-stopped
    depends_on:
      postgres:
        condition: service_healthy
    environment:
      # Database (SSL required — self-signed cert between containers)
      DATABASE_URL: postgresql://${POSTGRES_USER:-ninerlog}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-ninerlog}?sslmode=require

      # Server
      PORT: 3000
      GIN_MODE: ${GIN_MODE:-release}

      # JWT (no defaults — must be set in .env)
      JWT_SECRET: ${JWT_SECRET}
      REFRESH_SECRET: ${REFRESH_SECRET}
      JWT_EXPIRES_IN: ${JWT_EXPIRES_IN:-15m}
      REFRESH_EXPIRES_IN: ${REFRESH_EXPIRES_IN:-7d}

      # CORS (must match your domain)
      CORS_ORIGIN: ${CORS_ORIGIN}

      # WebAuthn / Passkeys (optional — leave WEBAUTHN_RP_ID empty to disable)
      WEBAUTHN_RP_ID: ${WEBAUTHN_RP_ID:-}
      WEBAUTHN_RP_NAME: ${WEBAUTHN_RP_NAME:-NinerLog}
      WEBAUTHN_RP_ORIGINS: ${WEBAUTHN_RP_ORIGINS:-}

      # OIDC single sign-on (optional — leave OIDC_ISSUER empty to disable).
      # WARNING: setting OIDC_ISSUER switches the whole server to OIDC mode —
      # password login, registration, TOTP and passkeys are all disabled.
      # See docs/OIDC.md before enabling.
      OIDC_ISSUER: ${OIDC_ISSUER:-}
      OIDC_CLIENT_ID: ${OIDC_CLIENT_ID:-}
      OIDC_CLIENT_SECRET: ${OIDC_CLIENT_SECRET:-}
      OIDC_REDIRECT_URL: ${OIDC_REDIRECT_URL:-}
      OIDC_POST_LOGIN_REDIRECT: ${OIDC_POST_LOGIN_REDIRECT:-}
      OIDC_PROVIDER_NAME: ${OIDC_PROVIDER_NAME:-}
      OIDC_SCOPES: ${OIDC_SCOPES:-}
      OIDC_NAME_CLAIM: ${OIDC_NAME_CLAIM:-}
      OIDC_LINK_BY_VERIFIED_EMAIL: ${OIDC_LINK_BY_VERIFIED_EMAIL:-}
      OIDC_TRUST_EMAIL_VERIFIED: ${OIDC_TRUST_EMAIL_VERIFIED:-}
      OIDC_LOGIN_STATE_TTL: ${OIDC_LOGIN_STATE_TTL:-}
      OIDC_HANDOFF_TTL: ${OIDC_HANDOFF_TTL:-}

      # Admin
      ADMIN_EMAIL: ${ADMIN_EMAIL:-}

      # Logging
      LOG_LEVEL: ${LOG_LEVEL:-info}

      # SMTP
      SMTP_HOST: ${SMTP_HOST:-}
      SMTP_PORT: ${SMTP_PORT:-587}
      SMTP_USERNAME: ${SMTP_USERNAME:-}
      SMTP_PASSWORD: ${SMTP_PASSWORD:-}
      SMTP_FROM: ${SMTP_FROM:-noreply@ninerlog.com}

      # Notification checker interval (default: 1h)
      NOTIFICATION_CHECK_INTERVAL: ${NOTIFICATION_CHECK_INTERVAL:-1h}

      # Cloud backups (optional — leave empty to disable the feature)
      # 32 random bytes, base64-encoded. Generate with: openssl rand -base64 32
      BACKUP_CREDENTIALS_KEY: ${BACKUP_CREDENTIALS_KEY:-}
    expose:
      - "3000"
    networks:
      - ninerlog-network
    healthcheck:
      test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/health"]
      interval: 15s
      timeout: 10s
      retries: 8
      start_period: 60s

  # Frontend (nginx reverse proxy + static assets)
  frontend:
    image: ghcr.io/fjaeckel/ninerlog-frontend:latest
    container_name: ninerlog-frontend
    restart: unless-stopped
    depends_on:
      api:
        condition: service_healthy
    environment:
      VITE_API_BASE_URL: ${VITE_API_BASE_URL:-/api/v1}
      VITE_ENV: ${VITE_ENV:-production}
      VITE_APP_NAME: ${APP_NAME:-${VITE_APP_NAME:-}}
      BETA_PASSWORD: ${BETA_PASSWORD:-}
      TLS_DOMAIN: ${TLS_DOMAIN}
    ports:
      - "${FRONTEND_PORT:-80}:80"
      - "${FRONTEND_TLS_PORT:-443}:443"
    expose:
      # Internal-only nginx stub_status port for the nginx-exporter sidecar.
      # Never published to the host — see nginx.conf.
      - "8080"
    volumes:
      # Let's Encrypt certs (shared with certbot container)
      - letsencrypt_certs:/etc/letsencrypt:ro
      # ACME challenge webroot (shared with certbot container)
      - certbot_webroot:/var/www/certbot:ro
    networks:
      - ninerlog-network
    healthcheck:
      test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 10s

  # Prometheus exporter for the frontend nginx.
  # Scrapes the internal-only stub_status endpoint and exposes Prometheus
  # metrics on port 9113. The port is NOT published here — reveal it to your
  # monitoring host via docker-compose.override.yml, exactly like the API.
  # See docs/MONITORING.md.
  nginx-exporter:
    image: nginx/nginx-prometheus-exporter:1.5.3
    container_name: ninerlog-nginx-exporter
    restart: unless-stopped
    depends_on:
      frontend:
        condition: service_healthy
    command:
      - "--nginx.scrape-uri=http://frontend:8080/stub_status"
    expose:
      - "9113"
    networks:
      - ninerlog-network

  # Prometheus exporter for PostgreSQL.
  # Connects to the database over TLS (sslmode=require, same as the API) and
  # exposes Prometheus metrics on port 9187. The port is NOT published here —
  # reveal it to your monitoring host via docker-compose.override.yml, exactly
  # like the API. See docs/MONITORING.md.
  postgres-exporter:
    image: quay.io/prometheuscommunity/postgres-exporter:v0.20.1
    container_name: ninerlog-postgres-exporter
    restart: unless-stopped
    depends_on:
      postgres:
        condition: service_healthy
    environment:
      DATA_SOURCE_NAME: "postgresql://${POSTGRES_USER:-ninerlog}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-ninerlog}?sslmode=require"
    expose:
      - "9187"
    networks:
      - ninerlog-network

  # Scheduled PostgreSQL backups (compressed)
  db-backup:
    build:
      context: ./scripts
      dockerfile: Dockerfile.db-backup
    container_name: ninerlog-db-backup
    restart: unless-stopped
    depends_on:
      postgres:
        condition: service_healthy
    environment:
      PGHOST: postgres
      PGPORT: "5432"
      PGUSER: ${POSTGRES_USER:-ninerlog}
      PGDATABASE: ${POSTGRES_DB:-ninerlog}
      PGPASSWORD: ${POSTGRES_PASSWORD}
      BACKUP_INTERVAL: ${BACKUP_INTERVAL:-21600}
      BACKUP_RETENTION: ${BACKUP_RETENTION:-30}
    volumes:
      - ${BACKUP_PATH:-./backups}:/backups
    networks:
      - ninerlog-network

  # Let's Encrypt certificate manager
  # Initial cert request (run once):
  #   docker compose run --rm certbot certonly --webroot -w /var/www/certbot \
  #     -d yourdomain.com --agree-tos --email your@email.com
  # After that, the container auto-renews every 12 hours.
  certbot:
    image: certbot/certbot:latest
    container_name: ninerlog-certbot
    volumes:
      - letsencrypt_certs:/etc/letsencrypt
      - certbot_webroot:/var/www/certbot
    entrypoint: certbot
    command: ["renew", "--webroot", "-w", "/var/www/certbot", "--quiet"]
    depends_on:
      - frontend
    restart: "no"

volumes:
  postgres_data:
    driver: local
  letsencrypt_certs:
    driver: local
  certbot_webroot:
    driver: local

networks:
  ninerlog-network:
    driver: bridge