docker-compose.yml
Rama main · réplica de lectura del repositorio original.
# BoardRipper Docker Compose Configuration
#
# Quick start:
# 1. docker compose up -d
# 2. Open http://localhost:8081
#
# Volumes:
# - ./data → uploaded board files (persists across restarts)
# - ./library → mount your board file folders here for browsing in the Library panel
# Mount individual folders as subdirectories of /library, e.g.:
# - /path/to/MacBooks:/library/MacBooks:ro
# - /path/to/iPhones:/library/iPhones:ro
# These will appear as top-level folders in the Library browser.
#
# Environment variables:
# PORT — internal server port (default: 8080, no need to change)
# STATIC_DIR — frontend files location (default: /static, set by image)
# DATA_DIR — upload storage (default: /data, mapped via volume)
# LIBRARY_DIR — library root for file browser (default: /library)
services:
boardripper:
build: .
# The image ships USER 65532:65532 (the distroless nonroot convention)
# so a hypothetical RCE doesn't own the bind-mounted volumes. The
# default `docker compose up -d` flow creates ./data and ./library on
# the host as root, which UID 65532 then can't write — `databank.Open`
# log.Fatals at boot, the container exits, and the user gets a restart
# loop on a clean install. The simplest fix that preserves the
# documented one-command flow is to override USER → root for this
# compose deploy, matching what `scripts/deploy-remote.sh` already
# does on the NAS. Comment this out (or remove it) and either
# (a) chown your host bind-mount targets to 65532:65532, or
# (b) switch ./data to a named volume — Docker initializes named
# volumes from the image's pre-chowned /data, preserving 65532.
user: "0:0"
ports:
- "8081:8080" # Change 8081 to your preferred external port
volumes:
- ./data:/data # Board file upload storage
- ./library:/library # Board file library (read-only browsing)
# Docker socket — required for self-update. The running container
# calls the Docker Engine API to pull the new image and swap itself
# out. Without this mount, `Update & Restart` will be greyed out and
# the drop-to-update fallback won't work either. Combined with the
# `user: "0:0"` above, this is what makes in-app updates possible on
# a fresh install. Remove only if you don't want self-update.
- /var/run/docker.sock:/var/run/docker.sock
restart: unless-stopped
# No container healthcheck: the scratch image has no shell, curl, or wget,
# so it cannot self-healthcheck. `test: ["NONE"]` disables any healthcheck
# inherited from the image (the interval/timeout/retries/start_period keys
# are no-ops once the test is NONE, so they're omitted). Liveness is polled
# externally — the self-update orchestrator hits /api/health after a swap
# (see the secure update pipeline in CLAUDE.md / docs/RELEASE_RUNBOOK.md).
healthcheck:
test: ["NONE"]
# Hard memory cap. `mem_limit` maps to --memory on every Compose version;
# `deploy.resources.limits.memory` was ignored by early Compose v2 outside
# swarm (the container ran uncapped) and is honoured by current releases
# (measured 2026-09-12 on Compose 5.5) — `mem_limit` is the one that has
# always worked, so it stays. The runtime derives a soft GOMEMLIMIT at
# 90% of this (see configureMemoryLimit in main.go) so the GC reclaims before
# the hard cap OOM-kills, and GODEBUG=madvdontneed=1 (Dockerfile) returns
# freed pages promptly. Tune down on a small NAS together with
# PDFINDEX_POOL_MAX; the pdfium/wazero linear memory is the main floor.
mem_limit: 2g
environment:
- PORT=8080
- PDFINDEX_POOL_MAX=2 # max concurrent pdfium/wazero workers; reduce to 1 on NAS with <1 GB free RAM
# GITHUB_TOKEN is no longer needed — updates use offline-signed manifests
# from ripperdoc.de and ghcr.io (see docs/RELEASE_RUNBOOK.md).