# servers Homelab infrastructure managed with Ansible and tested against local QEMU VMs. Two production VPS hosts (proxy and IDP) are connected via WireGuard and run everything behind Apache reverse proxies with Let's Encrypt TLS. ## Architecture ``` ┌─────────────────────────────────────────────────┐ │ Proxy (10.0.0.1) │ │ ┌───────────┐ ┌──────────┐ ┌────────────────┐ │ │ │ Apache │ │ Prosody │ │ Docker Registry│ │ │ │ (reverse │ │ (XMPP) │ │ (pull-through) │ │ │ │ proxy) │ │ │ │ │ │ │ ├───────────┤ ├──────────┤ ├────────────────┤ │ │ │ ConverseJS│ │ Bugzilla │ │ Comentario │ │ │ │ Kellnr │ │ DevPI │ │ dnsmasq │ │ │ └───────────┘ └──────────┘ └────────────────┘ │ │ WireGuard ─────────────────────────┐ │ └─────────────────────────────────────┼────────────┘ │ ┌─────────────────────────────────────┼────────────┐ │ IDP (10.0.0.2) │ │ │ ┌──────────────────────────────────┘ │ │ │ Authentik (identity provider) │ │ │ PostgreSQL + Redis │ │ └───────────────────────────────────────────────┘ └──────────────────────────────────────────────────┘ ``` Clients (phone, workstation) connect over WireGuard and resolve names via dnsmasq on the proxy. ## Quick start ```bash # Install Python dependencies pipenv install # Install Ansible collection dependencies pipenv run ansible-galaxy collection install -r ansible/requirements.yml # Deploy to production pipenv run ansible-playbook -i ansible/inventories/prod/hosts.ini ansible/playbooks/setup.yml # Deploy a single service (e.g. bugzilla) pipenv run ansible-playbook -i ansible/inventories/prod/hosts.ini ansible/playbooks/setup.yml --tags bugzilla # Deploy a service without its dependencies pipenv run ansible-playbook -i ansible/inventories/prod/hosts.ini ansible/playbooks/setup.yml --tags bugzilla --skip-tags docker,apache ``` See [DEVELOPMENT.md](DEVELOPMENT.md) for the full local development workflow. ## Available tags | Tag | What it deploys | Host | |-----|----------------|------| | `host` | Base host config (SSH keys, users, swap) | all | | `docker` | Docker engine + registry mirrors | docker_hosts | | `wireguard` | WireGuard tunnels | proxy, idp | | `apache` | Apache + Let's Encrypt certs | proxy | | `dnsmasq` | DNS resolver | proxy | | `docker-registry` | Pull-through registry mirrors | proxy | | `restic` | Backup infrastructure | proxy, idp | | `prosody` | XMPP server + upload proxy | proxy | | `conversejs` | Web XMPP client | proxy | | `kellnr` | Rust crate registry | proxy | | `devpi` | Python package index | proxy | | `comentario` | Comment system | proxy | | `bugzilla` | Bug tracker | proxy | | `authentik` | Identity provider + reverse proxy | idp, proxy | | `blog` | Static blog site | proxy | | `spec` | Static spec site | proxy | ## Backups ```bash # Run backup pipenv run ansible-playbook -i ansible/inventories/prod/hosts.ini ansible/playbooks/backup.yml # Restore pipenv run ansible-playbook -i ansible/inventories/prod/hosts.ini ansible/playbooks/restore.yml ``` Automated backups run via systemd timer (bi-weekly by default). ## Vault Sensitive variables live in `ansible/inventories/prod/group_vars/all/vault.yml`, encrypted with `ansible-vault`. The vault password file is `.vault-pass` (not committed). ```bash # Edit vault pipenv run ansible-vault edit ansible/inventories/prod/group_vars/all/vault.yml ``` ## Authentik notes App passwords with more than 30 minutes expiration require a user or group attribute: ``` goauthentik.io/user/token-maximum-lifetime: days=365 ```