133 lines
5.1 KiB
YAML
133 lines
5.1 KiB
YAML
services:
|
|
postgres:
|
|
image: postgres:16-alpine
|
|
environment:
|
|
POSTGRES_DB: production
|
|
POSTGRES_USER: production
|
|
# No fallback default — a missing .env value must stop the stack, not
|
|
# silently start Postgres with a well-known password.
|
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD must be set in .env}
|
|
volumes:
|
|
- pg_data:/var/lib/postgresql/data
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U production"]
|
|
interval: 5s
|
|
timeout: 5s
|
|
retries: 5
|
|
restart: unless-stopped
|
|
ports:
|
|
# Bound to TAILSCALE_IP (or loopback if unset), never 0.0.0.0 — useful
|
|
# if you replicate/back up this database to another host over a
|
|
# private network (e.g. Tailscale) instead of exposing Postgres
|
|
# publicly. Safe to ignore if you don't need that; it just falls back
|
|
# to 127.0.0.1.
|
|
- "${TAILSCALE_IP:-127.0.0.1}:5433:5432"
|
|
|
|
minio:
|
|
image: minio/minio
|
|
command: server /data --console-address ":9001"
|
|
environment:
|
|
# No fallback defaults — see POSTGRES_PASSWORD above for why.
|
|
MINIO_ROOT_USER: ${MINIO_ACCESS_KEY:?MINIO_ACCESS_KEY must be set in .env}
|
|
MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY:?MINIO_SECRET_KEY must be set in .env}
|
|
volumes:
|
|
- minio_data:/data
|
|
healthcheck:
|
|
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 5
|
|
restart: unless-stopped
|
|
ports:
|
|
# Same Tailscale-only binding as postgres above — useful for MinIO
|
|
# bucket replication/backups to another host over a private network.
|
|
- "${TAILSCALE_IP:-127.0.0.1}:9002:9000"
|
|
|
|
backend:
|
|
# Built locally from source by default so anyone can `docker compose up
|
|
# --build` without needing access to a private image registry. If you
|
|
# run this in production at scale, you may prefer to build images in CI
|
|
# and push them to your own registry instead (see
|
|
# .gitea/workflows/build-release.yml for an example of that pattern) and
|
|
# swap this `build:` block for `image: your-registry/production-backend:latest`.
|
|
build:
|
|
context: ./backend
|
|
dockerfile: Dockerfile
|
|
env_file: .env
|
|
# Optional deploy-agent self-update integration (see
|
|
# deploy-agent/README.md) is NOT wired up by default — it needs a bind
|
|
# mount to a Unix socket that doesn't exist on a fresh install, which
|
|
# would otherwise fail `docker compose up` for everyone. If you want the
|
|
# "check/apply update" button in Settings → Обновления, install
|
|
# deploy-agent per its README first, then add:
|
|
# volumes:
|
|
# - ./deploy-agent/agent.sock:/run/deploy-agent/agent.sock:ro
|
|
# here yourself.
|
|
ports:
|
|
# loopback-only — put a reverse proxy (Caddy, nginx, etc.) in front
|
|
# for public access.
|
|
- "127.0.0.1:18091:3000"
|
|
networks:
|
|
default: {}
|
|
platform:
|
|
aliases:
|
|
- production
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
minio:
|
|
condition: service_healthy
|
|
restart: unless-stopped
|
|
|
|
# Self-hosted Telegram Bot API server (internal/notify) — backend talks to
|
|
# it over the docker network (TG_API_BASE_URL) instead of
|
|
# api.telegram.org directly. This is optional: requires
|
|
# TELEGRAM_API_ID/TELEGRAM_API_HASH from my.telegram.org (one-time manual
|
|
# registration, needs a phone number) or it exits immediately on start;
|
|
# restart: "no" so a missing registration doesn't crash-loop this into the
|
|
# logs forever. backend has no depends_on for this service —
|
|
# notify.Handler fails soft when it's down or unreachable, same as an
|
|
# invalid token would. If you don't need Telegram notifications, leave
|
|
# this service stopped.
|
|
telegram-bot-api:
|
|
image: aiogram/telegram-bot-api:latest
|
|
environment:
|
|
TELEGRAM_API_ID: ${TELEGRAM_API_ID:-}
|
|
TELEGRAM_API_HASH: ${TELEGRAM_API_HASH:-}
|
|
TELEGRAM_LOCAL: "1"
|
|
volumes:
|
|
- telegram_bot_api_data:/var/lib/telegram-bot-api
|
|
restart: unless-stopped
|
|
|
|
web:
|
|
# Built locally from source, same reasoning as backend above. Vite bakes
|
|
# VITE_CORE_URL/VITE_PRODUCTION_URL into the JS bundle at build time (the
|
|
# browser's own URLs for core/production, not the container-network
|
|
# addresses) — wired here from WEB_CORE_URL/WEB_PRODUCTION_URL in .env.
|
|
# If you build/push images in CI instead (see .gitea/workflows/
|
|
# build-release.yml), pass the same two build args there and swap this
|
|
# for `image: your-registry/production-web:latest`.
|
|
build:
|
|
context: ./web
|
|
dockerfile: Dockerfile
|
|
args:
|
|
VITE_CORE_URL: ${WEB_CORE_URL}
|
|
VITE_PRODUCTION_URL: ${WEB_PRODUCTION_URL}
|
|
ports:
|
|
- "127.0.0.1:18092:80"
|
|
restart: unless-stopped
|
|
|
|
volumes:
|
|
pg_data:
|
|
minio_data:
|
|
telegram_bot_api_data:
|
|
|
|
networks:
|
|
# Shared with the companion "core" auth service (and any other modules
|
|
# you run alongside this one) — lets this module reach core's
|
|
# /api/modules/:name/heartbeat by the "core" alias. Create it once:
|
|
# `docker network create platform_net`.
|
|
platform:
|
|
name: platform_net
|
|
external: true
|