wrolpi/docker-compose.yml
Roland 513f46baf8
ai: package llama-server for all three deployments, with idle unload
install_llama_cpp.sh builds llama-server from the SHA-pinned v0.3.0
source tarball (single source of truth: on-device upgrade, Pi image,
Live ISO, and the new docker/llm container all call it; version-marker
makes re-runs no-ops).  start_llama_server.sh refuses to start unless
ai.yaml enables AI and the active model file exists — which also covers
unmounted media.  wrolpi-llm.service is installed but deliberately
never enabled at boot: the Controller starts it on demand (registered
in managed_services; the llm compose service follows the samba
pattern), and the API's perpetual idle-unload worker asks the
Controller to stop it after the configured idle minutes.  llama-server
is localhost-only natively, internal-network-only in docker, and never
proxied by Caddy.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-24 18:23:21 -06:00

188 lines
6.4 KiB
YAML

# Development containers, these should only be used if you are developing.
# The database is SQLite inside the media directory (./test/config/wrolpi.db); no db service.
version: '3'
services:
controller: # WROLPi Controller Service
build:
context: .
dockerfile: docker/controller/Dockerfile
restart: unless-stopped
ports:
- "8080:80"
volumes:
# Controller code (for development hot-reload)
- ./controller:/app/controller
# Docker socket for container management
- /var/run/docker.sock:/var/run/docker.sock
# Host system info (read-only)
- /proc:/host/proc:ro
- /sys:/host/sys:ro
# Media directory (needed for Samba share path validation)
- ${MEDIA_DIRECTORY:-./test}:/media/wrolpi
environment:
- DOCKERIZED=true
- MEDIA_DIRECTORY=/media/wrolpi
# Override CMD to enable auto-reload for development
command: ["python", "-m", "controller.serve"]
healthcheck:
test: [ 'CMD-SHELL', 'curl -f http://127.0.0.1:80/api/health || exit 1' ]
interval: 30s
timeout: 5s
start_period: 10s
retries: 3
api:
depends_on:
controller:
condition: service_started
build:
context: .
dockerfile: docker/api/Dockerfile
environment:
# Sanic 25.x wants 'spawn' but our code uses 'fork' for multiprocessing.
# This tells Sanic to accept the existing start method.
- SANIC_START_METHOD_SET=1
# llama-server runs in the llm container (native installs use 127.0.0.1).
- LLAMA_URL=http://llm:11435
volumes:
- './main.py:/opt/wrolpi/main.py'
- './wrolpi:/opt/wrolpi/wrolpi'
- './modules:/opt/wrolpi/modules'
- './scripts:/opt/wrolpi/scripts'
- './test:/opt/wrolpi/test'
- './test:/media/wrolpi'
# Help markdown for the AI help search (native installs read /opt/wrolpi-help directly).
- './docker/help/wrolpi-help/docs:/opt/wrolpi-help/docs:ro'
- './alembic:/opt/wrolpi/alembic'
- './alembic.ini:/opt/wrolpi/alembic.ini'
ports:
- ${REACT_APP_API-0.0.0.0:8081}:8081
user: '${UID-1000}:${GID-1000}'
healthcheck:
test: [ 'CMD-SHELL', 'curl http://127.0.0.1:8081/api/echo' ]
interval: 2m
archive: # Python/Sanic wrapper around Singlefile and Readability.
build:
context: .
dockerfile: docker/archive/Dockerfile
volumes:
- './docker/archive/main.py:/app/main.py'
ports:
- '8083:8080'
healthcheck:
test: [ 'CMD-SHELL', 'curl http://127.0.0.1:8080/' ]
interval: 2m
app: # React App
depends_on:
- api
build:
context: .
dockerfile: docker/app/Dockerfile
volumes:
- './app/public:/app/public'
- './app/src:/app/src'
- './app/package.json:/app/package.json'
- '.env:/app/.env'
command: 'npm run start'
stdin_open: true
healthcheck:
test: [ 'CMD-SHELL', 'curl http://127.0.0.1:3000' ]
interval: 1m
user: '${UID-1000}:${GID-1000}'
environment:
# Force React to use Caddy port to connect to it's websocket.
- WDS_SOCKET_PORT=0
# react-scripts lints in-process on every incremental rebuild, which costs time on each
# save and buries the compile result under a few hundred warnings the repo has carried
# for years. This container only serves; linting still happens where it is acted on --
# `npm run build` and CI both run it, and both fail on a real error.
#
# NOT a memory fix, though it was tried as one after the dev server was OOM-killed three
# times in an afternoon. Measured either way, the container settles anywhere between
# 3.0GB and 3.7GB and the spread between two runs of the SAME configuration is wider
# than the spread between configurations. Raising Docker Desktop's VM limit is what
# actually stopped the kills.
- DISABLE_ESLINT_PLUGIN=true
web: # Caddy reverse proxy - handles HTTPS for all services.
depends_on:
- app
- zim
- help
build:
context: .
dockerfile: docker/web/Dockerfile
ports:
- ${WEB_HOST-0.0.0.0}:${WEB_HTTPS_PORT-8443}:443
- '8084:8084'
- '8085:8085'
- '8086:8086'
volumes:
- './docker/web/Caddyfile:/etc/caddy/Caddyfile'
- './icon.ico:/etc/caddy/favicon.ico'
- './icon.png:/etc/caddy/icon.png'
- './test:/opt/media'
- './modules/map/static:/opt/wrolpi/modules/map/static:ro'
# No mount over /opt/wrolpi-blobs. A bind mount REPLACES the image's directory rather
# than merging with it, and the host './blobs' this used to mount is git-ignored -- so
# it was empty on every clone and hid the blobs the image provides. The web image bakes
# map-overview.pmtiles in; see docker/web/Dockerfile.
environment:
- MEDIA_DIRECTORY=/opt/media
# Set EXTRA_SANS to include the host's LAN IP in the TLS certificate.
# Example: EXTRA_SANS=IP:10.0.0.5
- EXTRA_SANS=${EXTRA_SANS:-}
healthcheck:
test: [ 'CMD-SHELL', 'wget --spider --no-check-certificate https://127.0.0.1:443 || exit 1' ]
interval: 1m
zim: # kiwix-serve
build:
context: .
dockerfile: docker/zim/Dockerfile
volumes:
- './test:/media/wrolpi'
- './docker/zim/entrypoint.sh:/entrypoint.sh'
healthcheck:
test: [ 'CMD-SHELL', 'curl http://127.0.0.1:80' ]
interval: 1m
llm: # llama-server (local AI) - started/stopped by the controller on demand; the API stops it when idle.
build:
context: .
dockerfile: docker/llm/Dockerfile
restart: "no"
depends_on:
controller:
condition: service_healthy
volumes:
- ${MEDIA_DIRECTORY:-./test}:/media/wrolpi
- './docker/llm/entrypoint.sh:/entrypoint.sh'
healthcheck:
test: [ 'CMD-SHELL', 'curl http://127.0.0.1:11435/health' ]
interval: 1m
samba: # Samba file sharing - started/stopped by the controller based on configured shares.
build:
context: .
dockerfile: docker/samba/Dockerfile
restart: "no"
depends_on:
controller:
condition: service_healthy
ports:
- "445:445"
volumes:
- ${MEDIA_DIRECTORY:-./test}:/media/wrolpi
help: # The WROLPi Help documentation.
build:
context: .
dockerfile: docker/help/Dockerfile
volumes:
- './docker/help/wrolpi-help:/opt/wrolpi-help'
healthcheck:
test: [ 'CMD-SHELL', 'curl http://127.0.0.1:8086' ]
interval: 1m