Skip to content

Latest commit

 

History

History
142 lines (132 loc) · 4.44 KB

File metadata and controls

142 lines (132 loc) · 4.44 KB

HomeLab Ansible

Infrastructure-as-code for my homelab. Uses playbook layering, component roles, and orchestrator roles for flexible targeting (e.g., deploy only Alloy on host A; Alloy+Loki on hosts in lokihosts).

Requirements

  • Ansible >= 2.19
  • Collections
ansible-galaxy install -r collections/requirements.yml
  • Recommended tree:
.
├── ansible.cfg
├── collections/requirements.yml
├── inventories/
│   └── prod/
│       ├── hosts.yml
│       ├── group_vars/
│       └── host_vars/
├── playbooks/
│   ├── site.yml
│   ├── base.yml
│   ├── services.yml
│   ├── docker.yml
│   └── keepalived.yml
└── roles/
    ├── apt_cache/
    ├── grafana_common/         # repo + shared prereqs
    ├── grafana_alloy/
    ├── grafana_loki/
    ├── grafana_stack/          # orchestrator (select components)
    ├── docker_common/          # engine + compose
    ├── docker_app_dns/         # AdGuard + Unbound compose app
    ├── docker_stack/           # orchestrator (select apps)
    └── keepalived/

Ensure ansible.cfg includes roles_path = ./roles:./playbooks/roles:~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles.

Inventory (example)

all:
  children:
    rpis:
      hosts:
        adguard01: { ansible_host: 192.168.10.253 }
        adguard02: { ansible_host: 192.168.10.254 }
    grafana_hosts:
      hosts:
        adguard01:
    lokihosts:
      hosts:
        log01:
    vip_dns:
      hosts:
        adguard01:
        adguard02:

inventories/lab/group_vars/grafana_hosts.yml

grafana_components: ["alloy"]   # picked up by grafana_stack

inventories/lab/group_vars/lokihosts.yml

grafana_components: ["loki"]   # picked up by grafana_stack

inventories/lab/group_vars/docker_hosts.yml

docker_apps: ["dns"]           # picked up by docker_stack

inventories/lab/group_vars/vip_dns.yml

keepalived_group: "vip_dns"
keepalived_vrid: 52
keepalived_vip: "192.168.10.252/24"
keepalived_auth_pass: "supersecret"

Playbooks

  • playbooks/site.yml: Baseline (e.g., apt cache)
  • playbooks/docker.yml: Docker engine + selected compose apps
  • playbooks/services.yml: Grafana stack (Alloy/Loki/etc.)
  • playbooks/keepalived.yml: VRRP VIP for DNS

Examples

Run baseline on a single host:

ansible-playbook -i inventories/lab/hosts.yml playbooks/base.yml --limit adguard02

Deploy Grafana layer to its group (repo + selected components):

ansible-playbook -i inventories/lab/hosts.yml playbooks/services.yml --tags grafana

Only Alloy configure step (no Loki) on current host:

ansible-playbook -i inventories/lab/hosts.yml playbooks/services.yml --tags alloy,configure --limit adguard01

Deploy Docker engine + DNS compose app:

ansible-playbook -i inventories/lab/hosts.yml playbooks/docker.yml --tags docker

Re-render DNS compose after Certbot (adds LE mount; restarts AdGuard only):

ansible-playbook -i inventories/lab/hosts.yml -l adguard01 -t configure \
  -e 'ansible_python_interpreter=/usr/bin/python3' \
  -e 'docker_app_dir=/opt/containers/dns' \
  -e 'docker_compose_file=/opt/containers/dns/compose.yml' \
  -e 'adguard_root=/opt/containers/dns/adguardhome' \
  -e 'unbound_root=/opt/containers/dns/unbound' \
  -e 'use_host_network_for_adguard=true' \
  -e 'adguard_listen_web_port=3000' \
  -e 'unbound_host_port=5335' \
  roles/docker_app_dns/tasks/render.yml

Deploy keepalived to VIP members:

ansible-playbook -i inventories/lab/hosts.yml playbooks/keepalived.yml --tags keepalived

Tagging Convention

  • Layer: base, docker, grafana, keepalived
  • Component: alloy, loki, dns, etc.
  • Step: install, configure, start, stop, `remove

Examples:

# Install phase for all Grafana components on target hosts
--tags grafana,install

# Only Loki install
--tags loki,install

# Only Docker repo setup
--tags docker:repo

Notes

  • Orchestrators (grafana_stack, docker_stack) do nothing by default. Enable components/apps per host/group with:
    • grafana_components: ["alloy", "loki"]
    • docker_apps: ["dns"]
  • Handlers restart only affected services (e.g., Alloy or AdGuard) when templates change.
  • Prefer ansible_host in inventory so peer IPs (keepalived unicast) resolve to addresses while still honoring SSH Host aliases.