# 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 ```bash # Select a stack (one-time per clone) echo > .stack && direnv allow # or: export LINDERHOF_STACK= # 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//` 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//`): - `$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`: ```yaml # 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_: 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