Files
MangaCircus/docker-compose.yml
T
MaashkinoozheandClaude Opus 5.5 086f6353f8 Add built-in backup of the data folder (BL-0044)
The reader owns a scheduled backup into BACKUP (Compose: BACKUP_PATH mounted at
/backup, off when unset). A run pauses the scraper queue through its control
API, waits (bounded) until no job is active, writes a verified tar.gz of DATA
with db.json from memory and scraper/queue.json stored paused, excludes
runtime/lock/pid/temp files, resumes only a queue it paused, then applies
count- or day-based retention to its own archive names only. Schedule,
retention, "Back up now" and last result live in Settings > Service health.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 18:03:21 +02:00

169 lines
4.7 KiB
YAML

name: mangacircus
x-app: &app
image: ${APP_IMAGE:-mangacircus:local}
build:
context: .
args:
VCS_REF: ${VCS_REF:-development}
init: true
restart: unless-stopped
stop_grace_period: 90s
read_only: true
tmpfs: [/tmp]
security_opt: [no-new-privileges:true]
cap_drop: [ALL]
secrets: [scraper_token]
logging:
driver: local
options:
max-size: "10m"
max-file: "3"
services:
reader:
<<: *app
image: ${READER_IMAGE:-${APP_IMAGE:-mangacircus:local}}
command: [node, runtime/launch.js, reader]
ports: ["${READER_BIND:-127.0.0.1}:${READER_PORT:-8080}:8080"]
volumes:
- type: bind
source: ${LIBRARY_PATH:-./library}
target: /library
read_only: true
bind: {create_host_path: false}
- type: bind
source: ${DATA_PATH:-./data}
target: /data
bind: {create_host_path: false}
# Built-in backups (docs/backup.md). The folder must exist and be writable by
# uid 1000; backups stay off unless BACKUP_PATH is set.
- type: bind
source: ${BACKUP_PATH:-./backup}
target: /backup
bind: {create_host_path: false}
environment:
PORT: "8080"
DATA: /data
LIBRARY: /library
BACKUP: ${BACKUP_PATH:+/backup}
TZ: ${TZ:-Europe/Berlin}
SIGNUP: closed
SCAN_INTERVAL: "10"
COOKIE_SECURE: ${COOKIE_SECURE:-0}
READING_WINDOWS_JSON: ${READING_WINDOWS_JSON:-}
READING_TIMEZONE: ${READING_TIMEZONE:-Europe/Berlin}
READING_QUIZ_UNLOCK_MINUTES: ${READING_QUIZ_UNLOCK_MINUTES:-60}
READING_QUIZ_CATALOG_PATH: ${READING_QUIZ_CATALOG_PATH:-}
SCRAPER_URL: http://scraper:8091
SENTINEL_URL: http://sentinel:8092
SCRAPER_TOKEN_FILE: /run/secrets/scraper_token
networks: [backend, egress]
healthcheck:
test: [CMD, node, tools/healthcheck.js, reader]
interval: 30s
timeout: 5s
start_period: 60s
retries: 3
scraper:
<<: *app
command: [node, runtime/launch.js, scraper]
volumes:
- type: bind
source: ${LIBRARY_PATH:-./library}
target: /library
bind: {create_host_path: false}
- type: bind
source: ${SCRAPER_DATA_PATH:-./data/scraper}
target: /data/scraper
bind: {create_host_path: false}
- type: bind
source: ${PEER_DATA_PATH:-./data/peering}
target: /data/peering
read_only: true
environment:
DATA: /data
LIBRARY: /library
SCRAPER_DATA: /data/scraper
METADATA_CACHE: /data/scraper/metadata/anilist
TZ: ${TZ:-Europe/Berlin}
SCRAPER_HOST: 0.0.0.0
SCRAPER_PORT: "8091"
SCRAPER_TOKEN_FILE: /run/secrets/scraper_token
PEER_DATA: /data/peering
networks: [backend, egress]
healthcheck:
test: [CMD, node, tools/healthcheck.js, scraper]
interval: 30s
timeout: 5s
start_period: 60s
retries: 3
sentinel:
<<: *app
command: [node, runtime/launch.js, sentinel]
volumes:
- type: bind
source: ${SENTINEL_DATA_PATH:-./data/sentinel}
target: /data/sentinel
bind: {create_host_path: false}
environment:
DATA: /data
SENTINEL_DATA: /data/sentinel
SENTINEL_HOST: 0.0.0.0
SENTINEL_PORT: "8092"
READER_URL: http://reader:8080
SCRAPER_URL: http://scraper:8091
SCRAPER_TOKEN_FILE: /run/secrets/scraper_token
TZ: ${TZ:-Europe/Berlin}
networks: [backend]
healthcheck:
test: [CMD, node, tools/healthcheck.js, sentinel]
interval: 30s
timeout: 5s
start_period: 60s
retries: 3
peer-gateway:
<<: *app
profiles: [peering]
command: [node, peering/gateway.js]
ports: ["${PEER_BIND:-127.0.0.1}:${PEER_PORT:-8093}:8093"]
volumes:
- type: bind
source: ${LIBRARY_PATH:-./library}
target: /library
read_only: true
bind: {create_host_path: false}
- type: bind
source: ${PEER_DATA_PATH:-./data/peering}
target: /data/peering
read_only: true
- type: bind
source: ${PEER_USAGE_PATH:-./data/peering-usage}
target: /data/peering-usage
environment:
LIBRARY: /library
PEER_DATA: /data/peering
PEER_USAGE_FILE: /data/peering-usage/usage.json
PEER_HOST: 0.0.0.0
PEER_PORT: "8093"
# Docker Desktop does not publish host ports from an internal-only network.
networks: [backend, egress]
healthcheck:
test: [CMD, node, tools/healthcheck-peer.js]
interval: 30s
timeout: 5s
start_period: 30s
retries: 3
networks:
backend:
internal: true
egress: {}
secrets:
scraper_token:
file: ${SCRAPER_TOKEN_PATH:-./.secrets/scraper_token}