Chapter 1: Build Your Practice Lab

What you are building

The guide's exercises assume a conventional Ansible training classroom: a workstation control node and managed hosts servera to serverd on lab.example.com. This chapter builds a faithful copy on one Ubuntu machine at home, with Rocky Linux 9 VMs using the usual classroom names, IP addresses and users, on a private network that nothing outside can connect into.

The site's system administration path uses a lighter version of this lab: workstation, servera and serverb, without Ansible. This lab is a complete superset of it, with the same names, addresses, lab command and rht-vmctl, so if you build this one you never need the other.

It can't be grown into this one in place: its machines were built without the devops account Ansible connects as, and cloud-init only sets that up when a machine is first created. Keep anything you want from its workstation, tear it down, then build this lab from section 1.3 onwards. Your host preparation, the seal's rules file and rht-vmctl stay as they are.

The idea in one picture

Six VMs sit on a private network called rhcebr0. Inside that network they talk to each other freely, and they can reach the internet, so dnf install works. From outside, nobody can open a connection in: not your laptop, not your router, and not even the host itself. You step inside with lxc exec (which does not use the network at all), then SSH between the VMs exactly as in the classroom.

Pick a traffic flow to see its path and the firewall rule that decides it:

The address plan

The lab uses the classroom network 172.25.250.0/24 and the domain lab.example.com, so every hostname and IP address in this guide works unchanged.

NameIPvCPU / RAMExtra diskRole
(rhcebr0 gateway)172.25.250.254——Lives on the host. Routes the lab to the internet; runs DHCP and DNS.
utility172.25.250.81 / 1 GiB—Web server for practice files (optional)
workstation172.25.250.92 / 2 GiB—Ansible control node. You work here.
servera172.25.250.101 / 1 GiB5 GiBManaged host
serverb172.25.250.111 / 1 GiB5 GiBManaged host
serverc172.25.250.121 / 1 GiB5 GiBManaged host
serverd172.25.250.131 / 1 GiB5 GiBManaged host

Accounts on every VM

UserPasswordPurpose
studentstudentYour everyday login. sudo with a password.
devopsredhatThe account Ansible connects as. Passwordless sudo.
rootredhatSame as the classroom

Where each step happens

Every step in this chapter is labelled with where you run it:

A terminal on the Ubuntu host.

The LXD web interface in your browser.

Inside a lab VM, usually as student on workstation.

Your lab values

A few values differ on every home network. The instructions write them as placeholders, highlighted in amber: <HOST_LAN_IP>, <HOST_USER> and <ROUTER_IP>. Enter yours once here and every command in this guide shows (and copies) your real values instead:

PlaceholderMeaningHow to find it
<HOST_LAN_IP>The Ubuntu host's address on your local networkhostname -I on the host
<HOST_USER>Your login name on the Ubuntu hostwhoami on the host
<ROUTER_IP>Your local network's default gatewayip route | grep default

Everything else, including the 172.25.250.0/24 network, the hostnames and the student/devops passwords, is the same for everyone.

Why the host has two IP addresses

Your Ubuntu host keeps its normal address on your local network, <HOST_LAN_IP>. When you create the lab network, LXD builds a virtual switch called rhcebr0 inside the host and gives the host a second address on it: 172.25.250.254. That second address is the lab's gateway.

Why the host has two IP addresses: The host keeps its normal LAN address (wired or Wi-Fi) and gains a second one, 172.25.250.254, on the lab's virtual switch. The VMs use that second address as their gateway.

The gateway can't be removed: without it the VMs would get no addresses (DHCP), couldn't find each other by name (DNS), and couldn't install packages. What you can do, and what the seal does, is make it one-way: traffic may leave the lab, but nothing, including the host, may start a connection into it.

Host interfaceAddressFaces
Your ethernet / Wi-Fi card<HOST_LAN_IP>Your local network and the internet. Unchanged.
rhcebr0 (virtual, created by LXD)172.25.250.254The lab VMs only. Gateway, DHCP and DNS.

What you need

MinimumRecommendedTested on
Operating systemUbuntu Server LTS, x86_64, with snap supportUbuntu Server 26.04.1 LTS
CPU4 threads with Intel VT-x or AMD-V4+ coresIntel Core i5-7500, 4 cores / 4 threads, 3.4 GHz
Memory12 GB16 GB or more32 GB (30 GiB usable)
Free disk40 GB100 GB or more (SSD)200 GiB ZFS pool on a loop file
NetworkA fixed address on your LAN and internet accessWiredWi-Fi (USB adapter) on a /16 home LAN. The lab is routed with NAT, not bridged, so Wi-Fi works.

With all six VMs running idle, the tested host measured:

ResourceLab stoppedLab runningWhat it means
Host RAM available26 GiB22 GiBThe whole lab costs about 4 GiB at idle
RAM inside each VM—≈ 295 MiB per server · 373 MiB utility · 501 MiB workstationGuests use far less than their limits
Configured RAM limits7 GiB in totalThe ceiling if every VM is busy; size the host for this plus the OS
CPU—load average 0.12 (4 cores)7 vCPUs on 4 cores is fine; Ansible runs are short bursts
Storage pool used2.19 GiB of 192.81 GiBAll VMs, the image and the clean snapshots; ZFS stores only what's written

Tested versions

Newer patch releases should behave the same. If something differs, compare against this table first.

LayerComponentVersionHow it's pinned
HostUbuntu Server26.04.1 LTS, kernel 7.0.0-34-generic—
LXD (snap)5.21.8 LTS, rev 40958--channel=5.21/stable + snap refresh --hold
nftablesUbuntu 26.04 package—
Guest imageimages:rockylinux/9/cloudbuild 20260929_0805Optional local copy: rocky9-lab
Rocky Linux9.8 (Blue Onyx)—
workstationansible-core (dnf)2.14.18-3.el9_8.1Rocky 9.8 AppStream
rhel-system-roles1.120.5Rocky 9.8 AppStream
podman5.8.2Rocky 9.8 AppStream
python3.113.11.13Rocky 9.8 AppStream
ansible-navigator26.9.0pip install "ansible-navigator==26.9.0" in a venv
ansible.posix1.5.4ansible-galaxy collection install ansible.posix:1.5.4
community.general9.5.13ansible-galaxy collection install community.general:9.5.13
Managed hostsPython (platform)3.9Rocky 9 system Python
All VMsSELinuxEnforcing, selinux-policy 38.1.75Added by the profile; the image ships without it
firewalld, chronyRunningAdded by the profile, as on a RHEL server

The build at a glance