linderhof/CLAUDE.md
Matthias Johnson 75891c3271 initial commit
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 15:09:25 -07:00

5 KiB

CLAUDE.md

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

Project Overview

Island is an Ansible-based self-hosting infrastructure stack that deploys email, web server, git hosting, Matrix homeserver, monitoring, and backup services using Docker Compose on Ubuntu servers.

Common Commands

# Select a stack (one-time per clone)
echo <stack-name> > .stack && direnv allow
# or: export LINDERHOF_STACK=<stack-name>

# Run all playbooks
ansible-playbook playbooks/site.yml

# Run a specific playbook
ansible-playbook playbooks/mail.yml

# Run specific tags only
ansible-playbook playbooks/site.yml --tags mail,monitoring

# Edit encrypted secrets
ansible-vault edit $LINDERHOF_DIR/group_vars/all/vault.yml

# Encrypt/decrypt vault
ansible-vault encrypt $LINDERHOF_DIR/group_vars/all/vault.yml
ansible-vault decrypt $LINDERHOF_DIR/group_vars/all/vault.yml

Note: Inventory and vault password are set via ANSIBLE_INVENTORY and ANSIBLE_VAULT_PASSWORD_FILE in .envrc, driven by LINDERHOF_STACK. No extra flags needed once the stack is selected.

Architecture

Deployment Pattern: Each service is deployed to /srv/<service>/ on the target host with a compose.yml and environment files.

Standalone Playbooks (not in site.yml):

  • provision.yml - Provision a cloud VM (Hetzner). Usage: ansible-playbook playbooks/provision.yml
  • dns.yml - Manage DNS zones/records via Hetzner DNS API

Playbook Execution Order (via site.yml):

  1. bootstrap.yml - SSH, sudo, users, base packages (manual only)
  2. docker.yml - Docker engine installation
  3. docker_network.yml - Pre-create all Docker networks (must run before any service)
  4. nebula.yml - Overlay network (Nebula)
  5. caddy.yml - Web server / reverse proxy
  6. mail.yml - Email (docker-mailserver + rainloop)
  7. forgejo.yml - Git server
  8. tuwunel.yml - Matrix homeserver (Tuwunel)
  9. monitoring.yml - Prometheus, Grafana, Loki, Alloy
  10. goaccess.yml - Web analytics
  11. diun.yml - Docker image update notifications
  12. restic.yml - Encrypted backups
  13. fail2ban.yml - Intrusion prevention

Role Structure: Each role in roles/ contains:

  • tasks/main.yml - Core provisioning tasks
  • templates/ - Jinja2 templates (compose.yml.j2, config files)
  • handlers/main.yml - Service restart handlers
  • files/ - Static configuration files

Configuration (lives outside the repo in $XDG_CONFIG_HOME/linderhof/<stack>/):

  • $LINDERHOF_DIR/hosts.yml - Host connection info only
  • $LINDERHOF_DIR/group_vars/all/config.yml - All public configuration
  • $LINDERHOF_DIR/group_vars/all/vault.yml - All secrets (encrypted)
  • $LINDERHOF_DIR/group_vars/all/dns.yml - DNS zone definitions
  • $LINDERHOF_DIR/group_vars/all/overrides.yml - Per-stack variable overrides (optional)
  • $LINDERHOF_DIR/stack.env - Per-stack shell vars (DOCKER_HOST, etc.)
  • $LINDERHOF_DIR/vault-pass - Vault encryption key (chmod 600)

Template files (in the repo, used by setup.sh):

  • inventory/group_vars/all/config.yml.setup - Config template
  • inventory/group_vars/all/vault.yml.setup - Vault template
  • inventory/group_vars/all/dns.yml.setup - DNS zones template

Overriding variables without editing config.yml — create overrides.yml:

# Example: override mail hostname during migration
mail_hostname: mail2.example.com
# Example: add extra static sites to Caddy
caddy_sites:
  - example.com
  - example2.com

Service Toggles: Set enable_<service>: false in config.yml to disable:

  • enable_mail
  • enable_forgejo
  • enable_tuwunel
  • enable_monitoring
  • enable_restic
  • enable_fail2ban
  • enable_nebula
  • enable_diun

Docker Networks: All networks are pre-created by the docker_network role before any service deploys. Services declare all networks as external: true in their compose.yml.j2 — no service creates its own network. Networks are created conditionally based on enable_* flags:

Network Created when
caddy always
mail enable_mail
webmail enable_mail
git enable_forgejo
monitoring enable_monitoring
tuwunel enable_tuwunel
radicale enable_radicale

Caddy's compose.yml.j2 also conditionally declares network references using the same enable_* flags so it never references a network that wasn't created.

Adding a new service: create the network in docker_network/tasks/main.yml with the appropriate when: condition, declare it external: true in the service compose template, and add it to caddy's compose template if caddy needs to reach it.

Available Tags

  • bootstrap - Initial server setup (use --tags bootstrap)
  • docker - Docker installation
  • mail - Mail server
  • forgejo - Git server
  • tuwunel - Matrix homeserver
  • monitoring - Monitoring stack
  • restic - Backup configuration
  • fail2ban - Intrusion prevention
  • nebula - Overlay network
  • diun - Docker image update notifications
  • config - Configuration-only updates