# ============================================================ # opsx-loki - Production Docker Compose Configuration # ============================================================ # Features: # - Multi-instance deployment with load balancing # - Read-only root filesystem for security # - tmpfs for temporary directories # - Stricter resource limits # - Docker Secrets support # - Health checks with longer intervals # - Rolling update configuration # ============================================================ services: # ---- Nginx Load Balancer ---- nginx: image: nginx:1.27-alpine container_name: opsxloki-lb restart: unless-stopped ports: - "8080:8080" volumes: - ./nginx.conf:/etc/nginx/nginx.conf:ro networks: - opsxloki-net healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/health"] interval: 30s timeout: 5s retries: 3 deploy: resources: limits: cpus: '0.5' memory: 128M reservations: cpus: '0.1' memory: 32M logging: driver: "json-file" options: max-size: "20m" max-file: "3" security_opt: - no-new-privileges:true cap_drop: - ALL cap_add: - NET_BIND_SERVICE read_only: true tmpfs: - /var/cache/nginx:noexec,nosuid,size=10m - /var/run:noexec,nosuid,size=5m depends_on: opsxloki-1: condition: service_healthy opsxloki-2: condition: service_healthy # ---- Application Instance 1 ---- opsxloki-1: build: context: . dockerfile: Dockerfile args: TARGETARCH: ${TARGETARCH:-amd64} image: opsxloki:v2 container_name: opsxloki-1 hostname: opsxloki-1 restart: unless-stopped security_opt: - no-new-privileges:true cap_drop: - ALL cap_add: - NET_BIND_SERVICE # P0: Read-only root filesystem read_only: true # P0: tmpfs for writable directories tmpfs: - /tmp:noexec,nosuid,size=50m,uid=1000,gid=1000 volumes: # Named volume for data persistence - opsxloki-data-1:/app/data # kubeconfig read-only (P0: non-root path) - ${KUBECONFIG_PATH:-~/.kube}:/home/opsxloki/.kube:ro # SSH keys read-only - ${SSH_KEY_PATH:-~/.ssh}:/home/opsxloki/.ssh:ro environment: - SERVER_PORT=8080 - DATA_DIR=/app/data - KUBECONFIG=/home/opsxloki/.kube/config # Secrets via Docker Secrets file injection - JWT_SECRET_FILE=${JWT_SECRET_FILE:-/run/secrets/jwt_secret} - ENCRYPTION_KEY_FILE=${ENCRYPTION_KEY_FILE:-/run/secrets/encryption_key} secrets: - jwt_secret - encryption_key # Health check healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/index.html"] interval: 30s timeout: 5s retries: 3 start_period: 20s # Stricter resource limits for production deploy: resources: limits: cpus: '1.0' memory: 256M pids: 50 reservations: cpus: '0.25' memory: 64M restart_policy: condition: on-failure delay: 5s max_attempts: 3 window: 60s logging: driver: "json-file" options: max-size: "50m" max-file: "5" labels: "service,instance" stop_grace_period: 30s networks: - opsxloki-net # ---- Application Instance 2 ---- opsxloki-2: build: context: . dockerfile: Dockerfile args: TARGETARCH: ${TARGETARCH:-amd64} image: opsxloki:v2 container_name: opsxloki-2 hostname: opsxloki-2 restart: unless-stopped security_opt: - no-new-privileges:true cap_drop: - ALL cap_add: - NET_BIND_SERVICE read_only: true tmpfs: - /tmp:noexec,nosuid,size=50m,uid=1000,gid=1000 volumes: - opsxloki-data-2:/app/data - ${KUBECONFIG_PATH:-~/.kube}:/home/opsxloki/.kube:ro - ${SSH_KEY_PATH:-~/.ssh}:/home/opsxloki/.ssh:ro environment: - SERVER_PORT=8080 - DATA_DIR=/app/data - KUBECONFIG=/home/opsxloki/.kube/config - JWT_SECRET_FILE=${JWT_SECRET_FILE:-/run/secrets/jwt_secret} - ENCRYPTION_KEY_FILE=${ENCRYPTION_KEY_FILE:-/run/secrets/encryption_key} secrets: - jwt_secret - encryption_key healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/index.html"] interval: 30s timeout: 5s retries: 3 start_period: 20s deploy: resources: limits: cpus: '1.0' memory: 256M pids: 50 reservations: cpus: '0.25' memory: 64M restart_policy: condition: on-failure delay: 5s max_attempts: 3 window: 60s logging: driver: "json-file" options: max-size: "50m" max-file: "5" labels: "service,instance" stop_grace_period: 30s networks: - opsxloki-net # ---- Named Volumes ---- volumes: opsxloki-data-1: driver: local opsxloki-data-2: driver: local # ---- Docker Secrets ---- secrets: jwt_secret: file: ${JWT_SECRET_FILE_PATH:-./secrets/jwt_secret.txt} encryption_key: file: ${ENCRYPTION_KEY_FILE_PATH:-./secrets/encryption_key.txt} # ---- Network ---- networks: opsxloki-net: driver: bridge ipam: config: - subnet: 172.28.1.0/24