infra/CLAUDE.md

7.3 KiB

CLAUDE.md

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

Repository Overview

This is a multi-environment infrastructure-as-code repository managing:

  • Ansible: Configuration management for servers across multiple environments (VNTX, home, app servers)
  • Terraform: DNS infrastructure management across DNSimple, Porkbun, and PowerDNS
  • Home Lab: Server configurations with Tailscale integration

Common Commands

Ansible Operations

# Run main playbook
cd ansible && ./run.sh

# Bootstrap new host
make bootstrap-init HOSTNAME=hostname ANSIBLE_HOST=ip_address

# Run specific playbook
ansible-playbook general.yml

# Run with specific tags
ansible-playbook -t caddy general.yml

# Gather facts from all hosts
make facts

Terraform Operations

IMPORTANT: Use OpenTofu (tofu) instead of terraform for all operations.

cd terraform
tofu init
tofu plan
tofu apply

# Format terraform files before committing
tofu fmt

Kubernetes Operations (K3s cluster at 204.110.191.2)

# Set kubeconfig
export KUBECONFIG=/Users/graham/dev/infra/home/ansible/kubeconfig

# Deploy APRS.me application
cd home/cluster/aprs
./deploy.sh

# Check deployment status
kubectl get pods -n aprs
kubectl get svc -n aprs

Talos Operations

# Apply configuration to nodes (from cluster directory)
talosctl apply-config --talosconfig=./talosconfig -e 10.0.101.21 --nodes 10.0.101.21 --file controlplane.yaml --config-patch @node1.patch.yaml --config-patch @tailscale.patch.yaml

# Check node status
talosctl --talosconfig=./talosconfig -e 10.0.101.21 get nodes

# Bootstrap cluster (only needed once)
talosctl --talosconfig=./talosconfig -e 10.0.101.21 bootstrap

# Get kubeconfig
talosctl --talosconfig=./talosconfig -e 10.0.101.21 kubeconfig

Architecture

Directory Structure

  • ansible/ - Ansible playbooks and roles for server configuration

    • roles/ - Reusable roles (base, caddy, docker, resolvers, etc.)
    • group_vars/ - Group-specific variables
    • host_vars/ - Host-specific variables
    • Main playbooks: playbook.yml, general.yml, bootstrap.yml
  • home/terraform/ - DNS infrastructure management

    • Manages domains: w5isp.com, vntx.net/org, manero.org, mcintire.me, gridmap.org
    • Providers: DNSimple, Porkbun, PowerDNS (reverse DNS)
  • home/ - Home lab configurations

    • cluster/ - Kubernetes application deployments
      • aprs/ - APRS.me deployment with PostgreSQL/PostGIS and Redis
      • wavelog/ - Wavelog amateur radio logging application
    • terraform/ - DNS infrastructure management

Key Infrastructure Services

Monitoring: Icinga2 with MariaDB backend DNS: Unbound resolvers, PowerDNS for reverse zones VPN: Tailscale integration across environments Web Proxy: Caddy for reverse proxy and SSL termination

Security Notes

  • Ansible user (UID 10001) with passwordless sudo
  • SSH keys fetched from GitHub for authentication
  • Tailscale requires TAILSCALE_KEY environment variable
  • Terraform credentials should use environment variables (TF_VAR_*)

Development Workflow

Adding New Hosts

  1. Add host to ansible/hosts inventory
  2. Create host_vars file if needed
  3. Run bootstrap: make bootstrap-init HOSTNAME=x ANSIBLE_HOST=y
  4. Apply configuration: ansible-playbook -l hostname general.yml

DNS Changes

  1. Edit appropriate home/terraform/dns_*.tf file
  2. Run tofu fmt to format
  3. Review with tofu plan
  4. Apply with tofu apply

Testing

Ansible

# Syntax check
ansible-playbook --syntax-check playbook.yml

# Dry run
ansible-playbook --check playbook.yml

# Test on specific host
ansible-playbook -l hostname playbook.yml

Terraform

tofu validate
tofu plan

Recent Infrastructure Updates

K3s Cluster at 204.110.191.2

  • Purpose: Home lab Kubernetes cluster
  • Node1 IPs:
    • Public: 204.110.191.2
    • Tailscale: 100.102.235.20
    • Internal: 10.0.101.21
  • API Access: Currently only available on 10.0.101.21:6443 (not on Tailscale)
  • Context: Use kubectl config use-context homelab to access
  • Storage: Longhorn for persistent volumes
  • Applications:
    • APRS.me: Amateur radio APRS tracking application (https://aprs.me)
      • PostgreSQL 17 with PostGIS (100GB Longhorn PVC)
      • Redis for caching
      • 2-replica StatefulSet for high availability
      • Erlang clustering enabled
      • LoadBalancer service assigned: 10.0.101.32
      • Note: ghcr.io/aprsme/aprs.me:latest requires authentication
      • Deploy with: cd home/cluster/aprs && ./deploy.sh
    • Wavelog: Amateur radio logging application (https://log.w5isp.com)
      • MariaDB 11 database (10GB storage)
      • 20GB storage for configs, backups, uploads, QSL cards
      • PHP application with custom limits (50MB uploads, 256MB memory)
      • Deploy with: cd home/cluster/wavelog && ./deploy.sh

New Server: w5isp.w5isp.com (204.110.191.200)

  • Purpose: Single-node server running Debian 13
  • Features:
    • Web services with automatic SSL
    • Single public IP handles all services

DNS Configuration

  • Photos: photos.w5isp.com → 204.110.191.212 (Caddy) → 100.107.11.77:2283
  • Mailcow: mcintire.me mail records → mail.w5isp.com (sync.w5isp.com at 204.110.191.216)
    • Full mail setup with MX, SPF, DKIM, DMARC records
    • Autodiscover/Autoconfig for mail clients
    • Backup MX: mail.nsnw.ca (priority 20)
    • Note: sync.w5isp.com is the Mailcow server (not using Caddy proxy)

Code Guidelines

Communication Practices

  • Do not ever reply with "you're right". Just fix the issue
  • Never say you're right, just accept it and move on

Notes for Claude

  • As you learn new things, keep claude.md updated

Clusters

  • k3s cluster at 204.110.191.2

Application Deployment Patterns

Standard App Deployment Process

  1. Check for existing manifests in /home/cluster/[app-name]/
  2. Set namespace security policy: kubectl label namespace [app] pod-security.kubernetes.io/enforce=privileged --overwrite
  3. Use ClusterIP services (internal only) with Traefik for external access
  4. Deploy via existing deploy.sh scripts when available
  5. Use Longhorn storage for persistent volumes
  6. Configure Traefik IngressRoute for SSL and routing

Deployed Applications

Common Issues & Solutions

  • PodSecurity violations: Set namespace to privileged security policy
  • Database connectivity: Use short service names (postgres not postgres.namespace.svc.cluster.local)
  • Init container permissions: Required for volume ownership fixes
  • SSL certificates: cert-manager with Let's Encrypt, handled automatically via Traefik
  • Service DNS: Services accessible via service-name within same namespace

Traefik Configuration

  • Host-based routing: Multiple apps on same IP (204.110.191.2)
  • Automatic SSL: cert-manager integration with Let's Encrypt
  • HTTP to HTTPS redirect: Configured in IngressRoute manifests
  • ClusterIP backend: All apps use ClusterIP, Traefik provides external access