Files
2026-05-03 16:34:57 +02:00

4.0 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Overview

A collection of Docker Compose recipes for self-hosted services. Each service lives in a numbered directory (e.g. 01-blog/, 08-immich/) containing a single docker-compose.<name>.yml file.

Deploying services

Copy .env.default to .env and configure it. The DEPLOY variable is a space-separated list of directory names to activate:

DEPLOY=01-blog 04-gitea 07-ntfy

Run all active services at once:

./run-docker-compose.sh --env-file .env up -d
./run-docker-compose.sh --env-file .env down

Or target a single service directly:

docker compose --env-file .env -f 01-blog/docker-compose.blog.yml up -d
docker compose --env-file .env -f 01-blog/docker-compose.blog.yml logs -f

Repository structure

Directory Service Port
00-diun Docker image update notifier (reads $ROOT_INSTALL/data/diun/diun.yml)
00-tugtainer Tugtainer agent — exposes Docker API via socket proxy 9413
01-blog Custom blog + Stacosys comments (private registry: source.madyanne.fr) 8011
02-selfoss Self-hosted RSS reader 8888
03-shaarli Bookmark manager 8013
04-gitea Git hosting (rootless, SSH on 2222) 8014
05-wallabag Read-it-later 8015
06-heimdall Application dashboard 8016
07-ntfy Push notification server 8017
07-seafile File sync (MariaDB + Memcached stack) 8017/8080
08-immich Photo management (Postgres + Redis + ML stack) 2283
08-ittools IT-Tools web app 8018
09-bichon Email client 8019
09-navidrome Music streaming (reads music from $SEAFILE_ZIC) 4533
10-monitor Tugtainer monitoring dashboard with socket proxy 9412

Key conventions

  • Persistent data: bind-mounted from $ROOT_INSTALL/data/<service>/ on the host (default /srv/data/<service>/). Named Docker volumes are used for cache/ephemeral data.
  • Common env vars: TZ, PUID, PGID, ROOT_INSTALL, DOMAIN — set once in .env, referenced across all compose files.
  • Socket proxy pattern: services that need Docker API access (tugtainer, monitor) use lscr.io/linuxserver/socket-proxy instead of mounting the socket directly.
  • Private images: 01-blog and 02-selfoss pull from source.madyanne.fr. GIT_TOKEN is passed to the blog container for private repo access.
  • Seafile bind mounts: 08-immich and 09-navidrome mount Seafile storage via rslave propagation and require privileged: true + SYS_ADMIN cap.

Adding a new service

  1. Create a new numbered directory (pick a prefix that reflects priority/grouping).
  2. Add a docker-compose.<name>.yml inside it.
  3. Reference shared variables from .env using ${VAR} syntax.
  4. Add the directory name to the DEPLOY line in .env to activate it.
  5. Document any new required variables in .env.default.

Compose file conventions

  • Image overrides: images use ${IMAGE_VAR:-default:tag} syntax so the tag can be pinned via .env without editing the compose file.
  • Healthchecks: multi-container stacks define healthcheck: on dependencies and use depends_on: <svc>: condition: service_healthy to enforce startup order.
  • Named volumes are used for mutable app state that doesn't need direct host access (DB data, caches). Use bind mounts only when the host path matters (e.g. backup targets, shared Seafile storage).
  • run-docker-compose.sh reads DEPLOY directly from .env (ignoring any --env-file argument for that purpose), so .env must always exist at the repo root.

Useful .env.default groupings

The default file includes commented-out DEPLOY examples showing how services are grouped for deployment:

  • Full stack: 00-tugtainer 01-blog 02-selfoss 03-shaarli 04-gitea 05-wallabag 06-heimdall 07-ntfy 08-ittools 09-bichon
  • Seafile cluster: 00-tugtainer 07-seafile
  • Media stack: 08-immich 09-navidrome