LoVisual/backend/docker-compose.prod.yml
loki5512344 b0ee4ab317
Some checks failed
CI / backend (cargo test + clippy) (push) Failing after 1s
CI / frontend (lint + test + build) (push) Failing after 26s
CI / mod (gradle test + checkFolderLimit) (push) Successful in 1m57s
build(backend): one shared Dockerfile with cargo cache mounts, sequential compose build, stripped release binaries
All four services now share one builder stage (dependencies compile once per
deploy, not once per service). BuildKit cache mounts keep the cargo registry
and target/ between deploys: a one-line change rebuilds in ~16 s instead of
recompiling the whole dependency tree.
2026-10-09 22:01:09 +02:00

156 lines
4.5 KiB
YAML

name: lovisual
networks:
lovisual-internal:
driver: bridge
volumes:
lovisual-pg-data:
lovisual-minio-data:
services:
postgres:
image: postgres:16
restart: unless-stopped
environment:
POSTGRES_USER: lovisual
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: postgres
volumes:
- lovisual-pg-data:/var/lib/postgresql/data
- ./deploy/pg-init:/docker-entrypoint-initdb.d:ro
networks: [lovisual-internal]
healthcheck:
test: ["CMD-SHELL", "pg_isready -U lovisual"]
interval: 5s
timeout: 5s
retries: 10
minio:
image: quay.io/minio/minio:latest
restart: unless-stopped
command: server /data
environment:
MINIO_ROOT_USER: ${S3_ACCESS_KEY}
MINIO_ROOT_PASSWORD: ${S3_SECRET_KEY}
volumes:
- lovisual-minio-data:/data
networks: [lovisual-internal]
healthcheck:
test: ["CMD", "mc", "ready", "local"]
interval: 5s
timeout: 5s
retries: 10
minio-init:
image: quay.io/minio/minio:latest
depends_on:
minio:
condition: service_healthy
networks: [lovisual-internal]
entrypoint: >
/bin/sh -c "
mc alias set local http://minio:9000 $${S3_ACCESS_KEY} $${S3_SECRET_KEY} &&
(mc mb local/$${S3_BUCKET} || true) &&
echo bucket-ready
"
environment:
S3_ACCESS_KEY: ${S3_ACCESS_KEY}
S3_SECRET_KEY: ${S3_SECRET_KEY}
S3_BUCKET: ${S3_BUCKET}
accounts-service:
build:
context: .
dockerfile: Dockerfile
target: accounts-service
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
minio-init:
condition: service_completed_successfully
env_file: .env
environment:
DATABASE_URL: postgres://lovisual:${POSTGRES_PASSWORD}@postgres:5432/accounts_db
S3_ENDPOINT: http://minio:9000
# Browser-facing avatar prefix. Served same-origin through the site's
# /api proxy -> gateway -> accounts-service, so MinIO stays private.
AVATAR_PUBLIC_BASE_URL: https://visual.loki-code.dev/api/media
PORT: 8081
GRPC_PORT: 50051
# Outgoing mail (Resend SMTP): secrets live in .env on the VPS, never
# in the repo. Empty SMTP_HOST keeps mail disabled (endpoints answer
# 503, fail-closed).
SMTP_HOST: ${SMTP_HOST:-}
SMTP_PORT: ${SMTP_PORT:-587}
SMTP_USER: ${SMTP_USER:-}
SMTP_PASSWORD: ${SMTP_PASSWORD:-}
MAIL_FROM: ${MAIL_FROM:-}
# Link origin inside emails; must match the public site origin.
PUBLIC_BASE_URL: https://visual.loki-code.dev
MAIL_LANG: ${MAIL_LANG:-ru}
networks: [lovisual-internal]
configs-service:
build:
context: .
dockerfile: Dockerfile
target: configs-service
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
accounts-service:
condition: service_started
env_file: .env
environment:
CONFIGS_DATABASE_URL: postgres://lovisual:${POSTGRES_PASSWORD}@postgres:5432/configs_db
ACCOUNTS_GRPC_URL: http://accounts-service:50051
CONFIGS_PORT: 8082
networks: [lovisual-internal]
chat-service:
build:
context: .
dockerfile: Dockerfile
target: chat-service
restart: unless-stopped
env_file: .env
environment:
CHAT_PORT: 8083
networks: [lovisual-internal]
gateway:
build:
context: .
dockerfile: Dockerfile
target: gateway
restart: unless-stopped
depends_on:
accounts-service:
condition: service_started
configs-service:
condition: service_started
chat-service:
condition: service_started
env_file: .env
environment:
GATEWAY_PORT: 8080
ACCOUNTS_HTTP_URL: http://accounts-service:8081
ACCOUNTS_GRPC_URL: http://accounts-service:50051
CONFIGS_HTTP_URL: http://configs-service:8082
CHAT_HTTP_URL: http://chat-service:8083
# This stack is only ever exposed through nginx (see deploy/), so the
# rate limiter must read the client IP from X-Forwarded-For. Without
# this every client shares the proxy's socket address and one bucket:
# the global 300/min and login 5/min would be site-wide self-DoS.
# Keep TRUST_PROXY=false in .env for direct-exposure dev runs.
TRUST_PROXY: "true"
# Read-only static files served under GET /downloads/* (lovisual.jar).
DOWNLOADS_DIR: /srv/downloads
volumes:
- ./downloads:/srv/downloads:ro
ports:
- "127.0.0.1:8080:8080"
networks: [lovisual-internal]