# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Overview This is a personal infrastructure Ansible playbook that automates deployment and management of 22+ self-hosted Docker services across two domains (`thesatelliteoflove.com` and `nerder.land`). The setup uses Tailscale VPN for secure networking and Caddy for reverse proxy with automated HTTPS. **Important**: Always review `DEPLOYMENT_LEARNINGS.md` when working on this repository for lessons learned and troubleshooting guidance. ## Common Commands ### Initial Setup ```bash # Install Ansible dependencies ansible-galaxy install -r requirements.yml # Bootstrap new server (creates user, installs Tailscale, security hardening) ansible-playbook bootstrap.yml -i hosts.yml # Deploy all Docker services ansible-playbook site.yml -i hosts.yml # Update DNS records in AWS Route53 ansible-playbook dns.yml -i hosts.yml ``` ### Service Management ```bash # Deploy specific services using tags (now properly isolated) ansible-playbook site.yml -i hosts.yml --tags caddy --vault-password-file vault_pass --extra-vars "@secrets.enc" ansible-playbook site.yml -i hosts.yml --tags authentik --vault-password-file vault_pass --extra-vars "@secrets.enc" ansible-playbook site.yml -i hosts.yml --tags mmdl --vault-password-file vault_pass --extra-vars "@secrets.enc" ansible-playbook site.yml -i hosts.yml --tags docker --vault-password-file vault_pass --extra-vars "@secrets.enc" # all docker services # Deploy services by category (new organized structure) ansible-playbook site.yml -i hosts.yml --tags infrastructure --vault-password-file vault_pass --extra-vars "@secrets.enc" ansible-playbook site.yml -i hosts.yml --tags media,productivity --vault-password-file vault_pass --extra-vars "@secrets.enc" ansible-playbook site.yml -i hosts.yml --tags development,monitoring --vault-password-file vault_pass --extra-vars "@secrets.enc" # Deploy only infrastructure components ansible-playbook site.yml -i hosts.yml --tags common,cron --vault-password-file vault_pass --extra-vars "@secrets.enc" ``` ## Architecture ### Host Configuration - **Bootstrap Host** (`netcup`): 152.53.36.98 - Initial server setup target - **Docker Host** (`docker-01`): 100.70.169.99 - Main service deployment via Tailscale ### Role Structure - **bootstrap**: Initial server hardening, user creation, Tailscale VPN setup - **common**: Basic system configuration, UFW firewall management - **docker**: Comprehensive service deployment (22+ containerized applications, organized by category) - **cron**: Scheduled task management (currently Warhammer RSS feed generation) ### Docker Role Organization (Reorganized into Logical Categories) The docker role is now organized into logical service groups under `roles/docker/tasks/`: - **infrastructure/**: Core platform components - Caddy (reverse proxy), Authentik (SSO), Dockge (container management) - **development/**: Development and collaboration tools - Gitea, Code Server, Matrix (Conduit) - **media/**: Content creation and consumption - Audiobookshelf, Calibre, Ghost blog, Pinchflat, Pinry, Karakeep, Manyfold - **productivity/**: Personal organization and document management - Paperless-NGX, MMDL, Baikal (CalDAV/CardDAV), Syncthing, Heyform, Dawarich, Pingvin - **communication/**: Social media and external communication - GoToSocial (Fediverse), Postiz (social media management) - **monitoring/**: System monitoring and alerts - Changedetection, Glance dashboard, AppriseAPI ### Variable Management **Critical**: This infrastructure uses a centralized variable hierarchy in `group_vars/all/`: - **domains.yml**: Domain and subdomain mappings (use `{{ subdomains.service }}`) - **infrastructure.yml**: Network configuration, Docker settings (`{{ docker.network_name }}`, `{{ docker.hairpin_ip }}`) - **vault.yml**: Encrypted secrets with `vault_` prefix - **services.yml**: Service-specific configuration and feature flags **Important**: All templates use variables instead of hardcoded values. Never hardcode domains, IPs, or secrets. ### Data Structure - All service data stored in `/opt/stacks/[service-name]/` on docker host - Docker Compose files generated from Jinja2 templates in `roles/docker/templates/` - Environment files templated for services requiring configuration - All configurations use centralized variables for consistency ## Key Implementation Details ### Template-Driven Configuration The docker role uses Jinja2 templates exclusively for all services. When modifying services: - Update templates in `roles/docker/templates/[service]-compose.yml.j2` - Environment files use `.env.j2` templates where needed - Task files organized by category in `roles/docker/tasks/[category]/[service].yml` - All services now use templated configurations (no static compose files) ### DNS Management The `dns.yml` playbook manages AWS Route53 records for both domains. All subdomains point to the netcup server (152.53.36.98), with Caddy handling internal routing to the docker host via Tailscale. ### Security Architecture - Tailscale provides secure networking between management and service hosts - Services are network-isolated using Docker - Caddy handles SSL termination with automatic Let's Encrypt certificates - UFW firewall managed through Docker integration script ### Service Dependencies Many services depend on Authentik for SSO. When deploying new services, consider: - Whether SSO integration is needed - Caddy routing configuration for subdomain access - Network connectivity requirements within Docker stack - Hairpinning fixes for internal service-to-service communication ### Hairpinning Resolution Services inside Docker containers cannot reach external domains that resolve to the same server. Fix by adding `extra_hosts` mappings: ```yaml extra_hosts: - "{{ subdomains.auth }}:{{ docker.hairpin_ip }}" - "{{ subdomains.cal }}:{{ docker.hairpin_ip }}" ``` Common domains requiring hairpinning fixes: - `{{ subdomains.auth }}` (Authentik SSO) - `{{ subdomains.cal }}` (Baikal CalDAV) - Any service domain the container needs to communicate with **Note**: Use variables instead of hardcoded values for maintainability. ### Service-Specific Reference Configurations - **Dawarich**: Based on production compose file at https://github.com/Freika/dawarich/blob/master/docker/docker-compose.production.yml ## Service Memories - pingvin is the service that responds on files.thesatelliteoflove.com ## Variable Management Implementation Notes **Major Infrastructure Update**: Variable management system was implemented to replace all hardcoded values with centralized variables. ### Key Changes Made: - Created comprehensive `group_vars/all/` structure - Updated all Docker Compose templates to use variables - Fixed service tag isolation (individual service tags now deploy only that service) - Standardized domain and network configuration - Organized secrets by service with consistent `vault_` prefix ### Service Tag Fix: **Critical**: Service tags are now properly isolated. `--tags mmdl` deploys only MMDL (5 tasks), not the entire productivity category. ### Template Pattern: All templates now follow this pattern: ```yaml # Use variables, not hardcoded values glance.url: "https://{{ subdomains.service }}/" networks: default: external: true name: "{{ docker.network_name }}" extra_hosts: - "{{ subdomains.auth }}:{{ docker.hairpin_ip }}" ```