Skip to content

Inventory & variables

Inventory files

The active inventory is set in ansible.cfg:

inventory = inventories/stage/hosts.yml

Override it at runtime with -i or ENV=:

# Makefile
make ENV=prod web

# Direct
ansible-playbook -i inventories/prod/hosts.yml web.yml

Host groups

Both inventories use the same group structure:

Group Playbook Description
web web.yml WordPress/Docker web servers
db db.yml MariaDB database servers
tools tool.yml Verdaccio + tooling server
analytics analytics.yml Analytics stack
# inventories/prod/hosts.yml (abridged)
all:
  children:
    web:
      hosts:
        bkdk-web-01:
          ansible_host: bkdk-e01.subscribed.build
    db:
      hosts:
        bk-db-01:
          ansible_host: bk-a01.subscribed.build
    tools:
      hosts:
        tools-server-01:
          ansible_host: tl-e01.subscribed.build
    analytics:
      hosts:
        analytics-e01:
          ansible_host: analytics-e01.subscribed.build

Variable hierarchy

Ansible merges variables from multiple places. Higher numbers win:

1. role defaults/main.yml        (lowest priority)
2. group_vars/all/               (shared across all hosts)
3. inventories/prod/group_vars/  (group-specific, e.g. db.yml)
4. inventories/prod/host_vars/   (host-specific)
5. playbook vars / extra vars    (highest priority)

Variable directories

group_vars/all/

Shared defaults for every host in every environment:

File Content
common.yml Timezone, SSH settings, UFW defaults, operator users
allowlist.yml IP allowlists (office, Plecto, Supermetrics, private CIDRs)
cronjobs.yml Cron job definitions for web servers
deployment.yml Deploy user definition
vault.yml Encrypted global secrets

inventories/prod/group_vars/

Group-specific variables for production:

File Content
db.yml MariaDB bind address, binlog settings, auditd extra rules
db.vault.yml Encrypted DB secrets (passwords, SqlBak keys)
web.vault.yml Encrypted web secrets (Cloudflare tunnel token)
analytics.yml Analytics-specific settings

inventories/prod/host_vars/<hostname>/

Each host gets a directory with two files:

host_vars/
└── bkdk-web-01/
    ├── main.yml    ← non-sensitive: sites, cloudflare config, system.users
    └── vault.yml   ← encrypted: passwords, tokens

Always use this split pattern. Keep sensitive values in vault.yml and reference them from main.yml using vault_ prefixed variables.

The system variable

The system key is a structured variable defined in group_vars/all/common.yml and extended per-host. It acts as a namespace for host-level configuration:

system:
  users:
    - name: barberklingen
      sudo: true
      ssh:
        generation: true    # generate an ed25519 key pair on the server
        authorized_key: true
        connect: true       # add to SSH AllowUsers

  access_users_groups:
    - barberklingen
    - www-data
    - docker
    - sd
    - pt

  logrotate:
    force_cron_5_enabled: true
    mysql_enabled: false

The sites variable

Web hosts define their WordPress sites under sites. Each entry drives user creation, cron job installation, and log rotation:

sites:
  - name: barberklingen
    domain: barberklingen.dk
    user: barberklingen
    project_path: /home/barberklingen/barberklingen-dk-bedrock
    cronjob:
      dir: barberklingen-dk
      enable:
        - name: as-global
        - name: wp-cron
        - name: klaviyo-export-events
    logrotate:
      system_logs_glob_paths:
        - /home/barberklingen/barberklingen-dk-bedrock/system/logs/*.log

Adding a new group variable file

# Plain variables
touch inventories/prod/group_vars/web.yml

# Encrypted vault file
ansible-vault create inventories/prod/group_vars/web.vault.yml

Both files are loaded automatically by Ansible for every host in the web group.

Naming conventions

Pattern Use
vault_* Any variable whose value is a secret
*_enabled Boolean feature flags
role_name_* Variables owned by a specific role (e.g. mariadb_bind_address)
system.* Host-level structured config (users, groups, logrotate)