This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Roles

Ansible roles

1 - Adguard

Role to deploy adguard

1.1 - Defaults

Defaults for adguard
adguard_dir: /etc/adguard
adguard_work_dir: "{{ adguard_dir }}"
adguard_gpg_key_url: https://keys.openpgp.org/vks/v1/by-fingerprint/28645AC9776EC4C00BCE2AFC0FE641E7235E2EC6
adguard_gpg_key_src: files/key.asc
adguard_gpg_key_path: "{{ adguard_dir }}/release-key.asc"
adguard_gpg_key_checksum: "22d78dbacb461667861ee7d2dd13d13240ee003d"
adguard_release_source_url: https://github.com/AdguardTeam/AdGuardHome/releases/download
adguard_release_version: v0.107.48
adguard_release_checksum: "sha512:c0a336212e3792ae6fc6b8de976befce0038c5d6ddde919a00994c43a929349958c7bd65f7fb7fdd471962afa20855ecbf5830cc7a1d26f3037a716db638c101"
adguard_release_distrib: AdGuardHome_linux_amd64.tar.gz
adguard_release_url: "{{ adguard_release_source_url }}/{{ adguard_release_version }}/{{ adguard_release_distrib }}"
adguard_release_archive_dest: "{{ adguard_dir }}/release-{{ adguard_release_version }}-{{ adguard_release_distrib }}" 
adguard_release_dest: "{{ adguard_dir }}/release-{{ adguard_release_version }}" 
adguard_release_dir: "{{ adguard_release_dest }}/AdGuardHome"
adguard_release_bin: "{{ adguard_release_dir }}/AdGuardHome"
adguard_release_sig: "{{ adguard_release_bin }}.sig"

2 - Caddy

Role to deploy caddy

2.1 - Defaults

Defaults for caddy
caddy_enabled: false
caddy_user: caddy
caddy_config_path: /etc/caddy/Caddyfile
caddy_config_dir: /etc/caddy/configs
caddy_config: |
  import {{ caddy_config_dir }}/*

3 - CIFS

Setup cifs

3.1 - Defaults

Defaults for cifs
cifs_enabled: false
cifs_systemd_dir: /etc/systemd/system
cifs_mounts: []

4 - Consul

Consul

4.1 - Defaults

Defaults for consul
consul_user: consul
consul_user_uid: 4266
consul_dir: "/opt/consul"
consul_data_dir: "{{ consul_dir }}/data"
consul_config_dir: "{{ consul_dir }}/configs"
consul_config_path: "{{ consul_config_dir }}/consul.hcl"
consul_config_local_path: "files/consul.hcl"
consul_log_dir: "{{ consul_dir }}/logs"
consul_tls_dir: "{{ consul_dir }}/tls"
consul_service_path: "/lib/systemd/system/consul.service"
consul_tls_ca_file_local_path: files/pki_ca_servers.crt
consul_tls_ca_file_path: "{{ consul_tls_dir }}/tls_ca_file.crt"
consul_tls_cert_file_path: "{{ consul_tls_dir }}/cert_file.crt"
consul_tls_key_file_path: "{{ consul_tls_dir }}/key_file.crt"
consul_tls_common_name: "{{ inventory_hostname }}"
consul_tls_ttl: "2629746" # month
consul_tls_alt_names: ""
consul_vault_local_path: "{{ role_path }}/files/vault"
consul_bin_local_path: "{{ role_path }}/files/consul"
consul_bin_path: /usr/bin/consul

5 - Consul

Setup consul

5.1 - Defaults

Defaults for consul_old
consul_is_server: false
consul_user_name: consul
consul_user_uid:
consul_dir: "/opt/consul"
consul_persistence_dir: "{{ consul_dir }}"
consul_releases_dir: "{{ consul_dir }}/releases"
consul_config_symlink_dir: "{{ consul_configs_dir }}/current"
consul_config_symlink_data_dir: "{{ consul_config_symlink_dir }}/configs"
consul_configs_dir: "{{ consul_persistence_dir }}/configs/{{ inventory_hostname }}"
consul_data_dir: "{{ consul_persistence_dir }}/data/{{ inventory_hostname }}"
consul_config_path: "{{ consul_config_config_dir }}/consul.json"
consul_config_dir: "{{ consul_configs_dir }}/{{ consul_config_dir_name }}" 
consul_config_data_dir: "{{ consul_config_dir }}/data"
consul_config_config_dir: "{{ consul_config_dir }}/configs"
consul_config_dir_name: "{{ consul_config_version }}"
consul_config_version: "{{ lookup('ansible.builtin.pipe', 'git rev-parse HEAD') }}"
consul_env: {}
consul_configs: []
consul_env_path: "{{ consul_config_data_dir }}/consul.env"
consul_env_symlink_path: "{{ consul_config_symlink_dir }}/data/consul.env"
consul_release_dir: "{{ consul_releases_dir }}/{{ consul_release_dir_name }}"
consul_release_dir_name: "{{ consul_release_version }}"
consul_release_symlink_path: "{{ consul_releases_dir }}/current"
consul_release_symlink_bin_path: "{{ consul_release_symlink_path }}/consul"
consul_release_version: 1.20.2
consul_release_archive_name: "consul_{{ consul_release_version }}_linux_amd64"
consul_release_archive_fullname: "{{ consul_release_archive_name }}.zip"
consul_release_bin_path: "{{ consul_release_dir }}/consul"
consul_release_url: "https://releases.hashicorp.com/consul/{{ consul_release_version }}/{{ consul_release_archive_fullname }}"
consul_release_checksum: "sha256:1bf7ddf332f02e6e36082b0fdf6c3e8ce12a391e7ec7dafd3237bb12766a7fd5"
consul_agent_ca_cert_path: "{{ consul_config_data_dir }}/agent_ca_cert.pub"
consul_agent_ca_cert:
consul_server_cert_path: "{{ consul_config_data_dir }}/server_cert_{{ inventory_hostname }}.pub"
consul_server_cert:
consul_server_key_path: "{{ consul_config_data_dir }}/server_cert_{{ inventory_hostname }}.key"
consul_server_key:

6 - Consul envoy

Setup envoy for consul

6.1 - Defaults

Defaults for consul_envoy
consul_envoy_dir: "/opt/consul_envoy"
consul_envoy_deployments_dir: "{{ consul_envoy_dir }}/deployments"
consul_envoy_releases_dir: "{{ consul_envoy_dir }}/releases"
consul_envoy_deployment_dir: "{{ consul_envoy_deployments_dir }}/{{ consul_envoy_deployment_version }}"
consul_envoy_release_dir: "{{ consul_envoy_releases_dir }}/{{ consul_envoy_release_version }}"
consul_envoy_configs_dir: "{{ consul_envoy_deployment_dir }}/configs"
consul_envoy_bootstrap_config_path: "{{ consul_envoy_configs_dir }}/bootstrap.json"
consul_envoy_deployment_version: "{{ lookup('ansible.builtin.pipe', 'git rev-parse HEAD') }}"
consul_envoy_user_name: consul_envoy
consul_envoy_release_version: 1.31.0
consul_envoy_release_archive_fullname: "envoy_{{ consul_envoy_release_version }}_linux_amd64.zip"
consul_envoy_release_url: "https://releases.hashicorp.com/envoy/{{ consul_envoy_release_version }}/{{ consul_envoy_release_archive_fullname }}"
consul_envoy_release_bin_path: "{{ consul_envoy_release_dir }}/envoy"
consul_envoy_release_symlink_path: "{{ consul_envoy_releases_dir }}/current"
consul_envoy_release_checksum: "sha256:350782275de0fe50699844b4f9096d77a0193a5bcf205e14b8ad964ec429d45e"

7 - Dev VM

Set up repositories and packages for a development VM

The default task entry point configures package repositories and development packages. It uses the same dev_vm_packages list with either the ordinary package manager or RPM-OSTree layering. dev_vm_package_backend accepts auto, package, or rpm_ostree; auto selects RPM-OSTree when /run/ostree-booted exists. Set dev_vm_install_packages to false to skip repository and package management entirely.

RPM-OSTree changes are staged for the next boot by default. The role reports a pending deployment but does not reboot the host. Set dev_vm_rpm_ostree_apply_live to true only when live application is known to be safe for the selected packages.

The packaged Fedora mirror and Bazelisk tool currently make this role specific to Linux x86-64 development hosts.

The packages task entry point exposes package management separately from other role tasks. The bazel task entry point installs a caller-provided Bazel rc, Bazelisk, and the repository’s bazel_agent runner for one developer account.

Import the Bazel entry point with tasks_from: bazel and set:

  • dev_vm_bazel_user: owner of the installed files.
  • dev_vm_bazel_group: group of the installed files; defaults to the user.
  • dev_vm_bazel_home: absolute home directory for the developer account.
  • dev_vm_bazelrc_src: controller-side Bazel rc source; defaults to bazelrc.
  • dev_vm_bazelisk_src: controller-side Bazelisk source; defaults to bazelisk.
  • dev_vm_bazel_agent_src: controller-side bazel_agent source; defaults to bazel_agent.

The role packages its default Linux x86-64 Bazelisk and bazel_agent sources. The calling playbook must package the Bazel rc, plus any overridden binary sources, in an Ansible file search path. Cache storage and machine-specific Bazel settings remain the caller’s responsibility.

The optional android task entry point installs the caller-packaged repository Android installer as dev_vm_bazel_user. Supply dev_vm_android_installer_src and dev_vm_android_packages_src from //tools/android. It installs missing packages, accepts their licenses as part of the requested deployment, verifies each package receipt, and points ndk/current at the selected NDK. It requires the role’s Java package and does not invoke Bazel on the destination host.

7.1 - Defaults

Defaults for dev_vm
---
dev_vm_install_packages: true
dev_vm_package_backend: auto
dev_vm_rpm_ostree_apply_live: false
dev_vm_packages:
  - acl
  - clang
  - clang-tools-extra
  - java-latest-openjdk-devel
  - rust
  - cargo
  - mesa-libGL-devel
  - go
  - Xvfb
  - python3
  - gmp-devel
  - patch
  - podman
  - git
  - git-lfs
  - fuse
  - fuse-libs
  - pcsc-lite
  - pcsc-lite-devel
  - openssl
  - gnutls-utils
  - opensc
  - openssl-pkcs11
  - libdnet
  - qemu-kvm
  - qemu-img
dev_vm_bazel_user: "{{ undefined() }}"
dev_vm_bazel_group: "{{ dev_vm_bazel_user }}"
dev_vm_bazel_home: "{{ undefined() }}"
dev_vm_bazelrc_src: bazelrc
dev_vm_bazelisk_src: bazelisk
dev_vm_bazel_agent_src: bazel_agent
dev_vm_selinux_android_emulation: true
dev_vm_android_sdk_root: "{{ dev_vm_bazel_home }}/Android/Sdk"
dev_vm_android_installer_dir: "{{ dev_vm_bazel_home }}/.local/lib/android-installer"
dev_vm_android_installer_src: "{{ undefined() }}"
dev_vm_android_packages_src: "{{ undefined() }}"
dev_vm_android_install_timeout: 1800

8 - DNS

Setup dns records

8.1 - Defaults

Defaults for dns
dns_records: []
dns_domains: []
dns_njalla_api_url: https://njal.la/api/1/
dns_njalla_token:

9 - Docker

Setup docker

9.1 - Defaults

Defaults for docker
docker_enabled: false

10 - Download host

Btrfs storage, SSH publication, and native static hosting

alwaldend.main.download_host configures download hosts using the existing host, nginx, and traefik roles. It checks that the selected content device is a block device before configuring the host, creates Btrfs without forcing replacement of an existing filesystem, and mounts by UUID. This role is intended for the Fedora hosts used by infra/download.

The role creates only the publication roots: projects/, sites/, and private staging/, owned by the download publisher account. Publishers create individual projects, sites, and releases. The mount root and service state remain root-owned; privileged ownership changes never traverse per-site directories. Shared Nginx and Traefik roles own their service units. Traefik uses its standard system-disk location. A daily systemd timer runs incremental duperemove over projects and sites with resource limits and private hash state. Uploading files, extracting sites, and switching the current-release symlink are separate publisher operations. Administrators authenticate with existing SSH access and run publication as this account through sudo. The role does not manage its authorized_keys and requires no publisher key input. It installs rsync for SSH transfers.

On SELinux-enabled hosts, the role persists httpd_sys_content_t labels for static content and applies them with restorecon. This permits confined Nginx to read the custom document root; ordinary Unix permissions alone do not grant that access. See Red Hat’s Nginx configuration guide.

Filesystem creation, UUID-based mounting, directories, SELinux labels, and daily maintenance live in tasks/filesystem.yaml. The pinned community.general.btrfs_info module discovers filesystem UUIDs; selection uses the canonical device path so both direct device paths and by-id symlinks work. Attachment checks remain in tasks/main.yaml before base-host setup.

  • Select target hosts through inventory or deployment wrapper limits and enable privilege escalation. The role does not restrict play size.
  • Set force_handlers: true on the play so completed configuration changes run their notified handlers even if a later task fails.
  • Supply download_content_device. Administrator SSH and sudo access come from the shared host roles; no dedicated upload key is configured.
  • Override download_root and download_publisher as needed. The content mount must be under root-owned, non-publisher-writable parent directories.
  • Keep Traefik configuration and data on the system disk (the shared role defaults to /opt/traefik) so content-disk failure does not stop the proxy.
  • Supply the shared roles’ configuration, including nginx_config_template, Traefik static/dynamic templates, ACME settings, and base-host variables. The caller owns inventory, firewall policy, Vault references, and routing.
- name: Configure the selected download VM
  hosts: download
  become: true
  force_handlers: true
  roles:
    - alwaldend.main.download_host

The collection packages the role automatically through its existing role aggregation. Domain-specific serving templates stay with the deployment.

Default configuration

10.1 - Defaults

Defaults for download_host
# Persistent Btrfs content mount and its non-sudo content owner.
download_root: /srv/download
download_publisher: download
# Required: the content block device. Administrators publish via sudo.
download_content_device: "{{ undefined() }}"

11 - Facts

Setup ansible facts

11.1 - Defaults

Defaults for facts
facts_debug: false

12 - Filesystem

Setup filesystems

12.1 - Defaults

Defaults for filesystem
filesystem_setup: []

13 - Firewall

Setup firewall

13.1 - Defaults

Defaults for firewall
firewall_state: enabled
firewall_enabled: true
firewall_allow_port_from: []
firewall_allow_forward: []

14 - Forgejo

Forgejo

14.1 - Defaults

Defaults for forgejo
forgejo_user: forgejo
forgejo_user_uid: 5157
forgejo_dir: "/opt/forgejo"
forgejo_data_dir: "{{ forgejo_dir }}/data"
forgejo_config_dir: "{{ forgejo_dir }}/configs"
forgejo_work_dir: "{{ forgejo_dir }}/work"
forgejo_config_path: "{{ forgejo_config_dir }}/forgejo.ini"
forgejo_config_local_path: "files/forgejo.ini"
forgejo_disk:
forgejo_log_dir: "{{ forgejo_dir }}/logs"
forgejo_service_path: "/lib/systemd/system/forgejo.service"
forgejo_vault_local_path: "{{ role_path }}/files/vault"
forgejo_bin_local_path: "{{ role_path }}/files/forgejo"
forgejo_bin_path: /usr/bin/forgejo

15 - Forgejo

Forgejo

15.1 - Defaults

Defaults for threexui
threexui_user: threexui
threexui_user_uid: 6468
threexui_dir: "/opt/threexui"
threexui_data_dir: "{{ threexui_dir }}/data"
threexui_work_dir: "{{ threexui_dir }}/work"
threexui_disk:
threexui_log_dir: "{{ threexui_dir }}/logs"
threexui_service_path: "/lib/systemd/system/threexui.service"
threexui_vault_local_path: "{{ role_path }}/files/vault"
threexui_bin_dir: "{{ threexui_dir }}/bin"
threexui_bin_local_dir: "{{ role_path }}/files/threexui_bin"
threexui_config_dir: "{{ threexui_dir }}/configs"
threexui_env_path: "{{ threexui_config_dir }}/threexui.env"
threexui_env_override: {}
threexui_base_path: /
threexui_username: "{{ undefined() }}"
threexui_password: "{{ undefined() }}"
threexui_port: "2053"
threexui_debug: "false"
threexui_env:
  # https://docs.sanaei.dev/docs/reference/env-vars/
  XUI_DB_TYPE: sqlite
  XUI_DB_FOLDER: "{{ threexui_data_dir }}"
  XUI_PORT: "{{ threexui_port }}"
  XUI_INIT_WEB_BASE_PATH: "{{ threexui_base_path }}"
  XUI_ENABLE_FAIL2BAN: "true"
  XUI_SKIP_HSTS: "true"
  XUI_LOG_LEVEL: info
  XUI_DEBUG: "{{ threexui_debug }}"
  XUI_LOG_FOLDER: "{{ threexui_log_dir }}"
  XUI_BIN_FOLDER: "{{ threexui_bin_dir }}/bin"
  XRAY_VMESS_AEAD_FORCED: "false"
  XUI_ACCESS_URL: "https://{{ inventory_hostname }}{{ threexui_base_path }}"
  XUI_USERNAME: "{{ threexui_username }}"
  XUI_PASSWORD: "{{ threexui_password }}"

16 - Forgejo runner

Install and register a Forgejo Actions runner

Installs the repository-pinned Linux x86-64 Forgejo runner, creates its service account, registers it with Forgejo, and manages its systemd service. The binary and configuration are root-owned. The home and work directories belong to the runner account. Registration and the daemon use the same explicit configuration and registration file, so repeated deployments reuse the existing runner.

Supply forgejo_runner_instance and forgejo_runner_token for initial registration. forgejo_runner_name defaults to the inventory hostname. Inject the token from Vault; the role passes it through standard input with logging suppressed. The registration file is mode 0600. Changing labels or capacity updates the daemon configuration; changing a registered runner’s instance or identity requires deliberate re-registration.

Set forgejo_runner_install_packages: false when the calling deployment supplies all runner packages, including on RPM-OSTree hosts. Node.js is required for JavaScript actions such as checkout. The acl package must be available for Ansible to execute registration as the unprivileged runner account; both roles include it in their package defaults. Complete any required reboot before starting this role. Container runtime configuration remains the caller’s responsibility. The default installation and service paths are writable on OSTree hosts.

Host executor labels run jobs as the runner account without job isolation. Restrict registration to trusted repositories and keep deployment credentials out of the runner environment. The role does not grant sudo access. The Actions cache service is disabled by default; enable it only after configuring its network reachability. Configuration, binary, and service changes trigger a handler to reload systemd and restart the runner.

16.1 - Defaults

Defaults for forgejo_runner
---
forgejo_runner_user: forgejo-runner
forgejo_runner_user_uid: 5721
forgejo_runner_dir: /opt/forgejo-runner
forgejo_runner_home_dir: "{{ forgejo_runner_dir }}/home"
forgejo_runner_work_dir: "{{ forgejo_runner_dir }}/work"
forgejo_runner_bin_local_path: "{{ role_path }}/files/forgejo-runner"
forgejo_runner_bin_path: "{{ forgejo_runner_dir }}/forgejo-runner"
forgejo_runner_service_path: /etc/systemd/system/forgejo-runner.service
forgejo_runner_config_path: "{{ forgejo_runner_dir }}/config.yaml"
forgejo_runner_registration_path: "{{ forgejo_runner_work_dir }}/.runner"
forgejo_runner_install_packages: true
forgejo_runner_packages:
  - acl
  - git
  - nodejs
  - podman
forgejo_runner_instance: "{{ undefined() }}"
forgejo_runner_token: "{{ undefined() }}"
forgejo_runner_name: "{{ inventory_hostname }}"
forgejo_runner_labels:
  - "self-hosted:host"
forgejo_runner_capacity: 1
forgejo_runner_timeout: 3h
forgejo_runner_shutdown_timeout: 30s
forgejo_runner_service_stop_timeout: 60s
forgejo_runner_registration_timeout: 120
forgejo_runner_cache_enabled: false

17 - Gpg

Setup GPG

17.1 - Defaults

Defaults for gpg
gpg_users: []

18 - Harboer

Harbor

18.1 - Defaults

Defaults for harbor
harbor_user: harbor
harbor_user_uid: 7049
harbor_dir: "/opt/harbor"
harbor_installer_dir: "{{ harbor_dir }}/installer"
harbor_logs_dir: "{{ harbor_dir }}/logs"
harbor_data_dir: "{{ harbor_dir }}/data"
harbor_installer_local_dir: "{{ role_path }}/files/harbor_installer"
harbor_config_path: "{{ harbor_installer_dir }}/harbor.yml"
harbor_config_local_path: "files/harbor.yaml"
harbor_domain: "{{ undefined() }}"
harbor_ca_local_path: "{{ role_path }}/files/pki_ca_servers.crt"
harbor_ca_path: /etc/docker/certs.d/{{ harbor_domain }}/ca.crt
harbor_root_ca_local_path: "{{ role_path }}/files/pki_ca_root.crt"

19 - Hermes

Install and run the Hermes Agent gateway

19.1 - Defaults

Defaults for hermes
hermes_user: hermes
hermes_user_uid: 8150
hermes_dir: /opt/hermes
hermes_home_dir: "{{ hermes_dir }}/home"
hermes_install_dir: "{{ hermes_dir }}/hermes-agent"
hermes_disk:
hermes_port: 8642
hermes_commit: "{{ undefined() }}"
hermes_installer_local_path: "{{ role_path }}/files/install.sh"
hermes_installer_path: "{{ hermes_dir }}/install.sh"
hermes_config_local_path: "files/hermes.yaml"
hermes_config_path: "{{ hermes_home_dir }}/.hermes/config.yaml"
hermes_env_path: "{{ hermes_home_dir }}/.hermes/.env"
hermes_env: {}
hermes_bin_path: "{{ hermes_home_dir }}/.local/bin/hermes"
hermes_dashboard_service_path: /lib/systemd/system/hermes-dashboard.service

20 - Hiddify manager

Setup hiddify manager

20.1 - Defaults

Defaults for hiddify_manager
hiddify_manager_env:
  HIDDIFY_MANAGER_IMAGE: ghcr.io/hiddify/hiddify-manager:main@sha256:d5edef06d2133ffa26f0fcbb67e711f4356dba44c44a41a96570c4e03845a84c
  HIDDIFY_MANAGER_REDIS_IMAGE: redis:7.4.1@sha256:bb142a9c18ac18a16713c1491d779697b4e107c22a97266616099d288237ef47
  HIDDIFY_MANAGER_MARIADB_IMAGE: mariadb:11.6.2@sha256:a9547599cd87d7242435aea6fda22a9d83e2c06d16c658ef70d2868b3d3f6a80
  HIDDIFY_MANAGER_REDIS_PASSWORD: "{{ lookup('ansible.builtin.env', 'HIDDIFY_MANAGER_REDIS_PASSWORD', default=undef()) }}"
  HIDDIFY_MANAGER_MYSQL_PASSWORD: "{{ lookup('ansible.builtin.env', 'HIDDIFY_MANAGER_MYSQL_PASSWORD', default=undef()) }}"
  HIDDIFY_MANAGER_MARIADB_PASSWORD: "{{ lookup('ansible.builtin.env', 'HIDDIFY_MANAGER_MARIADB_PASSWORD', default=undef()) }}"
hiddify_manager_env_override: {}
hiddify_manager_dir: /opt/hiddify_manager

21 - Hiddify manager with host

Setup host and then hiddify manager

21.1 - Defaults

Defaults for hiddify_manager_host

22 - Host

Common host setup

22.1 - Defaults

Defaults for host

23 - Install CA

Install a certificate authority

23.1 - Defaults

Defaults for install_ca
install_ca:
  - path: "{{ role_path }}/files/pki_ca_root.crt"
  - path: "{{ role_path }}/files/pki_ca_servers.crt"

24 - K3s

Setup k3s

24.1 - Defaults

Defaults for k3s
k3s_data_dir: /srv/k3s_data
k3s_service_name: k3s
k3s_data_disk: /dev/sdb
k3s_storage_dir: /srv/k3s_storage
k3s_storage_disk: /dev/sdc
k3s_config_path: "/etc/rancher/k3s/config.yaml"
k3s_config_local_path: "files/k3s.yaml"
k3s_admission_config_path: "/etc/rancher/k3s/admission.yaml"
k3s_admission_config_local_path: "files/k3s_admission.yaml"
k3s_auth_config_path: "{{ [(k3s_config_path | dirname), 'k3s_auth.yaml'] | path_join }}"
k3s_auth_config_local_path: "files/k3s_auth.yaml"
k3s_manifests_local_path: "files/k3s_manifests.yaml"
k3s_manifests_path: "{{ k3s_data_dir }}/server/manifests/k3s-manifests.yaml"
k3s_bin_local_path: "{{ role_path }}/files/k3s"
k3s_bin_path: /usr/local/bin/k3s
k3s_install_script_local_path: "{{ role_path }}/files/k3s_install"
k3s_install_script_path: /usr/local/bin/k3s_install

25 - K3s bootstrap

Bootstrap k3s cluster

The role declares Python and uv as host packages, creates its Kubernetes virtual environment with uv venv, and installs the Python client with uv pip install. The environment is created only when its interpreter is missing; package installs report changes only when uv installs or removes distributions. These operations run during an explicitly authorized role deployment and require package-index access. Repository validation builds and renders the role without deploying it.

25.1 - Defaults

Defaults for k3s_bootstrap
k3s_bootstrap_apply_path:
k3s_bootstrap_token_name: "{{ lookup('ansible.builtin.env', 'USER', default=undef()) }}"
k3s_bootstrap_token_namespace: kube-system
k3s_bootstrap_venv_dir: "/opt/bootstrap-venv"
k3s_bootstrap_venv_bin: "{{ k3s_bootstrap_venv_dir | mandatory }}/bin/python"
k3s_bootstrap_kubeconfig: /etc/rancher/k3s/k3s.yaml
k3s_bootstrap_cluster_name: "{{ inventory_hostname }}"
k3s_bootstrap_cluster_ca_path: "{{ lookup('ansible.builtin.env', 'HOME', default=undef()) }}/.kube/ca-{{ k3s_bootstrap_cluster_name | mandatory }}.ca.crt"
k3s_bootstrap_user_ca_path: "{{ k3s_bootstrap_cluster_ca_path | mandatory | dirname }}/ca-{{ k3s_bootstrap_cluster_name | mandatory }}-{{ k3s_bootstrap_token_name | mandatory }}.ca.crt"
k3s_bootstrap_user_name: "{{ k3s_bootstrap_cluster_name | mandatory }}-{{ k3s_bootstrap_token_name | mandatory }}"
k3s_bootstrap_cluster_port:

26 - K3s cluster

Setup k3s cluster

26.1 - Defaults

Defaults for k3s_cluster

27 - K3s kubeconfig

Write k3s kubeconfig to a secret

27.1 - Defaults

Defaults for k3s_kubeconfig
k3s_kubeconfig_secret_path: "{{ undefined() }}"
k3s_kubeconfig_secret_mount: "{{ undefined() }}"
k3s_kubeconfig_path: /etc/rancher/k3s/k3s.yaml
k3s_kubeconfig_vault_bin_local_path: "{{ role_path }}/files/vault"

28 - lvg

Setup LVM volume groups

lvg_volume_groups describes the complete desired physical-volume list for each volume group. Extra physical volumes are not removed unless a caller sets remove_extra_pvs: true.

Creating a volume group initializes every listed device as an LVM physical volume. Callers must resolve stable device paths and verify that each device is the intended, unused disk before applying this role; pvcreate may overwrite an existing disk signature.

28.1 - Defaults

Defaults for lvg
lvg_volume_groups: []

29 - lvol

Setup LVM logical volumes

Each lvol_volumes entry can constrain allocation with pvs, pass creation arguments with opts, and disable shrinking with shrink: false.

29.1 - Defaults

Defaults for lvol
lvol_volumes: []

30 - Mount

Setup posix mounts

Mount entries can set opts, dump, and passno; state: mounted keeps the mount active and writes its persistent /etc/fstab entry.

30.1 - Defaults

Defaults for mount
mount_points: []

31 - Nginx

Native Nginx with validated consumer configuration

Installs Fedora’s nginx package and manages its native systemd service. The consumer supplies a complete nginx_config_template, including listeners, HTTP settings, MIME includes, and document roots. Replacing the main config also removes package-default virtual hosts from the active configuration.

Ansible renders and validates the entire candidate with nginx -t -c before atomically replacing nginx_config_path. Invalid candidates leave the prior configuration and running service intact. Valid changes notify a reload; an unchanged run does not restart or reload Nginx. The role never manages published content. Packages can be caller-managed with nginx_manage_packages: false.

The download component owns its routes, storage permissions, SELinux labels, and the Traefik TLS frontend. The role validation record links the consumer fixture and lifecycle results.

31.1 - Defaults

Defaults for nginx
# Complete configuration template owned by the consumer.
nginx_config_template: "{{ undefined() }}"
nginx_config_path: /etc/nginx/nginx.conf
nginx_binary: /usr/sbin/nginx
nginx_manage_packages: true
nginx_packages:
  - nginx
nginx_service_name: nginx

32 - Opencode

Opencode

32.1 - Defaults

Defaults for opencode
opencode_user: opencode
opencode_user_uid: 8149
opencode_dir: "/opt/opencode"
opencode_port: 4096
opencode_domain: "{{ undefined() }}"
opencode_log_dir: "{{ opencode_dir }}/logs"
opencode_work_dir: "{{ opencode_dir }}/work"
opencode_home_dir: "{{ opencode_dir }}/home"
opencode_config_dir: "{{ opencode_home_dir }}/.config/opencode"
opencode_config_path: "{{ opencode_config_dir }}/opencode.json"
opencode_config_local_path: "files/opencode.json"
opencode_disk:
opencode_service_path: "/lib/systemd/system/opencode.service"
opencode_bin_local_path: "{{ role_path }}/files/opencode"
opencode_bin_path: /usr/bin/opencode
opencode_env_path: "{{ opencode_config_dir }}/opencode.env"
opencode_env_override: {}
opencode_ssh_public_key: "{{ undefined() }}"
opencode_ssh_private_key: "{{ undefined() }}"
opencode_server_username: "{{ undefined() }}"
opencode_server_password: "{{ undefined() }}"
opencode_env:
  OPENCODE_SERVER_USERNAME: "{{ opencode_server_username }}"
  OPENCODE_SERVER_PASSWORD: "{{ opencode_server_password }}"

33 - OpenHands automation

OpenHands automation server

Runs the OpenHands automation server natively from a uv-managed virtual environment. It stores schedules and event triggers, tracks run lifecycle, and dispatches conversations to the agent server.

The automation server is not a sandbox. It records which agent server should run a dispatched conversation and hands that work to AUTOMATION_AGENT_SERVER_URL; setting that variable selects the agent server’s local mode, which uses a persistent local agent server instead of managed OpenHands Cloud sandboxes.

openhands_automation_agent_server_api_key must equal the agent server’s session API key because both services authenticate the same X-Session-API-Key value. openhands_automation_local_api_key is the key browsers present to this service.

The service stores state in SQLite via AUTOMATION_DB_URL by default, which local mode supports without a separate database role. Set openhands_automation_database_url to a PostgreSQL URL to use one instead. openhands_automation_kv_secret enables the automation key-value store and is required for automations that persist state between runs.

openhands_automation_base_url is the externally reachable origin the service appends /api/automation to when it builds callback URLs, so it must be the origin the browser and the dispatched agent server side both use.

33.1 - Defaults

Defaults for openhands_automation
openhands_automation_user: openhands
openhands_automation_user_uid: 8154
openhands_automation_dir: /opt/openhands-automation
openhands_automation_home_dir: "{{ openhands_automation_dir }}/home"
openhands_automation_work_dir: "{{ openhands_automation_dir }}/work"
openhands_automation_state_dir: "{{ openhands_automation_dir }}/state"
openhands_automation_disk:
openhands_automation_packages:
  - python3.12
  - uv
openhands_automation_host: 127.0.0.1
openhands_automation_port: 18001
openhands_automation_version: "{{ undefined() }}"
openhands_automation_agent_server_version: "{{ undefined() }}"
openhands_automation_agent_server_url: "{{ undefined() }}"
openhands_automation_sandbox_agent_server_url: ""
openhands_automation_agent_server_api_key: "{{ undefined() }}"
openhands_automation_base_url: "{{ undefined() }}"
openhands_automation_local_api_key: "{{ undefined() }}"
openhands_automation_kv_secret: "{{ undefined() }}"
openhands_automation_database_url: "sqlite+aiosqlite:///{{ openhands_automation_state_dir }}/automations.db"
openhands_automation_workspace_base: "{{ openhands_automation_work_dir }}/automations"
openhands_automation_venv_dir: "{{ openhands_automation_dir }}/venv"
openhands_automation_service_path: /lib/systemd/system/openhands-automation.service
openhands_automation_env_path: "{{ openhands_automation_dir }}/.env"
openhands_automation_env:
  PYTHONUTF8: "1"
  OPENHANDS_REMOTE_WS_READY_REQUIRED: "false"
  OPENHANDS_SUPPRESS_BANNER: "1"
openhands_automation_env_override: {}

34 - OpenHands canvas

OpenHands Agent Canvas frontend

Runs the OpenHands Agent Canvas browser client from the published npm package. The service listens on loopback port openhands_canvas_port; TLS, the public hostname, and routing to the agent server and automation API belong to the deployment’s Traefik role.

The service runs the package’s scripts/static-server.mjs directly with Node, an explicit loopback host, and the package’s build/ directory. It serves the browser client without starting an agent server, an automation server, or the npm launcher’s additional ingress process. Agent Canvas owns no conversation or automation state and executes no tools.

The browser starts with no configured backend. The operator adds one with the canvas origin as the host and the agent server’s session key as the credential. The static server receives no session key and injects none into served assets. This configuration does not advertise an embedded VSCode editor.

The npm prefix is installed inside openhands_canvas_dir. openhands_canvas_package_dir identifies the installed package containing the static server and browser assets.

34.1 - Defaults

Defaults for openhands_canvas
openhands_canvas_user: openhands
openhands_canvas_user_uid: 8153
openhands_canvas_dir: /opt/openhands-canvas
openhands_canvas_home_dir: "{{ openhands_canvas_dir }}/home"
openhands_canvas_work_dir: "{{ openhands_canvas_dir }}/work"
openhands_canvas_state_dir: "{{ openhands_canvas_dir }}/state"
openhands_canvas_package_dir: "{{ openhands_canvas_dir }}/lib/node_modules/@openhands/agent-canvas"
openhands_canvas_disk:
openhands_canvas_packages:
  - nodejs24
  - nodejs24-npm
openhands_canvas_host: 127.0.0.1
openhands_canvas_port: 8000
openhands_canvas_version: "{{ undefined() }}"
openhands_canvas_service_path: /lib/systemd/system/openhands-canvas.service
openhands_canvas_env_path: "{{ openhands_canvas_dir }}/.env"
openhands_canvas_env:
  NODE_ENV: production
openhands_canvas_env_override: {}

35 - Openhands server

OpenHands agent server

Runs the OpenHands agent server natively from a uv-managed virtual environment. It hosts conversations, executes tools, and streams events for Agent Canvas. The service binds loopback only; TLS and external reachability belong to the calling deployment.

Set openhands_server_secure to true to require a session API key. The unsecured mode omits the session key and is only appropriate where the loopback boundary is trusted.

The caller can set openhands_server_install_packages to false and install openhands_server_packages through its host-specific package workflow. OSTree hosts use the shared dev_vm package entry point and must boot into a deployment containing those dependencies before installing the service. They also set openhands_server_service_path under /etc/systemd/system.

35.1 - Defaults

Defaults for openhands_server
openhands_server_user: openhands
openhands_server_user_uid: 8152
openhands_server_dir: /opt/openhands-server
openhands_server_home_dir: "{{ openhands_server_dir }}/home"
openhands_server_work_dir: "{{ openhands_server_dir }}/work"
openhands_server_state_dir: "{{ openhands_server_dir }}/state"
openhands_server_conversations_dir: "{{ openhands_server_state_dir }}/conversations"
openhands_server_persistence_dir: "{{ openhands_server_state_dir }}/persistence"
openhands_server_workspace_dir: "{{ openhands_server_work_dir }}/project"
openhands_server_bash_events_dir: "{{ openhands_server_state_dir }}/bash_events"
openhands_server_tools_dir: "{{ openhands_server_dir }}/tools"
openhands_server_disk:
openhands_server_secure: true
openhands_server_install_packages: true
openhands_server_packages:
  - python3.12
  - uv
openhands_server_host: 127.0.0.1
openhands_server_port: 18000
openhands_server_version: "{{ undefined() }}"
openhands_server_session_api_key: "{{ undefined() }}"
openhands_server_secret_key: "{{ undefined() }}"
openhands_server_import_modules: canvas_ui_tool
openhands_server_vscode_port: 8001
openhands_server_vscode_base_path: /vscode
openhands_server_venv_dir: "{{ openhands_server_dir }}/venv"
openhands_server_venv_python: "{{ openhands_server_venv_dir }}/bin/python"
openhands_server_service_path: /lib/systemd/system/openhands-server.service
openhands_server_env_path: "{{ openhands_server_dir }}/.env"
openhands_server_env:
  LOG_JSON: "true"
  PYTHONUTF8: "1"
  OPENHANDS_REMOTE_WS_READY_REQUIRED: "false"
  OH_PERSISTENCE_DIR: "{{ openhands_server_persistence_dir }}"
  OH_CONVERSATIONS_PATH: "{{ openhands_server_conversations_dir }}"
  OH_WORKSPACE_PATH: "{{ openhands_server_workspace_dir }}"
  OH_BASH_EVENTS_DIR: "{{ openhands_server_bash_events_dir }}"
  OH_SECRET_KEY: "{{ openhands_server_secret_key }}"

36 - OS

Common os setup

36.1 - Defaults

Defaults for os
os_hardening_enabled: true

37 - Posgresql

Setup Postgresql

37.1 - Defaults

Defaults for postgresql
postgresql_packages:
  - postgresql-server
  - postgresql-contrib
postgresql_service: postgresql

38 - Pve

Proxmox Virtual Environment deploy

38.1 - Defaults

Defaults for pve
pve_repo_keyring_path: /usr/share/keyrings/proxmox-archive-keyring.gpg
pve_repo_path: /etc/apt/sources.list.d/pve-install-repo.sources
pve_repo_keyring_url: https://enterprise.proxmox.com/debian/proxmox-archive-keyring-trixie.gpg
pve_repo_keyring_checksum: "sha256:136673be77aba35dcce385b28737689ad64fd785a797e57897589aed08db6e45"
pve_snippets_dir: /var/lib/vz/snippets
pve_snippets: []
pve_repo_content: |
  Types: deb
  URIs: http://download.proxmox.com/debian/pve
  Suites: trixie
  Components: pve-no-subscription
  Signed-By: {{ pve_repo_keyring_path }}

39 - PVE cluster

Setup PVE cluster

39.1 - Defaults

Defaults for pve_cluster
pve_cluster_remove_repos:
  - pve-enterprise.list 
  - ceph.list
pve_cluster_add_repos:
  - name: pve-no-subscription
    value: deb http://download.proxmox.com/debian/pve bookworm pve-no-subscription
  - name: ceph-squid
    value: deb http://download.proxmox.com/debian/ceph-squid bookworm no-subscription
pve_cluster_node_config: []
pve_cluster_admins: []
pve_cluster_cluster_name:
pve_cluster_acme_accounts: []
pve_cluster_acme_plugins: []
pve_cluster_venv_dir: /opt/ansible/pve_cluster_venv
pve_cluster_venv_packages: 
  - pexpect
pve_cluster_base_image_dir: /var/lib/vz/template
pve_cluster_node_name: "{{ inventory_hostname | mandatory | split('.') | first }}"
pve_cluster_ceph_enabled: false
pve_cluster_ceph_repository: no-subscription
pve_cluster_ceph_osd: []
pve_cluster_ceph_pools: []
pve_cluster_ceph_mon: []
pve_cluster_ceph_mgr: []
pve_cluster_ceph_mds: []
pve_cluster_storages: []
pve_cluster_storage_config_path: /etc/pve/storage.cfg

40 - PVE VM

Setup PVE VM

40.1 - Defaults

Defaults for pve_vm
pve_vm_agent: 
pve_vm_name: "{{ inventory_hostname }}"
pve_vm_delegate_update_known_hosts: localhost
pve_vm_delegate_pve: 
pve_vm_set: "{{ pve_vm_set_default + pve_vm_set_additional }}"
pve_vm_set_default: []
pve_vm_set_additional: []
pve_vm_set_creation: "{{ pve_vm_set_creation_default + pve_vm_set_creation_additional }}"
pve_vm_set_creation_default: []
pve_vm_set_creation_additional: []
pve_vm_resize: []
pve_vm_venv_dir: "/opt/ansible/pve_vm_venv"
pve_vm_init_user:
pve_vm_venv_packages:
  - proxmoxer
  - requests

41 - PVE VM remove

Remove PVE VM

41.1 - Defaults

Defaults for pve_vm_remove

42 - Raspberry pi

Setup raspberry pi

42.1 - Defaults

Defaults for raspberry_pi
raspberry_pi_argon_enabled: false
raspberry_pi_containers_enabled: false
raspberry_pi_argon_eeprom_script_path: /usr/local/bin/argon_argon_eeprom.sh
raspberry_pi_argon_argon1_script_path: /usr/local/bin/argon_argon1.sh
raspberry_pi_cmdline_path: /boot/firmware/cmdline.txt

43 - SSH

Setup ssh

43.1 - Defaults

Defaults for ssh
ssh_hardening_enabled: true
ssh_vault_mount: ssh/servers
ssh_vault_role: "{{ undefined }}"
ssh_sign_ignore_errors: false
ssh_valid_principals: []

44 - SSH port forwarding

Setup SSH port forwarding

44.1 - Defaults

Defaults for ssh_port_forward
ssh_port_forward_local_addr: 127.0.0.1
ssh_port_forward_script_path: /usr/local/bin/ssh-port-forward

45 - SSH update known hosts

Update known hosts

45.1 - Defaults

Defaults for ssh_update_known_hosts
ssh_update_known_hosts_hosts: []

46 - T3 Code

Run the T3 Code server as a systemd service

46.1 - Defaults

Defaults for t3code
t3code_user: t3code
t3code_user_uid: 8151
t3code_dir: /opt/t3code
t3code_home_dir: "{{ t3code_dir }}/home"
t3code_work_dir: "{{ t3code_dir }}/work"
t3code_disk:
t3code_port: 5120
t3code_version: "{{ undefined() }}"
t3code_state_dir: "{{ t3code_dir }}/state"
t3code_auth_token: "{{ undefined() }}"
t3code_service_path: /lib/systemd/system/t3code.service
t3code_env_path: "{{ t3code_dir }}/.env"
t3code_env: {}

47 - Traefik

Traefik

47.1 - Defaults

Defaults for traefik
traefik_user: traefik
traefik_no_user: false
traefik_owner: root
traefik_user_uid: 3578
traefik_dir: "/opt/traefik"
traefik_log_dir: "{{ traefik_dir }}/logs"
traefik_data_dir: "{{ traefik_dir }}/data"
traefik_tls_dir: "{{ traefik_dir }}/tls"
traefik_disk:
traefik_mtls_ca_local_path: "{{ role_path }}/files/traefik_mtls_ca.crt"
traefik_mtls_ca_path: "{{ traefik_tls_dir }}/mtls_ca.crt"
traefik_acme_path: "{{ undefined }}"
traefik_config_dir: "{{ traefik_dir }}/configs"
traefik_config_path: "{{ traefik_config_dir }}/traefik.toml"
traefik_disable_eab: false
traefik_config_local_path: "files/traefik.toml"
traefik_dynamic_config_path: "{{ traefik_config_dir }}/traefik_dynamic.toml"
traefik_dynamic_config_local_path: "files/traefik_dynamic.toml"
traefik_service_path: "/lib/systemd/system/traefik.service"
traefik_service_scope: system
traefik_bin_local_path: "{{ role_path }}/files/traefik"
traefik_bin_path: /usr/bin/traefik
traefik_vault_bin_local_path: "{{ role_path }}/files/vault"

48 - Traefik (old)

Old traefik role

48.1 - Defaults

Defaults for traefik_old
traefik_user: traefik
traefik_user_uid: 3653
traefik_env: {}
traefik_data_dir: /mnt/traefik
traefik_log_dir: "/var/log/traefik"
traefik_certificates_dir: "{{ traefik_data_dir }}/certificates"
traefik_configs_dir: "{{ traefik_data_dir }}/configs"
traefik_configs_version: "{{ lookup('ansible.builtin.pipe', 'git rev-parse HEAD') }}"
traefik_configs_dynamic_dir: "{{ traefik_configs_cur_dir }}/dynamic"
traefik_configs_dynamic_dir_symlink: "{{ traefik_configs_symlink }}/dynamic"
traefik_configs_cur_dir_name: "{{ traefik_configs_version }}"
traefik_configs_cur_dir: "{{ traefik_configs_dir }}/{{ traefik_configs_cur_dir_name }}"
traefik_configs_symlink: "{{ traefik_configs_dir }}/current"
traefik_static_config_path: "{{ traefik_configs_symlink }}/traefik.yaml"
traefik_releases_dir: /opt/traefik/releases
traefik_static_config:
traefik_dynamic_configs: []
traefik_release_version: v3.3.1
traefik_release_dir_name: "{{ traefik_release_version }}"
traefik_release_dir: "{{ traefik_releases_dir }}/{{ traefik_release_dir_name }}"
traefik_release_symlink: "{{ traefik_releases_dir }}/current"
traefik_release_symlink_bin: "{{ traefik_release_symlink }}/traefik"
traefik_release_archive_name: "traefik_{{ traefik_release_version }}_linux_amd64"
traefik_release_archive_full: "traefik_{{ traefik_release_version }}_linux_amd64.tar.gz"
traefik_release_bin_path: "{{ traefik_release_dir }}/traefik"
traefik_release_download_url: "https://github.com/traefik/traefik/releases/download/{{ traefik_release_version }}/{{ traefik_release_archive_full }}"
traefik_release_download_checksum: sha256:27fd502a8d80fc81b97faf5cebd7a1cbc1a2c5369d18e0f4471a66d1a8c139ce

49 - Update all packages

Update all system packages

49.1 - Defaults

Defaults for update_all_packages

50 - Users

Setup system users

50.1 - Defaults

Defaults for users
users_regular: {}
users_remove: {}
users_admins:
  ansible:
    name: ansible
    disable_passwd: true
    sudo_no_passwd: true
users_ssh_keys: {}

51 - Vault

Setup Hashicorp Vault

51.1 - Defaults

Defaults for vault
vault_user: vault
vault_time_server: time.cloudflare.com
vault_user_uid: 3123
vault_dir: "/opt/vault"
vault_data_dir: "{{ vault_dir }}/data"
vault_raft_dir: "{{ vault_dir }}/raft"
vault_config_dir: "{{ vault_dir }}/configs"
vault_config_path: "{{ vault_config_dir }}/vault.hcl"
vault_config_local_path: "files/vault.hcl"
vault_log_dir: "{{ vault_dir }}/logs"
vault_tls_dir: "{{ vault_dir }}/tls"
vault_service_path: "/lib/systemd/system/vault.service"
vault_tls_client_ca_file_local_path: files/tls_client_ca_file.pem
vault_tls_client_ca_file_path: "{{ vault_tls_dir }}/tls_client_ca_file.pem"
vault_tls_cert_file_path: "{{ vault_tls_dir }}/tls_cert_file.pem"
vault_tls_key_file_path: "{{ vault_tls_dir }}/tls_key_file.pem"
vault_tls_issue_path:
vault_tls_alt_names: ""
vault_bin_local_path: "{{ role_path }}/files/vault"
vault_bin_path: /usr/bin/vault
vault_peers_write: false
vault_peers_local_path:
vault_peers_remote_path: "{{ vault_raft_dir }}/raft/peers.json"

52 - Wireguard

Setup wireguard

52.1 - Defaults

Defaults for wireguard
wireguard_dir: "/etc/wireguard"
wireguard_local_config_paths: []

53 - Xray

Setup xray

Links:

53.1 - Defaults

Defaults for xray
xray_enabled: false
xray_config_dir: /usr/local/etc/xray
xray_log_dir: /var/log/xray
xray_config_path: "{{ xray_config_dir }}/0-main.json"
xray_client_id:
xray_wireguard_relays: []
xray_wireguard_ipv4_address:
xray_wireguard_ipv6_address:
xray_wireguard_private_key:
xray_config:
xray_configs: {}
xray_user: xray
xray_socket_dir: /var/shm/xray
xray_caddy_config_path: /etc/caddy/configs/xray.Caddyfile
xray_subscriptions_dir: /usr/local/etc/xray-subscriptions
xray_caddy_user: caddy