This Ansible playbook allows me to set up and configure all my home lab servers completely automatically, with little to no intervention. It is for personal use; do not rely on this for anything important.
Special thanks to Wolfgang for the idea of automating the installation process. This project was largely inspired by his own infra repo.
The following services are managed completely automatically:
- Gitea
- Matrix Synapse
- Syncthing
- Navidrome
- Paperless-ngx
- Exim mail (internal use only)
- Caddy reverse proxy
- WireGuard VPN server.
- Firewall setup (UFW)
- Python bootstrapping
- Setting up static IP in LAN
- External storage decryption/mounting
- Dotfile installation
The playbook assumes fresh Arch Linux ARM images installed on machines in your LAN, connected via Ethernet.
They should start off with default credentials (i.e. alarm:alarm
, root:root
).
This repo takes care of everything else.
The intended topology is a bastion host facing the Internet, with reverse proxies forwarding traffic to a service host inside the firewall.
The servers are all on a WireGuard network.
This network also serves as the typical "encrypted tunnel" for devices on the go.
-
Flash all your machines with Arch Linux ARM.
-
Copy
inventory.example.yml
toinventory.yml
. -
Write down the machines' DHCP addresses inside
inventory.yml
under thefallback_host
field. -
Assign static LAN IP addresses for your machines in the inventory.
-
Create ssh keys for all your hosts:
mkdir -p ~/.ssh/keys ssh-keygen -t ed25519 -f ~/.ssh/keys/your_host_name
It is important for the hostnames to match your inventory hostnames.
-
Set up your domain name and networks.
- Forward all needed ports to your bastion host's static IP.
- Set up a dynamic DNS subdomain, for example via nsupdate.
- Create subdomains for Gitea, Matrix, and Navidrome. These should be forwarded to your dynamic DNS subdomain via CNAME records. Configure these subdomains in
group_vars
(see below.)
-
Install required packages:
- sshpass
- python-passlib
-
Look at
group_vars/all/50-vars.yml
, and set needed settings inhost_vars/<hostname>/vars.yml
, orgroup_vars/all/90-overlay.yml
. (Files in group vars with a larger number have more precedence.) -
Look at the following roles, and for each of them override their
defaults/vars.yml
in host or group vars:networking/connection
networking/nameserver
caddy
containers
filesystems
firewall
syncthing
website
-
Create vault for secrets:
ansible-vault create group_vars/all/80-vault.yml ansible-vault edit group_vars/all/80-vault.yml
Copy-paste
group_vars/all/00-secret_template.yml
into this vault, and modify as needed. -
Add secret files:
# Keyfile for LUKS disk encryption dd if=/dev/random of=roles/filesystems/files/host1.secret bs=1024 count=2 ansible-vault encrypt roles/filesystems/files/host1.secret # repeat the above for every host with encrypted external storage
-
Run the playbook:
ansible-playbook run.yml --ask-vault-pass