How to Install and Configure MAAS on Ubuntu 24.04 for Bare-Metal Provisioning
Learn how to install MAAS 3.7 on Ubuntu 24.04, initialize it in region+rack mode, create an admin account, and configure DNS and DHCP so real servers can be discovered and provisioned.
Every organization that outgrows the cloud, or that never fully moved into it, eventually runs into the same wall: physical servers do not provision themselves. Someone has to walk up with a USB stick, set the BIOS boot order, install an operating system, configure the network, and repeat that for every single machine in the rack. MAAS, short for Metal as a Service, is Canonical’s answer to that problem: it turns a room full of physical servers into an inventory you manage the way you would manage cloud virtual machines, discovering, commissioning, deploying, and later repurposing hardware from one central control plane instead of one keyboard at a time.
Table Of Content
- Concepts worth understanding before you install anything
- Why a single machine can be both the region and the rack
- Why MAAS needs a PostgreSQL database
- The machine lifecycle: discover, commission, deploy, repurpose
- What you need before you start
- Hardware and operating system
- Access and network
- Command-line tools
- Step 1: Confirm your host is ready
- Step 2: Install the MAAS snap
- Step 3: Install PostgreSQL and create the MAAS database
- Step 4: Initialize MAAS in region+rack mode
- Step 5: Create your administrator account
- Step 6: Log in with the MAAS CLI
- Step 7: Open the MAAS web interface
- Step 8: Point MAAS at an upstream DNS server
- Step 9: Find the subnet and rack controller you will use for DHCP
- Gather your subnet details
- Gather your rack controller details
- Step 10: Create a dynamic IP range and turn on DHCP
- Step 11: Set the subnet gateway
- Common mistakes and how to catch them
- Verifying everything works end to end
- Next steps
Canonical published a refreshed getting-started path for MAAS on July 14, 2026, streamlining the route from a blank Ubuntu machine to a working installation. This tutorial walks that same path in full, beginner-friendly detail: by the end you will have MAAS 3.7 installed and initialized on Ubuntu 24.04 LTS, an administrator account you can log into from both the command line and the web interface, and a subnet configured with DNS and DHCP so that real or virtual machines on your network can be discovered and are ready to be commissioned.
Every command and flag below was checked directly against Canonical’s official MAAS documentation, specifically the Install MAAS guide, the Configure MAAS guide, and the Supported MAAS versions reference, all current as of publication.
A few terms are worth defining before the first command, since the rest of this tutorial uses them constantly:
- Region controller is the part of MAAS that runs the API, the web interface, and the DNS service, and that talks directly to the PostgreSQL database. It is the brain of a MAAS installation.
- Rack controller is the part of MAAS that sits close to the actual hardware, serving DHCP leases and handling the network boot process for machines on a given subnet.
- Region+rack mode means running both roles on the same single machine, which is exactly what this tutorial sets up. It is the model Canonical’s own documentation recommends as the starting point for a homelab, a proof of concept, or a small production environment.
- Commissioning is the process where MAAS boots a newly discovered machine over the network, inspects its hardware, and records what it found (CPU, memory, disks, network interfaces) so that machine can later be deployed with an operating system on demand.
- Snap is Ubuntu’s containerized software packaging format. Both MAAS and PostgreSQL are installed as snaps in this tutorial, which bundles each application with its own dependencies so it does not conflict with whatever else is installed on the host.
Concepts worth understanding before you install anything
Why a single machine can be both the region and the rack
In a large deployment, you might run several region controllers for redundancy, with rack controllers deployed separately near each group of physical machines, so that losing one host does not take down the whole system. That is real, useful architecture, but it is also more than a first installation needs. Running both roles on one host, region+rack mode, gives you a fully functional MAAS installation with a single maas init command, and it is the configuration this tutorial builds. You can always add dedicated rack controllers later as your hardware footprint grows. Canonical’s own explanation of MAAS controllers breaks down exactly which services run on each: the region controller handles the REST API on port 5240, the PostgreSQL database, DNS caching, and the web UI, while the rack controller handles DHCP, TFTP, image delivery over HTTP, and power management for whichever machines are connected to it.
Why MAAS needs a PostgreSQL database
MAAS does not keep any of its state in memory or in flat files. Every machine it discovers, every network it manages, every user account and configuration setting, lives in a PostgreSQL database. That is why installing MAAS is really two installations: the MAAS snap itself, and a PostgreSQL snap (or an existing PostgreSQL server) for it to talk to. Without a reachable database, maas init has nowhere to write anything and will fail immediately.
The machine lifecycle: discover, commission, deploy, repurpose
Once your MAAS server is running, a physical machine’s life inside it follows a consistent pattern. First MAAS discovers the machine when it network-boots for the first time and reaches out over DHCP. Next, an administrator triggers commissioning, where MAAS boots a small inspection environment on the machine, catalogs its hardware, and marks it Ready. From Ready, the machine can be deployed with an operating system image on demand, used for whatever workload it is needed for, then released and repurposed for the next job, all without anyone touching the hardware directly. This tutorial gets your MAAS server to the point where that first step, discovery, is ready to happen; actually commissioning a machine is a natural next step once you finish here.
What you need before you start
Hardware and operating system
- A physical or virtual machine running Ubuntu 24.04 LTS (Noble Numbat). This is the Ubuntu release MAAS 3.7 is built and supported against, paired with PostgreSQL 16, according to Canonical’s supported versions table.
- Enough resources to comfortably run MAAS plus PostgreSQL: 2 CPU cores and 4 GB of RAM is a reasonable floor for a homelab or proof-of-concept installation, with more headroom recommended if you plan to manage many machines.
Access and network
- sudo privileges on that machine.
- A network segment (subnet or VLAN) where you are allowed to run a DHCP server. This matters: later in this tutorial you will turn on DHCP for a subnet, and if another DHCP server, such as your home router, is already answering requests on that same segment, the two will hand out conflicting leases to the same devices. Either isolate a VLAN for MAAS to manage, or disable DHCP on the existing server for that segment before continuing.
Command-line tools
- Basic comfort with the Linux command line. No prior MAAS experience is assumed.
jq, a command-line JSON processor this tutorial uses to turn MAAS API output into readable tables. Install it withsudo apt install -y jqif you do not already have it.
Step 1: Confirm your host is ready
Start by confirming you are actually on Ubuntu 24.04, since the MAAS snap channel used in the next step is built specifically against it.
# Context: fresh Ubuntu machine intended to run MAAS.
# Purpose: confirm the exact Ubuntu release and version.
cat /etc/os-release
Expected output: a block of key-value lines including VERSION_ID="24.04" and VERSION_CODENAME=noble. If your version differs, either install a fresh 24.04 host or, per Canonical’s documentation, consult the Supported MAAS versions reference for the MAAS release that matches your Ubuntu version instead of following this tutorial’s 3.7-specific commands.
Next, disable systemd-timesyncd. MAAS manages time synchronization itself using chrony, and the two services will conflict if both are active:
# Context: Ubuntu 24.04 host, confirmed in the previous command.
# Purpose: stop and disable the default time-sync service so chrony (installed with MAAS) can manage clock sync instead.
sudo systemctl disable --now systemd-timesyncd
Expected output: one or two lines noting that the symlinks for the service were removed. No error should appear; if the service was already inactive, the command still completes cleanly.
Finally, install jq, which you will need starting in Step 9:
# Context: Ubuntu 24.04 host.
# Purpose: install the jq JSON processor used later to format MAAS API output.
sudo apt install -y jq
Expected output: apt resolves and installs the package, ending in a line confirming it was set up. Confirm with jq --version, which should print something like jq-1.7.
Step 2: Install the MAAS snap
With the host confirmed ready, install MAAS itself. Pinning the channel to 3.7/stable instead of just latest/stable means you get the exact version this tutorial’s commands were verified against, rather than whatever happens to be newest when you read this:
# Context: Ubuntu 24.04 host, systemd-timesyncd disabled, jq installed.
# Purpose: install MAAS 3.7 from its stable snap channel.
sudo snap install maas --channel=3.7/stable
Expected output: a download and installation progress indicator, finishing with a single confirmation line naming the installed package, its version, and its publisher, which is the standard way any snap reports a successful install. Confirm it separately with snap list maas, which should show the package with a 3.7 version string.
Step 3: Install PostgreSQL and create the MAAS database
MAAS needs somewhere to store its state. If you already run a PostgreSQL server elsewhere, you can point MAAS at it directly and skip the install below; this tutorial installs a local PostgreSQL 16 snap, matching the dependency Canonical lists for MAAS 3.7:
# Context: Ubuntu 24.04 host, MAAS snap installed.
# Purpose: install PostgreSQL 16 from its stable snap channel.
sudo snap install postgresql --channel=16/stable
Expected output: the same style of snap install progress and confirmation line as Step 2, this time for PostgreSQL.
Next, create a dedicated database role and database for MAAS to use. These two commands connect as the postgres superuser (-U postgres) over the snap’s local Unix socket in /tmp (-h /tmp) rather than over the network, so unlike every other command in this section, they do not need sudo:
# Context: PostgreSQL snap installed and running.
# Purpose: create a new database role named "maas" and set its password.
postgresql.createuser -U postgres -h /tmp -P maas
Expected output: two password prompts, Enter password for new role: and Enter it again:. Pick a strong password here and store it somewhere safe; you will need it in the very next step.
# Context: the "maas" database role was just created.
# Purpose: create a database named "maas", owned by the "maas" role (-O maas).
postgresql.createdb -U postgres -h /tmp -O maas maas
Expected output: no output at all on success. If you see a role or database “already exists” error, either you already ran this step, or another application is using that name; check with postgresql.psql -U postgres -h /tmp -c '\du' to list existing roles before proceeding.
Step 4: Initialize MAAS in region+rack mode
This is the command that actually brings MAAS up, connecting it to the database you just created and setting it to run both the region and rack controller roles on this one host. Substitute the password you chose in Step 3 for <password>:
# Context: MAAS and PostgreSQL snaps installed, "maas" role and database created.
# Purpose: initialize MAAS in single-host region+rack mode, pointed at the local PostgreSQL database.
sudo maas init region+rack --database-uri "postgres://maas:<password>@localhost/maas"
Expected output: MAAS will run through its setup sequence and prompt you to accept a default MAAS URL, or to enter your own; pressing Enter accepts the default. The command finishes by starting the MAAS services. If it instead fails to connect, double-check the password in the database URI matches exactly what you set in Step 3, since a typo there is the single most common reason this step fails.
Step 5: Create your administrator account
With MAAS initialized, create the admin user you will use to log in from both the CLI and the web UI. The --ssh-import flag is optional, importing a public SSH key from a GitHub username so MAAS can later inject it into machines it deploys for you:
# Context: MAAS initialized in the previous step.
# Purpose: create an administrator account named "admin".
sudo maas createadmin \
--username admin \
--email admin@localhost \
--ssh-import gh:<your-github-username> # optional, omit this line if you don't want to import a key yet
Expected output: a prompt for a password for the new administrator account. Choose a strong password and store it securely, the same as the database password from Step 3.
Step 6: Log in with the MAAS CLI
The MAAS CLI authenticates using an API key rather than the password you just set. This command fetches that key and logs in, saving the session under a local profile name, here also called admin:
# Context: administrator account created in Step 5.
# Purpose: log in to the MAAS CLI, creating a local profile named "admin".
maas login admin "http://localhost:5240/MAAS" $(sudo maas apikey --username=admin)
The $(...) syntax runs sudo maas apikey --username=admin first and substitutes its output, the actual API key, directly into the maas login command, so you never have to copy and paste a key by hand.
Expected output: a confirmation message stating you are now logged in to the MAAS server at its API endpoint, under the profile name admin, along with a hint to run maas admin --help for available commands. From here on, this tutorial refers to that profile as $PROFILE; either substitute admin directly in every command below, or save yourself the retyping with export PROFILE=admin in your current shell session.
Step 7: Open the MAAS web interface
MAAS is not only a CLI tool. Open a browser and navigate to your server’s address on port 5240:
http://<maas-ip>:5240/MAAS
Replace <maas-ip> with your server’s actual IP address, or use http://localhost:5240/MAAS if you are browsing from the same machine MAAS is installed on. Log in with the admin username and the password you set in Step 5. You should land on the MAAS dashboard, currently empty since no machines have been discovered yet.
Step 8: Point MAAS at an upstream DNS server
MAAS needs to resolve external hostnames itself, for example to fetch Ubuntu images later on, so give it an upstream resolver. This example uses Google’s public DNS server; substitute your own preferred resolver if you have one:
# Context: logged in via the CLI in Step 6, $PROFILE set to your profile name (admin).
# Purpose: configure the upstream DNS server MAAS uses to resolve external hostnames.
maas $PROFILE maas set-config name=upstream_dns value="8.8.8.8"
Expected output: no output on success, which is the normal behavior for a MAAS CLI configuration write. You can confirm the value was saved with maas $PROFILE maas get-config name=upstream_dns, which should print back "8.8.8.8".
Step 9: Find the subnet and rack controller you will use for DHCP
Before MAAS can serve DHCP to machines, you need to tell it exactly which subnet and which rack controller to use. MAAS already auto-detected the networks visible to your host during initialization, so this step is about identifying the right one rather than creating anything new.
Gather your subnet details
# Context: $PROFILE set, jq installed from Step 1.
# Purpose: list every subnet MAAS knows about, with its ID, fabric ID, VLAN ID, and current gateway.
maas $PROFILE subnets read | jq -r '
["subnet", "subnet ID", "fabric ID", "vlan VID", "gateway IP"],
(.[] | [.cidr, (.id|tostring), (.vlan.fabric_id|tostring), (.vlan.vid|tostring), (.gateway_ip // "-")])
| @tsv
' | column -t -s $'\t'
Expected output: a table with one row per subnet MAAS has detected, showing each subnet’s CIDR notation (for example 192.168.1.0/24), its numeric subnet ID, fabric ID, VLAN ID, and gateway IP if one is already set. Identify the row for the network you want MAAS to manage, then record its three IDs as shell variables:
SUBNET_ID=<subnet_id>
FABRIC_ID=<fabric_id>
VID=<vlan_vid>
Gather your rack controller details
A rack controller can only serve DHCP for a subnet if it has a network interface actually connected to that subnet’s VLAN. This command filters the list of rack controllers down to ones that qualify:
# Context: FABRIC_ID and VID set from the subnet you chose above.
# Purpose: list rack controllers that have a network interface on the chosen VLAN.
maas $PROFILE rack-controllers read | jq -r --argjson f "$FABRIC_ID" --argjson v "$VID" '
(["SYSTEM_ID", "HOSTNAME"] | @tsv),
(.[] | select(any(.interface_set[]; .vlan.fabric_id == $f and .vlan.vid == $v)) | [.system_id, .hostname] | @tsv)
' | column -t -s $'\t'
Expected output: a table listing the system ID and hostname of every rack controller connected to that VLAN. In a single-host region+rack setup, you should see exactly one row, your own machine. Record its system ID:
PRIMARY_RACK_CONTROLLER=<system_id>
If this command returns no rows at all, it means no rack controller has an interface on that VLAN, and DHCP cannot be enabled for it until one does; double-check you identified the correct subnet in the previous command.
Step 10: Create a dynamic IP range and turn on DHCP
MAAS needs a slice of your subnet’s address space to hand out to machines automatically, a “dynamic range.” Choose a start and end address within your subnet that is not already in use by other devices:
# Context: SUBNET_ID identified in Step 9.
# Purpose: reserve a range of IP addresses for MAAS to hand out via DHCP.
maas $PROFILE ipranges create subnet=$SUBNET_ID type=dynamic start_ip=<start_ip> end_ip=<end_ip>
Expected output: a JSON object describing the newly created IP range, including the start and end addresses you specified and its type of dynamic.
With a range reserved, turn DHCP on for the VLAN, pointing it at the rack controller you identified in Step 9:
# Context: dynamic IP range created, PRIMARY_RACK_CONTROLLER identified in Step 9.
# Purpose: enable DHCP on this VLAN, served by the specified rack controller.
maas $PROFILE vlan update $FABRIC_ID $VID dhcp_on=True primary_rack=$PRIMARY_RACK_CONTROLLER
Expected output: a JSON object describing the updated VLAN, now showing "dhcp_on": true and your rack controller’s system ID under primary_rack. This is the point where MAAS actually starts answering DHCP requests on this network, so if you have not already confirmed there is no conflicting DHCP server on the same segment (from the prerequisites section above), stop and confirm that now before continuing.
Step 11: Set the subnet gateway
Finally, tell MAAS which address on the subnet is the gateway, so machines it provisions know how to reach the wider network. This is typically the first usable address in the subnet, often the same address your router already uses:
# Context: SUBNET_ID identified in Step 9.
# Purpose: set the gateway IP address for the subnet.
maas $PROFILE subnet update $SUBNET_ID gateway_ip=<gateway_ip>
Expected output: a JSON object describing the updated subnet, now showing your chosen address under gateway_ip. DHCP and gateway configuration are now complete: machines connected to this subnet can obtain IP addresses and network configuration automatically.
Common mistakes and how to catch them
- Leaving systemd-timesyncd running. If Step 1’s disable command is skipped, chrony (which MAAS relies on) and systemd-timesyncd will fight over control of the system clock, and clock drift can quietly break certificate validation and API calls later. If MAAS behaves strangely after installation, check
systemctl status systemd-timesyncdto confirm it is actually inactive. - Losing the PostgreSQL “maas” role password. This password only exists in your own memory or notes; PostgreSQL does not display it again. If you lose it before running
maas initin Step 4, the simplest fix is to set a new one for the existing role rather than trying to recover the old one. - Running
maas initbefore the database exists. If Step 4 fails with a connection error, confirm Step 3’screateuserandcreatedbcommands both completed without error first;maas inithas nothing to connect to otherwise. - Forgetting to set
$PROFILE. Every command from Step 8 onward usesmaas $PROFILE .... If you did not runexport PROFILE=adminafter Step 6, either run it now or replace$PROFILEwithadmindirectly in each command, or you will see a “profile not found” style error. - Enabling DHCP on a subnet another server already serves. Two DHCP servers answering the same broadcast requests will hand out conflicting leases to the same devices, causing intermittent, hard-to-diagnose connectivity issues for anything on that network. Confirm no other DHCP server is active on the segment before Step 10, not after.
- Choosing a rack controller with no interface on the target VLAN. Step 9’s filtered query exists specifically to prevent this; if you skip straight to Step 10 with a system ID from an unfiltered list instead, the VLAN update can reference a rack controller that is physically unable to serve DHCP for that network.
Verifying everything works end to end
Confirm all of the following before considering the installation complete:
maas $PROFILE machines readreturns a valid (even if empty) JSON list rather than an authentication error, confirming your CLI login from Step 6 is still valid.- In the MAAS web UI, under Subnets, the VLAN you configured in Step 10 shows as MAAS-provided, confirming DHCP is active for it.
maas $PROFILE subnet read $SUBNET_IDshows the gateway IP you set in Step 11 undergateway_ip.- If you have a spare physical or virtual machine to test with, set it to boot from the network (PXE boot) on the interface connected to your configured subnet, power it on, and watch the MAAS UI’s machine list; it should appear as a newly discovered machine within a few minutes, ready to be commissioned.
If all of those hold, you have a working single-host MAAS installation: initialized, backed by PostgreSQL, reachable from both the CLI and the web UI, and actively serving DHCP to a subnet you control.
Next steps
From here, the natural next step is commissioning your first real machine, network-booting it and letting MAAS catalog its hardware, so you can deploy an operating system to it on demand. As your footprint grows beyond a single host, Canonical’s documentation covers configuring PostgreSQL for remote connections, so the database can live on its own machine separate from the region controller, and deploying multiple region and rack controllers for high availability, so the loss of any one host does not take your provisioning system down with it. Whichever direction you take it, the installation you just built is the same foundation Canonical’s own production guidance starts from.








No Comment! Be the first one.