An agentless configuration automation solution powered by Ansible, containerized with Docker. This setup simplifies Windows, Linux, and standalone ESXi management by leveraging idempotent configuration and Infrastructure as Code (IaC) principles across hypervisors and service nodes.
- Agentless configuration management powered by Ansible over SSH.
- Docker-based controller for environment consistency across platforms.
- Modular architecture leveraging reusable Terraform modules and Ansible roles with external variable configuration.
- Centralized variable management using external YAML files and encrypted via Ansible Vault.
flowchart LR
%% 1. KVM-SRV01
%% - Host
SRV01_SPECS[("`**Model:** Precision 5820 Tower
**CPU:** Intel Xeon W-2155 (10 cores - 20 threads)
**RAM:** 256GB (8 x 32GB ECC 2666MHz)
**Storage:**
• 2 x 1TB NVMe SSD (OS)
• 4TB SATA SSD (VMs)
• 8TB HDD (3 x 4TB - RSTe RAID 5)
**OS:** Windows Server 2025`")] -- specs --- SRV01[KVM-SRV01]
%% - VMs
SRV01 --> DB01[KVM-DB01] -- specs --- DB01_SPECS[("`**CPU:** 4 vCPU
**RAM:** 16GB
**Storage:** 200GB
**OS:** Windows Server 2025
**Role:** SQL Server`")]
SRV01 --> DC01[KVM-DC01] -- specs --- DC01_SPECS[("`**CPU:** 2 vCPU
**RAM:** 8GB
**Storage:** 100GB
**OS:** Windows Server 2025
**Role:** Primary Domain Controller`")]
SRV01 --> DC02[KVM-DC02] -- specs --- DC02_SPECS[("`**CPU:** 2 vCPU
**RAM:** 8GB
**Storage:** 100GB
**OS:** Windows Server 2025
**Role:** Secondary Domain Controller`")]
SRV01 --> IAM01[KVM-IAM01] -- specs --- IAM01_SPECS[("`**CPU:** 2 vCPU
**RAM:** 4GB
**Storage:** 40GB
**OS:** Ubuntu Server 26.04
**Role:** Identity & Access Management`")]
SRV01 --> MEDIA01[KVM-MEDIA01] -- specs --- MEDIA01_SPECS[("`**CPU:** 4 vCPU
**RAM:** 8GB
**Storage:** 200GB
**OS:** Ubuntu Server 26.04
**Role:** Media Server`")]
SRV01 --> MGMT01[KVM-MGMT01] -- specs --- MGMT01_SPECS[("`**CPU:** 2 vCPU
**RAM:** 4GB
**Storage:** 50GB
**OS:** Ubuntu Server 26.04
**Role:** Container Management Server`")]
SRV01 --> MONITOR01[KVM-MONITOR01] -- specs --- MONITOR01_SPECS[("`**CPU:** 2 vCPU
**RAM:** 4GB
**Storage:** 100GB
**OS:** Ubuntu Server 26.04
**Role:** Monitoring Server`")]
SRV01 --> NAS01[KVM-NAS01] -- specs --- NAS01_SPECS[("`**CPU:** 2 vCPU
**RAM:** 4GB
**Storage:** 50GB
**OS:** Ubuntu Server 26.04
**Role:** NAS`")]
SRV01 --> ROUTER01[KVM-ROUTER01] -- specs --- ROUTER01_SPECS[("`**CPU:** 2 vCPU
**RAM:** 8GB
**Storage:** 100GB
**OS:** Windows Server 2025
**Role:** Network Router & Gateway`")]
SRV01 --> SECURITY01[KVM-SECURITY01] -- specs --- SECURITY01_SPECS[("`**CPU:** 4 vCPU
**RAM:** 4GB
**Storage:** 50GB
**OS:** Ubuntu Server 26.04
**Role:** Surveillance System`")]
SRV01 --> VPN01[KVM-VPN01] -- specs --- VPN01_SPECS[("`**CPU:** 2 vCPU
**RAM:** 2GB
**Storage:** 20GB
**OS:** Ubuntu Server 26.04
**Role:** Primary VPN`")]
SRV01 --> WEB01[KVM-WEB01] -- specs --- WEB01_SPECS[("`**CPU:** 2 vCPU
**RAM:** 8GB
**Storage:** 100GB
**OS:** Windows Server 2025
**Role:** IIS Web Server`")]
SRV01 --> WRK01[KVM-WRK01] -- specs --- WRK01_SPECS[("`**CPU:** 4 vCPU
**RAM:** 16GB
**Storage:** 100GB
**OS:** Windows 11 Pro
**Role:** Workstation`")]
SRV01 --> WRK03[KVM-WRK03] -- specs --- WRK03_SPECS[("`**CPU:** 4 vCPU
**RAM:** 16GB
**Storage:** 100GB
**OS:** Windows 10 Pro
**Role:** Workstation`")]
%% Styling
class SRV01 hosts
classDef hosts fill:#f1c232,stroke:#000,color:#000
class DB01,DC01,DC02,IAM01,MEDIA01,MGMT01,MONITOR01,NAS01,ROUTER01,SECURITY01,VPN01,VPN02,WEB01,WRK01,WRK02,WRK03,WRK04 vms
classDef vms fill:#7ea6e0,stroke:#000,color:#000
class SRV01_SPECS,DB01_SPECS,DC01_SPECS,DC02_SPECS,IAM01_SPECS,MEDIA01_SPECS,MGMT01_SPECS,MONITOR01_SPECS,NAS01_SPECS,ROUTER01_SPECS,SECURITY01_SPECS,VPN01_SPECS,VPN02_SPECS,WEB01_SPECS,WRK01_SPECS,WRK02_SPECS,WRK03_SPECS,WRK04_SPECS specs
classDef specs fill:#d3d3d3,stroke:#000,color:#000
| Role | Description |
|---|---|
firewall_configuration |
Configure firewall rules |
linux_hostname_configuration |
Set hostname of Linux hosts |
linux_ntp_configuration |
Configure NTP settings for time synchronization on Linux hosts |
linux_updates |
Perform Linux package updates |
local_user_configuration |
Manage local user accounts and passwords |
region_configuration |
Set regional and locale settings |
system_configuration |
Apply system-wide settings |
system_information |
Gather host information |
windows_hostname_configuration |
Set hostname of Windows hosts |
windows_ntp_configuration |
Configure NTP settings for time synchronization on Windows hosts |
windows_updates |
Perform Windows updates |
| Role | Description |
|---|---|
domain_creation |
Create a new AD forest along with its domains and OUs |
domain_user_configuration |
Manage domain user accounts and passwords |
linux_domain_join |
Join Linux hosts to the domain |
windows_domain_join |
Join Windows hosts to the domain |
| Role | Description |
|---|---|
esxi_vm_deployment |
Deploy VMs on ESXi host |
hyper-v_configuration |
Install Hyper-V and configure virtual switches |
hyper-v_vm_deployment |
Deploy VMs on Hyper-V host |
| Role | Description |
|---|---|
adguardhome_deployment |
Configure AdGuard Home as a self-hosted DNS server and network-wide ad blocker |
authentik_deployment |
Configure Authentik as a centralized identity provider and SSO gateway for authentication and authorization |
docker_configuration |
Configure Docker settings |
nextcloud_deployment |
Configure Nextcloud as a self-hosted file manager |
grafana_deployment |
Configure Grafana as a analytics platform for dashboards and data virtualization |
home_assistant_deployment |
Configure Home Assistant as a centralized home automation platform for smart device integration |
homepage_deployment |
Configure Homepage as a centralized service dashboard for infrastructure monitoring and service discovery |
iis_deployment |
Install and configure Internet Information Services (IIS) |
immich_deployment |
Configure Immich as a self-hosted photo and video management solution for media backup |
jellyfin_deployment |
Configure Jellyfin as a self-hosted media server |
motion_deployment |
Configure Motion as a self-hosted security camera system |
node_exporter_deployment |
Configure Node Exporter as a metrics collector for Prometheus monitoring on Linux hosts |
npm_deployment |
Configure Nginx Proxy Manager as a reverse proxy for host forwarding |
portainer_deployment |
Configure Portainer as a container management platform for Docker environments |
prometheus_deployment |
Configure Prometheus as a time-series database for metrics collection |
snmp_exporter_deployment |
Configure SNMP Exporter as a metrics collector from network devices and ESXi host via SNMP |
tailscale_deployment |
Configure Tailscale as a mesh VPN service for secure remote access |
windows_exporter_deployment |
Configure Windows Exporter as a metrics collector for Prometheus monitoring on Windows hosts |
-
OpenSSH Server installed on the targeted hosts. Alternatively, this can be installed via PowerShell:
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
-
SSH Service enabled on the targeted hosts:
Start-Service sshd Set-Service -Name sshd -StartupType 'Automatic'
-
SSH allowed through the firewall:
New-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22 -Profile Any
Note
- The steps above apply strictly to Windows/Windows Server targets.
- Linux and ESXi distributions typically include pre-installed SSH services.
Tip
If the automated khangvum/answer-files are utilized for OS deployment, SSH is already provisioned and configured during the initial installation.
-
SSH connection verified once from the controller to each host:
ssh administrator@"<IP_ADDRESS>"Accept the host key on first connection by typing
yeswhen prompted.
- Update the inventory and variable files:
-
inventory.yml: Defines the target hosts. -
Docker variables:
File Description iam_var.ymlDefine Authentik settings for centralized identity provider and SSO gateway media_var.ymlDefine Immich and Jellyfin settings for self-hosted media server management_var.ymlDefine Portainer, Home Assistant, and Homepage settings for centralized service dashboard monitoring_var.ymlDefine Grafana, Prometheus, and Node Exporter settings for monitoring stack nas_var.ymlDefine Nextcloud settings for self-hosted file manager security_var.ymlDefine Motion settings for self-hosted security camera system vpn_var.ymlDefine Tailscale and Nginx Proxy Manager settings for Mesh VPN service -
Domain variables:
File Description domain_var.ymlDefines the domain configuration user_var.ymlDefines all domain users -
Hypervisor variables:
File Description esxi_vm_var.ymlDefines ESXi VMs and their specifications hyper-v_vm_var.ymlDefine the Hyper-V VMs and their specifications -
OS variables:
File Description esxi_var.ymlDefines ESXi-specific variables linux_var.ymlDefines Linux-specific variables windows_var.ymlDefines Windows-specific variables
-
Start the Ansible environment:
docker compose up -d
-
Access the running Ansible container:
docker exec -it ansible_service /bin/bash -
Execute the playbook to apply the settings:
ansible-playbook site.yml -i inventory.yml
Tip
Use -t option to selectively run specific plays:
ansible-playbook site.yml -i inventory.yml -t system_informationWhen managing sensitive credentials or variables in the homelab configuration, consider using ansible-vault.
To create a new encrypted file:
ansible-vault create "<VAULT_FILE_PATH>"To view the contents of an encrypted file without modifying it:
ansible-vault view "<VAULT_FILE_PATH>"To edit an encrypted file directly without manually decrypting, modifying, and re-encrypting it:
ansible-vault edit "<VAULT_FILE_PATH>"To secure an unencrypted file:
ansible-vault encrypt "<VAULT_FILE_PATH>"To permanently return an encrypted file to plaintext:
ansible-vault decrypt "<VAULT_FILE_PATH>"When the inventory or variable files are encrypted, Ansible requires a vault password to parse and run them successfully. Execution can be handled using one of the following methods:
To prompt interactively for the vault password at runtime, the --ask-vault-pass flag is appended to the execution command:
ansible-playbook site.yml -i inventory.yml --ask-vault-passTo bypass interactive prompts during automated runs or continuous deployment pipelines, a local password file can be referenced directly:
ansible-playbook site.yml -i inventory.yml --vault-password-file "<PASSWORD_FILE_PATH>"