Skip to content

⚙ Configuration

🔍 Dynamic Proxmox Inventory

This project uses dynamic inventory for Proxmox VE. The inventory files inventory/amd64.proxmox.yaml and inventory/arm64.proxmox.yaml use the community.proxmox.proxmox plugin to connect to a Proxmox VE instance and retrieve information about LXC containers. This allows for dynamic discovery of hosts based on your Proxmox setup.

🔑 Creating a Proxmox API Token

To use the dynamic inventory, you need to create a Proxmox API token. Here's how to do it:

  1. Log in to the Proxmox web interface.
  2. Navigate to Datacenter -> Permissions -> API Tokens.
  3. Click Add to create a new API token.
  4. Select the user you want to create the token for (e.g., root@pam).
  5. Provide a Token ID (e.g., ansible).
  6. Uncheck the Privilege Separation checkbox. This is important for the dynamic inventory to work correctly.
  7. Click Add.
  8. Copy the Token ID and Secret. You will need these for your Ansible configuration. The secret will not be shown again, so make sure to save it in a secure location.

Security Recommendation

For enhanced security, it is recommended to create a dedicated, non-root user for Ansible and to generate the API token under that user. This user should be granted only the minimum necessary permissions for Ansible to manage your Proxmox resources.

🔧 Options

The following options can be configured in inventory/amd64.proxmox.yaml and inventory/arm64.proxmox.yaml:

  • plugin: The Ansible plugin to use. This should be set to community.proxmox.proxmox.
  • url: The URL of your Proxmox VE instance.
  • validate_certs: Whether to validate the SSL certificate of the Proxmox server. Defaults to false.
  • want_facts: Whether to retrieve facts about the Proxmox nodes. Defaults to true.
  • user: The Proxmox user to connect with.
  • token_id: The ID of the Proxmox API token.
  • token_secret: The secret of the Proxmox API token. This should be encrypted with Ansible Vault, see the Secrets guide.

💻 Example

---
plugin: community.proxmox.proxmox
url: https://pve01.l.nicholaswilde.io
validate_certs: false
want_facts: true

# Proxmox credentials. Create a token on the Proxmox node.
user: root@pam
token_id: ansible

# password file .vault_pass
token_secret: !vault |
          $ANSIBLE_VAULT;1.1;AES256
          63356539313533663964636139333037386262616239373638333136643834303631653139633437
          6562613738653338613134343635383639363237343833390a353364373034383664343661316339
          64333066646635396134323334336164633965373830666665353431326338363131386530383631
          3939643133346134300a393136636137326431303965353537386665323766313464666331333337
          62326331663361376632393331396439383666333739323766316432393761666561356266643938
          3165353364633639326632336539313466343531363431373465

compose:
  ansible_host: proxmox_agent_interfaces[1]['ip-addresses'][0] | default(proxmox_net0.ip) | ansible.utils.ipaddr('address')
  ansible_user: "'root'"

ansible_user Quoting

The ansible_user value needs to be in single quotes inside of double quotes (e.g., "'root'"). This specific quoting is necessary due to the multi-layered parsing involved:

  1. YAML Parsing: The outer double quotes ("...") instruct the YAML parser to treat the entire content ('root') as a single string.
  2. Jinja2 Evaluation: Ansible's compose feature then evaluates this string using the Jinja2 templating engine. The inner single quotes ('...') ensure that the final output of this Jinja2 evaluation is a literal string 'root', which is what the underlying system expects for the ansible_user variable.

The ansible_user variable in the compose section sets the default SSH user for all discovered Proxmox LXC containers. In the example above, it is set to 'root' for all containers.

📝 Conditionally Setting ansible_user

You can conditionally set the ansible_user for specific hosts within the dynamic inventory by using a Jinja2 expression in the compose section. This is useful when most hosts use a default user (like root), but a few require a different user.

The name variable, which corresponds to the Proxmox guest's name, can be used in the conditional.

For example, to use the nicholas user for the host named arm64 and root for all other hosts discovered by this inventory source, you can set ansible_user as follows:

compose:
  ansible_host: proxmox_agent_interfaces[1]['ip-addresses'][0] | default(proxmox_net0.ip) | ansible.utils.ipaddr('address')
  ansible_user: "'nicholas' if name == 'arm64' else 'root'"

This approach keeps the user configuration within the dynamic inventory file, avoiding the need for separate static inventory files for overrides.

Raspberry Pi Hosts

I have multiple Raspberry Pis on my network that are not part my dynamic inventory. They are managed using a static inventory file and specific playbooks.

📁 Inventory

The inventory for the Raspberry Pi hosts is defined in inventory/rpis.yaml. This is a static inventory file that lists the hosts and their connection information.

inventory/rpis.yaml

---
rpis:
  vars:
    user: nicholas
    ansible_user: nicholas
  hosts:
    pi00:
      ansible_host: 192.168.3.219
    pi01:
      ansible_host: 192.168.3.229
    pi02:
      ansible_host: 192.168.3.250
    pi04:
      ansible_host: 192.168.2.88

📖 Playbooks

There are several playbooks available for managing the Raspberry Pi hosts:

  • Update All Raspberry Pis: The playbooks/update_rpis.yaml playbook updates all Raspberry Pi hosts. It uses the update_apt and update_git roles.

    To run this playbook:

    ansible-playbook playbooks/update_rpis.yaml
    
  • Run a Single Task: The playbooks/task_rpis.yaml playbook runs a single task on all Raspberry Pi hosts. It uses the single role.

    To run this playbook:

    ansible-playbook playbooks/task_rpis.yaml
    

OpenMediaVault Host

I have an OpenMediaVault (OMV) instance running as a VM on my Proxmox host. It is managed as a single host.

📖 Playbooks

  • Update OMV: The playbooks/update_omv.yaml playbook updates the OMV host. It runs the omv-update command.

    To run this playbook:

    ansible-playbook playbooks/update_omv.yaml
    

    Note

    The omv-update command is a wrapper script that is the recommended way to update OpenMediaVault. It is a non-interactive script that runs apt update and apt upgrade and also handles other OMV-specific configurations and checks to ensure the system remains stable.

⬇ Homelab-Pull

📖 Playbooks

  • Remove homelab-pull: The playbooks/remove_homelab_pull.yaml playbook removes the homelab-pull service and timer from all hosts.

    To run this playbook:

    ansible-playbook playbooks/remove_homelab_pull.yaml
    

⚙ Role Configuration Variables

Variables for individual roles are stored in the roles/<role name>/defaults/main.yaml

Example

---
setup_git_email: "[email protected]"
setup_git_signing_key: "69FF3D02ABFCD01F328778D5374FA199233281E4"
setup_git_repo: "[email protected]:nicholaswilde/homelab.git"
setup_git_homelab_folder: "git/nicholaswilde/homelab"

setup_nfs_path: "storage -fstype=nfs4,rw 192.168.2.19:/storage"

# setup_proxy: "http://192.168.2.40:3142"
setup_proxy: "http://aptcache.l.nicholaswilde.io:3142"

setup_beszel_key: ""

setup_dotfiles_repo: "https://github.com/nicholaswilde/dotfiles.git"
setup_dotfiles_folder: "git/nicholaswilde/dotfiles"


setup_reprepro_base_url: "http://deb.l.nicholaswilde.io"
setup_reprepro_key_url: "http://deb.l.nicholaswilde.io/public.gpg.key"

setup_syncthing_port: '8384'
setup_syncthing_control_node_ip: "192.168.2.28"
setup_syncthing_control_node_label: "amd2"

setup_debug_enabled: false

setup_adguard_home_url: "http://adguard01.l.nicholaswilde.io"

setup_ssh_public_key_url: "https://github.com/nicholaswilde.keys"

⚙ Ansible Configuration File

The ansible.cfg file contains default settings for Ansible. Here are the key configurations used in this project:

[defaults]
inventory = ./inventory/
playbook = ./playbooks/update_all.yaml 
timeout = 25
vault_password_file = ./.vault_pass
interpreter_python = auto_silent
roles_path = ./roles/
host_key_checking = False
private_key_file = /home/nicholas/.ssh/id_ed25519

[ssh_connection]
scp_if_ssh = True

📝 Explanation of Key Settings

  • inventory: Specifies the default inventory directory.
  • playbook: Sets a default playbook to run if none is specified (e.g., update_lxc.yaml).
  • timeout: Sets the default SSH connection timeout to 25 seconds.
  • vault_password_file: Points to the .vault_pass file for Ansible Vault passwords.
  • interpreter_python: Automatically detects the Python interpreter on remote hosts.
  • roles_path: Specifies the directory where Ansible roles are located.
  • host_key_checking: Disables host key checking for SSH connections (use with caution in production environments).
  • private_key_file: Specifies the default private key file for SSH connections.
  • scp_if_ssh: Forces Ansible to use scp for file transfers over SSH, even if sftp is available. ```