# Production deploy. # API 51300 → Kestrel 8080 # SPA 5250 → nginx (кабинет + proxy /api и /u на tile-server) # PG 5433 → Postgres (учётки, токены, стили, usage) # # docker compose up -d --build # # Persistent data (PBF, MBTiles, Planetiler sources, styles, keys) lives in the # named volume `tile-server-data`. Image rebuilds and `docker compose up` do not # touch it. Do NOT use `docker compose down -v` — that deletes the volume. # # If you already have files in ./data on the server, copy them once: # docker run --rm -v "$(pwd)/data:/from:ro" -v tile-server-data:/to alpine \ # sh -c "cp -a /from/. /to/" # # Heap must stay well below mem_limit: Planetiler maps temp files outside -Xmx. # Exit 137 = cgroup OOM killer. 8g heap in a 12g box dies on NW FO z15. # After raising MaxZoom (z14→z15) the next startup rebuilds MBTiles; keep the volume. services: db: image: postgres:16-alpine container_name: tile-server-db restart: unless-stopped environment: POSTGRES_DB: ${POSTGRES_DB:-tile_server} POSTGRES_USER: ${POSTGRES_USER:-tiles} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-tiles} ports: - "5433:5432" volumes: - tile-server-pg:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-tiles} -d ${POSTGRES_DB:-tile_server}"] interval: 5s timeout: 5s retries: 10 tile-server: build: context: . dockerfile: Dockerfile image: tile-server:latest container_name: tile-server restart: unless-stopped init: true env_file: - path: .env required: false depends_on: db: condition: service_healthy ports: - "51300:8080" environment: ASPNETCORE_ENVIRONMENT: Production ASPNETCORE_URLS: http://+:8080 TZ: UTC TileServer__DataDirectory: /data TileServer__JavaPath: /opt/java/openjdk/bin/java TileServer__JvmMaxHeap: 4g TileServer__PlanetilerThreads: "8" TileServer__PlanetilerMmapTemp: "false" TileServer__Sync__RunOnStartup: "true" TileServer__Sync__Cron: "0 3 * * *" TileServer__Sync__TimeZone: UTC # Public origin for MapLibre (must be absolute https, not a relative path). TileServer__PublicBaseUrl: https://tile-server.ru # Yandex ID — same flat names as please_pay_me_bot. Secret only in .env. YANDEX_CLIENT_ID: ${YANDEX_CLIENT_ID:-c76f28f3d66c447e85940a9a1f227452} YANDEX_CLIENT_SECRET: ${YANDEX_CLIENT_SECRET:-} YANDEX_REDIRECT_URI: ${YANDEX_REDIRECT_URI:-https://tile-server.ru} TileServer__Yandex__ClientId: ${YANDEX_CLIENT_ID:-c76f28f3d66c447e85940a9a1f227452} TileServer__Yandex__ClientSecret: ${YANDEX_CLIENT_SECRET:-} TileServer__Yandex__RedirectUri: ${YANDEX_REDIRECT_URI:-https://tile-server.ru} ConnectionStrings__Accounts: >- Host=db;Port=5432;Database=${POSTGRES_DB:-tile_server};Username=${POSTGRES_USER:-tiles};Password=${POSTGRES_PASSWORD:-tiles} volumes: - tile-server-data:/data healthcheck: test: ["CMD", "curl", "-fsS", "http://127.0.0.1:8080/health/live"] interval: 30s timeout: 5s retries: 5 start_period: 45s logging: driver: json-file options: max-size: "50m" max-file: "5" stop_grace_period: 2m mem_limit: 12g memswap_limit: 12g tile-web: build: context: . dockerfile: Dockerfile target: frontend image: tile-server-web:latest container_name: tile-server-web restart: unless-stopped depends_on: tile-server: condition: service_healthy ports: - "5250:80" healthcheck: test: ["CMD", "wget", "-q", "-O", "/dev/null", "http://127.0.0.1/"] interval: 30s timeout: 5s retries: 5 logging: driver: json-file options: max-size: "10m" max-file: "3" volumes: tile-server-data: name: tile-server-data tile-server-pg: name: tile-server-pg