docker-compose.yml
Rama main · réplica de lectura del repositorio original.
services:
postgres:
image: postgres:14-alpine
container_name: seneca-postgres
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER:?POSTGRES_USER is required}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}
POSTGRES_DB: ${POSTGRES_DB:-finflow}
ports:
- "127.0.0.1:${POSTGRES_PORT:-5432}:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER"]
interval: 10s
timeout: 5s
retries: 5
networks:
- finflow-network
redis:
image: redis:7-alpine
container_name: seneca-redis
restart: unless-stopped
ports:
- "127.0.0.1:${REDIS_PORT:-6379}:6379"
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
networks:
- finflow-network
backend:
# Prebuilt images are published by CD for every release (`latest` tracks
# the newest v* tag; set IMAGE_TAG to pin one). `docker compose pull`
# then starts the stack without building anything locally.
image: ghcr.io/stoic-budget/stoic-budget-backend:${IMAGE_TAG:-latest}
build:
context: ./backend
dockerfile: Dockerfile
container_name: seneca-backend
ports:
- "${BACKEND_PORT:-3001}:3001"
env_file:
- path: .env
required: false
environment:
- NODE_ENV=production
- RUN_MIGRATIONS=true
- PORT=3001
- FRONTEND_URL=${FRONTEND_URL:-http://localhost:3000}
- APP_URL=${APP_URL:-http://localhost:3000}
- APP_NAME=${APP_NAME:-Stoic Budget}
- REGISTRATION_ENABLED=${REGISTRATION_ENABLED:-true}
- DATABASE_URL=postgresql://${POSTGRES_USER:?POSTGRES_USER is required}:${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}@postgres:5432/${POSTGRES_DB:-finflow}
- REDIS_HOST=redis
- REDIS_PORT=6379
# Both the cache and the statement-parsing queue read REDIS_URL. Without it
# they fall back to localhost, which does not resolve inside the container.
- REDIS_URL=redis://redis:6379
- JWT_SECRET=${JWT_SECRET:?JWT_SECRET is required}
- JWT_REFRESH_SECRET=${JWT_REFRESH_SECRET:?JWT_REFRESH_SECRET is required}
- INTEGRATIONS_ENCRYPTION_KEY=${INTEGRATIONS_ENCRYPTION_KEY:?INTEGRATIONS_ENCRYPTION_KEY is required}
# Access-token lifetime intentionally left to the code default (30m,
# see auth.service.ts). The long-lived session lives in the refresh token.
- JWT_REFRESH_EXPIRES_IN=30d
- LOG_LEVEL=info
volumes:
- backend_uploads:/app/uploads
- update_requests:/app/update
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
networks:
- finflow-network
restart: unless-stopped
frontend:
image: ghcr.io/stoic-budget/stoic-budget-frontend:${IMAGE_TAG:-latest}
build:
context: ./frontend
dockerfile: Dockerfile
args:
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-http://localhost:3001/api/v1}
NEXT_PUBLIC_APP_URL: ${NEXT_PUBLIC_APP_URL:-http://localhost:3000}
NEXT_PUBLIC_APP_NAME: ${APP_NAME:-Stoic Budget}
API_PROXY_TARGET: http://backend:3001
container_name: seneca-frontend
ports:
- "${FRONTEND_PORT:-3000}:3000"
environment:
- NODE_ENV=production
- NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL:-http://localhost:3001/api/v1}
- NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL:-http://localhost:3000}
- NEXT_PUBLIC_APP_NAME=${APP_NAME:-Stoic Budget}
- NEXT_PUBLIC_ENV=production
- API_PROXY_TARGET=http://backend:3001
depends_on:
- backend
networks:
- finflow-network
restart: unless-stopped
# --- In-app updates (optional, off by default) -------------------------------
# Lets an instance admin run the equivalent of `make upgrade` from
# Settings -> Advanced. It drives Docker through the socket, which amounts to
# root on the host, so it is opt-in: add COMPOSE_PROFILES=updater to .env.
updater:
image: docker:28-cli
container_name: seneca-updater
profiles: ["updater"]
entrypoint: ["sh", "/updater.sh"]
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- .:/project
- ./infra/updater/updater.sh:/updater.sh:ro
- update_requests:/update
networks:
- finflow-network
restart: unless-stopped
# --- Receipt maps (optional, off by default) ---------------------------------
# Self-hosted tiles and geocoding for the receipt location map:
# docker compose --profile maps --profile geocoder up -d
# then set TILESERVER_URL=http://tileserver:8080 and GEOCODER_URL=http://nominatim:8080
# for the backend. The first start imports MAP_PBF_URL: minutes for a small
# country, hours for a large one, and several GB of disk either way.
map-assets:
image: alpine:3.22
profiles: ["maps"]
command: ["sh", "/scripts/prepare-assets.sh"]
volumes:
- ./infra/maps/prepare-assets.sh:/scripts/prepare-assets.sh:ro
- ./infra/maps/tileserver-config.json:/scripts/tileserver-config.json:ro
- map_data:/data
restart: "no"
map-tiles-init:
image: ghcr.io/onthegomap/planetiler:0.10.2
profiles: ["maps"]
# Builds OpenMapTiles vector tiles once; later starts see the file and exit.
entrypoint: ["sh", "-c"]
command:
- >-
[ -s /data/region.mbtiles ] && echo "Tiles already built" ||
exec java -cp @/app/jib-classpath-file com.onthegomap.planetiler.Main
--download --download_dir=/data/sources --tmpdir=/data/tmp
--osm_url="$${MAP_PBF_URL}" --output=/data/region.mbtiles
environment:
MAP_PBF_URL: ${MAP_PBF_URL:-https://download.geofabrik.de/asia/kazakhstan-latest.osm.pbf}
volumes:
- map_data:/data
restart: "no"
tileserver:
image: maptiler/tileserver-gl:v5.6.0
container_name: seneca-tileserver
profiles: ["maps"]
command: ["--config", "/data/config.json", "--port", "8080"]
# No published port: the backend proxies tiles to signed-in users.
volumes:
- map_data:/data
depends_on:
map-assets:
condition: service_completed_successfully
map-tiles-init:
condition: service_completed_successfully
networks:
finflow-network:
aliases: [tileserver]
restart: unless-stopped
nominatim:
image: mediagis/nominatim:5.3
container_name: seneca-nominatim
profiles: ["geocoder"]
environment:
PBF_URL: ${MAP_PBF_URL:-https://download.geofabrik.de/asia/kazakhstan-latest.osm.pbf}
NOMINATIM_PASSWORD: ${NOMINATIM_PASSWORD:-nominatim}
shm_size: 1gb
# Imports the special phrases that let the backend search by kind of place.
command: ["/seneca/start.sh"]
volumes:
- nominatim_data:/var/lib/postgresql/16/main
- ./infra/nominatim:/seneca:ro
networks:
finflow-network:
aliases: [nominatim]
restart: unless-stopped
volumes:
postgres_data:
redis_data:
backend_uploads:
update_requests:
map_data:
nominatim_data:
networks:
finflow-network:
driver: bridge