How to Install Canonical MaaS in Your HomeLab: Automate Bare-Metal OS Provisioning Over the Network

HomeLab tutorial - IT technology blog
HomeLab tutorial - IT technology blog

Quick Start: Get MaaS Running in 5 Minutes

If you have more than two physical machines in your HomeLab, you already know the pain — pulling out a USB drive, booting into the installer, clicking through the same screens over and over. I’ve done that ritual more times than I care to admit. MaaS (Metal as a Service) by Canonical is the tool that finally made me retire that USB drive for good.

You rack a machine, power it on, and MaaS handles the rest — PXE boot, disk partitioning, OS installation, network config, even post-install scripts. It’s what data centers use at scale. Turns out it runs surprisingly well on modest HomeLab hardware too.

Here’s the fastest path to a working MaaS region controller on Ubuntu 22.04:

# Install MaaS from the official snap
sudo snap install maas

# Initialize MaaS as a combined region+rack controller
sudo maas init region+rack \
  --database-uri "postgres://maas:maaspassword@localhost/maasdb" \
  --maas-url "http://192.168.1.10:5240/MAAS"

# Create your admin account
sudo maas createadmin \
  --username admin \
  --email [email protected] \
  --password yourpassword

Open http://192.168.1.10:5240/MAAS in your browser. If the dashboard loads, you’re already most of the way there.

Deep Dive: Understanding the MaaS Architecture

Before you enlist your first machine, it helps to know what’s actually happening under the hood. MaaS has two main components that matter for HomeLab use:

  • Region Controller — the brain. Runs the API, database, and web UI. You only need one.
  • Rack Controller — the hands. Handles DHCP, TFTP, and PXE on a specific subnet. You need one per physical network segment.

For most HomeLabs, a single machine running both is perfectly fine.

Prerequisites and Network Setup

MaaS needs to own DHCP on the subnet where your bare-metal machines live. This is the part that trips people up most. Already running pfSense or OPNsense? You have two options: disable DHCP on that subnet entirely, or carve out a dedicated VLAN for MaaS provisioning. I went the VLAN route — it keeps MaaS isolated without disrupting anything else.

My setup uses a dedicated 192.168.50.0/24 provisioning VLAN. MaaS owns DHCP there, and my main LAN stays untouched.

Setting Up the Database

MaaS needs PostgreSQL. Install and configure it before initializing MaaS:

# Install PostgreSQL
sudo apt update && sudo apt install -y postgresql

# Create the MaaS database and user
sudo -u postgres psql <<EOF
CREATE USER maas WITH PASSWORD 'maaspassword';
CREATE DATABASE maasdb OWNER maas;
\q
EOF

Importing OS Images

Right after login, go to Images in the sidebar. MaaS has to download OS images before it can provision anything. Select the Ubuntu versions you want — 22.04 LTS is a safe default — hit Update source, and let it sync. On a 100 Mbps connection, the initial download takes around 10–15 minutes; slower links can stretch to 20+.

# You can also trigger image sync from the CLI
maas admin boot-resources import

# Check sync status
maas admin boot-resources read

Configuring DHCP and DNS

Head to Networking → Subnets in the MaaS UI. Find your provisioning subnet and enable MaaS-managed DHCP. Set the gateway and DNS servers to match your network. From that point on, MaaS handles IP allocation for every machine that PXE-boots into it.

Enlisting Your First Machine

This is where the magic happens. On the target machine, set the BIOS boot order to PXE first. Power it on. Within 30 seconds, MaaS detects it — the machine shows up in the dashboard under Machines with status New.

Click on it, then hit Commission. MaaS boots a small RAM disk, inventories the hardware (CPU, RAM, disks, NICs), then powers the machine off. Status flips to Ready. That machine is now available for deployment.

Advanced Usage: Deploying Machines and Using Cloud-Init

Commissioning tells MaaS what the machine has. Deployment actually installs the OS. You can trigger it from the UI with a few clicks, or use the CLI — which is where MaaS really earns its keep for automation.

Deploying via CLI

# List all ready machines
maas admin machines read | jq '.[] | select(.status_name == "Ready") | {hostname, system_id}'

# Deploy Ubuntu 22.04 to a specific machine
maas admin machine deploy <system_id> \
  distro_series=jammy \
  hwe_kernel=ga-22.04

Replace <system_id> with the ID from the previous command. Deployment usually wraps up in 5–10 minutes. The machine comes up with SSH keys already installed, ready to use.

Cloud-Init for Post-Install Automation

MaaS supports cloud-init user data, which means you can run scripts automatically after OS installation. Here’s how I set up new machines with my standard packages and SSH config:

maas admin machine deploy <system_id> \
  distro_series=jammy \
  user_data=$(base64 -w 0 <<'EOF'
#cloud-config
packages:
  - vim
  - htop
  - curl
  - git
  - docker.io
runcmd:
  - systemctl enable docker
  - usermod -aG docker ubuntu
  - echo 'Provisioned by MaaS' > /etc/motd
EOF
)

Tagging Machines for Roles

Tags become essential once your HomeLab grows past half a dozen nodes. I tag machines by role — storage, compute, k8s-worker — so I can target the right hardware without hunting for system IDs every time.

# Add a tag to a machine
maas admin tag create name=k8s-worker
maas admin machine add-tag <system_id> k8s-worker

# List all machines with a specific tag
maas admin machines read | jq '.[] | select(.tag_names[] == "k8s-worker") | .hostname'

Integrating MaaS with Ansible

MaaS and Ansible pair well. Once MaaS finishes deployment, you have a clean machine with a known IP, primed for Ansible to configure. I use a small polling script that waits for Deployed status, then fires off a playbook automatically:

import subprocess
import time
import json

SYSTEM_ID = "abc123"

while True:
    result = subprocess.run(
        ["maas", "admin", "machine", "read", SYSTEM_ID],
        capture_output=True, text=True
    )
    data = json.loads(result.stdout)
    if data["status_name"] == "Deployed":
        ip = data["ip_addresses"][0]
        print(f"Machine deployed at {ip}, running Ansible...")
        subprocess.run(["ansible-playbook", "-i", f"{ip},", "site.yml"])
        break
    print(f"Status: {data['status_name']}, waiting...")
    time.sleep(15)

Practical Tips From Real-World HomeLab Use

Running MaaS in production — even HomeLab production — surfaces a handful of gotchas that the documentation glosses over. Here’s what actually bit me:

IPMI / BMC Is a Game Changer

MaaS supports IPMI out of the box. If your servers have IPMI or iDRAC, register them under the machine’s Power settings. MaaS can then power machines on and off automatically during commissioning and deployment — no physical intervention needed. This is what makes true lights-out automation possible.

# Set IPMI power parameters for a machine
maas admin machine update <system_id> \
  power_type=ipmi \
  power_parameters_power_address=192.168.50.101 \
  power_parameters_power_user=admin \
  power_parameters_power_pass=ipmiadminpass

Subnets and Fabric Gotchas

MaaS auto-discovers VLANs and subnets. If you’re running multiple VLANs, verify that MaaS correctly mapped each subnet to the right fabric. Mismatched fabrics cause PXE to fail silently — the machine boots to PXE, requests an IP, and gets nothing back from MaaS.

Release Before Re-deploying

Done with a machine and want to put a different OS on it? Release it first. That wipes the disk and drops it back to Ready. Deploying over an already-deployed machine doesn’t work — release is mandatory.

maas admin machine release <system_id>
# Wait for status to return to "Ready", then deploy again

Snap vs. Packages

The snap install is easier to manage — upgrades are a single sudo snap refresh maas. The catch: snap runs MaaS as a confined process. If you hit permission errors with custom scripts or external tools, the apt-based install gives you more flexibility at the cost of managing upgrades yourself.

Disk Partitioning Control

By default, MaaS hands the entire first disk to the OS. For finer control — a separate /data partition, LVM, ZFS — configure a custom storage layout in the machine’s detail page before deploying. It’s buried under Storage, but check it before you accidentally wipe a disk with data you care about.

Once MaaS is humming along, reprovisioning a machine drops from a 45-minute manual slog to a CLI command and a coffee break. For a HomeLab juggling Kubernetes, TrueNAS, and a handful of VMs, that’s not a small thing.

Share: