Automating Bare-Metal Provisioning with Canonical MAAS
How I built a reliable WhatsApp AI shopping assistant for Clickmothercare that survives hallucinated products, silent save failures, and multi-agent handoff bugs.
Managing physical servers traditionally involves manual ISO mounting, OS installation, and network configuration. Canonical MAAS (Metal as a Service) turns bare metal into an elastic, cloud-like resource, allowing you to provision physical machines as easily as AWS EC2 instances via PXE, IPMI/Redfish, and cloud-init.
Deploying the MAAS Region & Rack Controllers
A MAAS architecture consists of a Region Controller (handles the API and PostgreSQL DB) and one or more Rack Controllers (handles DHCP, TFTP/PXE, and BMC power management). We install both on a central management node using snaps.
# Install MAAS via snap
sudo snap install maas
# Initialize the Region and Rack controllers on the same node
sudo maas init --mode all
# Create the initial administrative user
sudo maas createadmin \
--username=admin \
--password=securepassword \
--email=admin@homelab.local \
--ssh-import=gh:yourgithubhandle
Annotation: Using the snap installation isolates MAAS dependencies. The --ssh-import flag is incredibly powerful; it automatically pulls your public SSH keys from GitHub and injects them into the authorized_keys of the ubuntu user on every physical machine MAAS deploys.
Configuring the Fabric and DHCP
For MAAS to PXE boot nodes, it must manage DHCP for a specific VLAN/subnet (a "Fabric" in MAAS terminology). We use the CLI to enable DHCP management.
# Authenticate the CLI session
maas login admin http://localhost:5240/MAAS/api/2.0/
# Identify the VLAN ID for our provisioning subnet (e.g., 10.0.10.0/24)
maas admin subnets read
# Assume VLAN ID is 0
# Enable DHCP on the untagged VLAN, setting IP ranges
maas admin vlan update 0 0 \
dhcp_on=True \
primary_rack=node-hostname
# Define the dynamic IP range for PXE booting (enlistment/commissioning)
maas admin ipranges create \
type=dynamic \
start_ip=10.0.10.100 end_ip=10.0.10.200 \
subnet=10.0.10.0/24
Annotation: MAAS uses two types of IP ranges. dynamic ranges are strictly for ephemeral PXE boot environments used during hardware discovery (enlistment) and testing (commissioning). Deployed nodes are assigned static IPs outside this range.
Automated Node Provisioning via Cloud-Init
Once physical nodes are enlisted (discovered via PXE) and commissioned (hardware specs recorded), they sit in a 'Ready' state. We allocate and deploy an OS using user-data (cloud-init) for post-install configuration.
# cloud-init.yaml
#cloud-config
packages:
- docker.io
- fail2ban
runcmd:
- systemctl enable --now docker
- usermod -aG docker ubuntu
---
# Allocate a node with specific constraints (e.g., min 8 cores, 32GB RAM)
NODE_ID=$(maas admin machines allocate cores=8 mem=32768 | jq -r '.system_id')
# Deploy Ubuntu 22.04 with our custom cloud-init script
maas admin machine deploy $NODE_ID \
osystem=ubuntu \
distro_series=jammy \
user_data=$(base64 -w0 cloud-init.yaml)
Annotation: The allocate command treats bare metal like a cloud API, acquiring a node matching the resource constraints. The deploy command leverages IPMI/Redfish to power on the server, instructs it to PXE boot the Ubuntu Jammy image, writes the OS to disk, injects the base64-encoded cloud-init script for first-boot execution, and restarts the machine.
Is your AI agent's infrastructure secure and reliable?
Book a Free 15-Min Technical Audit