If you want hands-on experience with Ansible and Terraform, you need infrastructure to manage. Most guides recommend paying for cloud resources on Amazon Web Services or Microsoft Azure. This article shows how to build a zero-cost local lab using your own Linux computer. We will use QEMU/KVM virtualization and the Terraform libvirt provider.
Lab requirements
- Linux host system: I am using Linux Mint. Any Ubuntu-based distribution should work with minimal changes. If using another Linux family, adapt package management and service commands accordingly. (Windows Subsystem for Linux technically supports nested virtualization, but that is beyond the scope of this guide.)
- Personal Computer: 1 reasonably modern computer
- Home internet
Final Result ▶ Watch on YouTube
Here is what the complete workflow looks like once you have followed the how-to below:
1. Verify no VM is running
matthew@matthew-ThinkPad-E14-Gen-6:~$ virsh list --all
Id Name State
--------------------
2. Provision with Terraform
matthew@matthew-ThinkPad-E14-Gen-6:~$ cd infra-lab/terraform/
matthew@matthew-ThinkPad-E14-Gen-6:~/infra-lab/terraform$ terraform apply
...
Apply complete! Resources: 3 added, 0 changed, 0 destroyed.
Outputs:
vm_ip = "192.168.122.100"
Terraform creates the disk, VM and Ansible inventory and outputs the IP.
3. Confirm tmux is not yet installed on the VM
matthew@matthew-ThinkPad-E14-Gen-6:~/infra-lab/terraform$ ssh matt@192.168.122.100
Welcome to elementary OS 8 (GNU/Linux 6.17.0-22-generic x86_64)
matt@ElementaryOS:~$ tmux
Command 'tmux' not found, but can be installed with:
sudo apt install tmux
4. Configure the VM with Ansible
matthew@matthew-ThinkPad-E14-Gen-6:~/infra-lab/ansible$ ansible-playbook -i inventory.ini playbook.yml
PLAY [Base lab configuration] *************************************************
TASK [Gathering Facts] ********************************************************
ok: [vm1]
TASK [Install packages] *******************************************************
changed: [vm1]
PLAY RECAP ********************************************************************
vm1 ok=2 changed=1 unreachable=0 failed=0
changed=1: Ansible detected tmux was missing and installed it.
5. Confirm tmux is now installed
matthew@matthew-ThinkPad-E14-Gen-6:~/infra-lab/ansible$ ssh matt@192.168.122.100
matt@ElementaryOS:~$ tmux
[exited]
tmux is installed and working.
6. Run again to demonstrate idempotency
matthew@matthew-ThinkPad-E14-Gen-6:~/infra-lab/ansible$ ansible-playbook -i inventory.ini playbook.yml
PLAY RECAP ********************************************************************
vm1 ok=2 changed=0 unreachable=0 failed=0
changed=0: Ansible verified the desired state is already satisfied and did nothing.
7. Cleanup
matthew@matthew-ThinkPad-E14-Gen-6:~/infra-lab/terraform$ terraform destroy
How To
Install Linux Mint (or your preferred flavour of Ubuntu)
Update your system
sudo apt update && sudo apt upgrade
Install Ansible
The cleanest way is to install with pipx as per the Ansible documentation:
sudo apt install pipx
pipx ensurepath
pipx install --include-deps ansible
Install Terraform
Ensure that your system is up to date and that you have installed the gnupg and software-properties-common packages. You will use these packages to verify HashiCorp's GPG signature and install HashiCorp's Debian package repository.
sudo apt-get update && sudo apt-get install -y gnupg software-properties-common
Install HashiCorp's GPG key:
wget -O- https://apt.releases.hashicorp.com/gpg | \
gpg --dearmor | \
sudo tee /usr/share/keyrings/hashicorp-archive-keyring.gpg > /dev/null
Verify the GPG key's fingerprint:
gpg --no-default-keyring \
--keyring /usr/share/keyrings/hashicorp-archive-keyring.gpg \
--fingerprint
The gpg command reports the key fingerprint:
/usr/share/keyrings/hashicorp-archive-keyring.gpg
-------------------------------------------------
pub rsa4096 XXXX-XX-XX [SC]
AAAA AAAA AAAA AAAA
uid [ unknown] HashiCorp Security (HashiCorp Package Signing) <security+packaging@hashicorp.com>
sub rsa4096 XXXX-XX-XX [E]
Add the official HashiCorp repository to your system:
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] \
https://apt.releases.hashicorp.com \
$(grep -oP '(?<=UBUNTU_CODENAME=).*' /etc/os-release || lsb_release -cs) main" | \
sudo tee /etc/apt/sources.list.d/hashicorp.list
Update apt and install Terraform:
sudo apt update
sudo apt-get install terraform
Configure virtualization
First check that your CPU supports virtualization:
egrep -o 'vmx|svm' /proc/cpuinfo | head -1
If this returns vmx (Intel) or svm (AMD), your CPU supports virtualization.
Optionally install the cpu-checker package and check virtualization is enabled at the BIOS level:
sudo apt install cpu-checker
kvm-ok
If output is:
INFO: /dev/kvm exists
KVM acceleration can be used
You can proceed. Otherwise if you see:
INFO: Your CPU does not support KVM extensions
KVM acceleration can NOT be used
Try enabling virtualization at the BIOS level.
In this lab we are using Virtual Machine Manager (virt-manager) to configure the base virtual machine image using its GUI. Install it with:
sudo apt install virt-manager
Then reboot your system.
In this guide I will use Elementary OS (based on Ubuntu 24 LTS) for the virtual machine, feel free to use whatever distribution you like. Navigate to https://elementary.io/ and download the ISO image (just enter $0 unless you wish to support the project).
Now start up Virtual Machine Manager and check you can see QEMU/KVM support is working. Navigate to File → New Virtual Machine and browse for local installation media to find the ISO. Choose Ubuntu 24 LTS as the operating system since this is what Elementary is based upon, you can leave installation defaults.
When you boot into the live environment, choose to install the operating system onto the virtual machine and it will reboot. I'm setting the user as matt, if you use something else you will need to update the Terraform ansible_user variable later.
Once the OS is installed, you should be able to use the internet fine on the VM due to NAT. Update the environment:
sudo apt update && sudo apt upgrade
Configure SSH access
Find the VM's private IP address (within the libvirt NAT network):
ip a
On the VM, install the OpenSSH server:
sudo apt install openssh-server
sudo systemctl enable --now ssh
On the host machine, generate an SSH key pair:
ssh-keygen
Accept the default file path. If you already have an SSH key you use for other purposes, specify a different filename with -f ~/.ssh/id_ansible to avoid overwriting it.
Copy your public key to the VM:
ssh-copy-id youruser@VM_IP
Validate the SSH connection:
ssh youruser@VM_IP
Then configure the SSH daemon on the VM:
nano /etc/ssh/sshd_config
Disable password authentication and root login:
PasswordAuthentication no
PermitRootLogin no
Restart the SSH service to apply changes:
sudo systemctl restart ssh
systemctl status ssh
You have now configured passwordless SSH access from the host to the virtual machine!
The machine image created by virt-manager is stored at /var/lib/libvirt/images/ubuntu24.04.qcow2. This qcow2 file is the base image Terraform will clone for each VM it provisions. If your image name is different, you will need to update the Terraform base_image_path variable later.
Clone the lab environment and configure Ansible and Terraform
Clone the base config:
git clone https://github.com/MatthewBieda/HomeLabCore.git
cd HomeLabCore
Create an Ansible vault for secure secret storage by running this from the repo root:
ansible-vault create ansible/group_vars/lab/vault.yml
Add your sudo password for the VM to the vault like this:
ansible_become_password: <vm sudo password>
It will be encrypted. The ansible.cfg file refers to the vault password file to look up the vault password automatically.
Store the vault password in a hidden file outside the project:
echo "your_vault_password" > ~/.ansible_vault_pass
For a more enterprise-ready solution you can look into Red Hat Ansible Automation Platform.
Now configure Terraform. Navigate to the terraform directory and initialise:
cd terraform
terraform init
Update variables.tf for your environment if needed — the key variables are:
# Path to your base qcow2 image
variable "base_image_path" {
default = "/var/lib/libvirt/images/ubuntu24.04.qcow2"
}
# Username configured during OS installation
variable "ansible_user" {
default = "matt"
}
You are now ready to run the demo as shown at the top of this article!
Congratulations, you now have a complete infrastructure-as-code solution and playground, self-hosted for free.
Thanks for reading!