Infrahub on Ubuntu 24.04 on Azure User Guide
Overview
Infrahub is an open source infrastructure data management platform — a source of truth you shape yourself. Instead of bending your infrastructure into someone else's fixed data model, you define your own schema: the node kinds, attributes and relationships that describe the sites, devices, circuits, IP address space and services you actually run. You then create, relate and query those objects through a GraphQL API or a web interface.
What makes Infrahub different from a database with a web front end is that it applies version control to your data. Changes are made on a branch, reviewed as a proposed change, and merged — so your source of truth carries the same branching, peer review and history that your teams already expect from code. Generators and transformations turn that data into device configuration and artifacts, and synchronised Git repositories keep templates and business logic alongside the data they render.
The cloudimg image installs Infrahub 1.10.5 as a digest pinned Docker Compose stack managed by systemd, so a working source of truth is online within minutes of launch.
What is included:
- Infrahub 1.10.5 (Apache-2.0), from the official published image
docker.io/opsmill/infrahub:1.10.5, pinned by digest so the appliance can never silently drift onto a different build - Docker Engine (Docker CE) and the Docker Compose plugin, from the official Docker package repository
- Neo4j 2025.10.1 Community Edition (GPLv3) — the graph database that holds your source of truth
- PostgreSQL 18 for the task manager, Redis 8.4.0 as the cache, and RabbitMQ 4.2.1 as the message broker
- The embedded Prefect task manager plus two task workers, for generators, transformations and Git synchronisation
- The Infrahub web interface and GraphQL API published on port 80. The graph database, PostgreSQL, Redis and RabbitMQ publish no host port at all and are reachable only on the private container network
- All stateful data on a dedicated 128 GiB data disk mounted at
/var/lib/infrahub(Docker's data-root is relocated there), independently resizable and re-provisioned on every VM - A per-VM
adminpassword and a per-VM API token, both generated at first boot - Anonymous read access disabled (upstream ships it enabled), and product telemetry opted out
- Bundled component licence notices for Neo4j and Redis under
/usr/share/doc/cloudimg/third-party-licences/ - Two systemd units:
infrahub.service(a oneshot wrapper arounddocker compose up -d) andinfrahub-firstboot.service - 24/7 cloudimg support
About the credentials
Upstream's public Compose file hardcodes a default admin password, an admin API token, an agent API token, a JWT signing key, a Neo4j password and a broker password. Those literals sit in a public git repository, so they are not secrets. None of them survive on this image. On the first boot of every VM, infrahub-firstboot.service generates a fresh value for each one, initialises the databases empty under the rotated credentials, verifies both the password login and the API token round trip, and then writes the two credentials you need to /root/infrahub-credentials.txt.
Prerequisites
An active Azure subscription, an SSH key, and a VNet and subnet. Standard_B8ms (8 vCPU / 32 GB RAM) is the recommended size. This is a genuinely heavy appliance: OpsMill documents a floor of 6 CPU cores and 12 GB RAM and a recommended 8 cores / 16 GB, and the stack runs seven containers including a JVM graph database, an Erlang message broker, PostgreSQL, Redis and three Python services. A 2 vCPU / 4 GB size is well under half the documented minimum and will not work.
NSG inbound rules: allow 22/tcp from your management CIDR for SSH, and 80/tcp from the CIDR that needs the Infrahub web interface and API (add 443/tcp if you put your own TLS in front).
Step 1: Connect over SSH
Replace <vm-ip> with the public IP of your VM. The default login user is azureuser.
ssh azureuser@<vm-ip>
Step 2: Confirm the services are active
sudo systemctl is-active docker.service infrahub.service
docker --version
docker compose version
You should see active printed twice, the Docker Engine version, and the Docker Compose v2 plugin version.

Step 3: Confirm the Infrahub containers are running
cd /opt/infrahub && sudo docker compose --env-file .env ps
All seven containers should report Up and the ones with health checks should report (healthy). The stack is infrahub-server, task-manager, two task-worker replicas, database (Neo4j), task-manager-db (PostgreSQL), cache (Redis) and message-queue (RabbitMQ).

Step 4: Retrieve your per-VM credentials
Every credential on this image is generated on first boot and is unique to your VM.
sudo cat /root/infrahub-credentials.txt
Keep this file safe. It contains your admin password and your API token.
Step 5: Confirm the API is answering
curl -s -o /dev/null -w 'HTTP %{http_code}\n' http://127.0.0.1/api/config
HTTP 200 confirms the Infrahub server is up and serving.
Step 6: Confirm the datastores are not reachable from the network
This image publishes exactly one host port. The graph database holding your source of truth, along with PostgreSQL, Redis and RabbitMQ, are reachable only on the private container network.
sudo ss -ltnH '! src 127.0.0.0/8' | awk '{print $4}' | sed 's/.*://' | sort -un
You should see 22 (SSH) and 80 (Infrahub) only. Neo4j's 7474 and 7687, PostgreSQL's 5432, Redis's 6379 and RabbitMQ's 5672 must not appear.

Step 7: Sign in to the web interface
Browse to http://<vm-ip>/ and sign in as admin with the password from Step 4.

After signing in you land on the Infrahub home view, with the object explorer, schema browser and branch controls in the left hand navigation.

Step 8: Browse the schema
Infrahub's schema is the heart of the product — it is what lets you model your own infrastructure rather than accept a fixed data model. Open Schema from the navigation to browse the node kinds currently loaded, their attributes and their relationships.

Step 9: Authenticate against the API
Infrahub's API is GraphQL. You can authenticate either with a session token from a password login, or directly with the API token from Step 4 using the X-INFRAHUB-KEY header. The token is simpler for scripting.
Set your token as a shell variable first (take the value from Step 4):
TOKEN='<INFRAHUB_API_TOKEN>'
Confirm it authenticates against a resource that requires authentication:
curl -s -o /dev/null -w 'HTTP %{http_code}\n' \
-H "X-INFRAHUB-KEY: $TOKEN" http://127.0.0.1/api/schema
HTTP 200 confirms the token is valid. The same request without the header is refused, because anonymous access is disabled on this image:
curl -s -o /dev/null -w 'HTTP %{http_code}\n' http://127.0.0.1/api/schema

Step 10: Load a schema of your own
This is the step that turns Infrahub into your source of truth. The following loads a small schema defining a Site node kind in a Demo namespace, with a name, a site code and a device count.
cat > /tmp/demo-schema.json <<'EOF'
{
"schemas": [
{
"version": "1.0",
"nodes": [
{
"name": "Site",
"namespace": "Demo",
"label": "Site",
"description": "A physical network site.",
"default_filter": "name__value",
"display_labels": ["name__value"],
"attributes": [
{ "name": "name", "kind": "Text", "unique": true },
{ "name": "code", "kind": "Text", "optional": true },
{ "name": "device_count", "kind": "Number", "optional": true }
]
}
]
}
]
}
EOF
curl -s -o /dev/null -w 'HTTP %{http_code}\n' \
-X POST 'http://127.0.0.1/api/schema/load?branch=main' \
-H "X-INFRAHUB-KEY: $TOKEN" -H 'Content-Type: application/json' \
--data-binary @/tmp/demo-schema.json
The schema is applied asynchronously. Confirm the new kind is live:
curl -s -H "X-INFRAHUB-KEY: $TOKEN" \
'http://127.0.0.1/api/schema?branch=main' | grep -c DemoSite
A count of 1 or more confirms DemoSite is now part of your schema.
Step 11: Create and read back an object
Now create an object of the kind you just defined, using a GraphQL mutation:
curl -s -X POST 'http://127.0.0.1/graphql' \
-H "X-INFRAHUB-KEY: $TOKEN" -H 'Content-Type: application/json' \
-d '{"query":"mutation { DemoSiteCreate(data: {name: {value: \"london-1\"}, code: {value: \"LON1\"}, device_count: {value: 24}}) { ok object { id name { value } } } }"}'
Then read it back with a GraphQL query:
curl -s -X POST 'http://127.0.0.1/graphql' \
-H "X-INFRAHUB-KEY: $TOKEN" -H 'Content-Type: application/json' \
-d '{"query":"query { DemoSite(name__value: \"london-1\") { edges { node { id name { value } code { value } device_count { value } } } } }"}'
The response contains the object you created, with the values you wrote. That round trip — define a schema, create an object, read it back — is Infrahub working end to end.
You can see the same object in the web interface under the object explorer.

Step 12: Work on a branch
Branching is Infrahub's defining capability. Create a branch, make changes against it, and the main branch stays untouched until you merge.
curl -s -X POST 'http://127.0.0.1/graphql' \
-H "X-INFRAHUB-KEY: $TOKEN" -H 'Content-Type: application/json' \
-d '{"query":"mutation { BranchCreate(data: {name: \"demo-change\", description: \"trial change\"}) { ok object { id name } } }"}'
List your branches to confirm:
curl -s -X POST 'http://127.0.0.1/graphql' \
-H "X-INFRAHUB-KEY: $TOKEN" -H 'Content-Type: application/json' \
-d '{"query":"query { Branch { id name description } }"}'
Queries and mutations can then be scoped to that branch by posting to /graphql/demo-change instead of /graphql. In the web interface, the branch selector at the top of the page switches the whole view to your branch, and Proposed Changes is where a branch is reviewed and merged.

Step 13: Change the admin password
The password from Step 4 is generated for your VM, but you should still set your own. In the web interface, open the account menu and change the password there.
Managing the service
Stop, start or restart the whole stack through systemd:
sudo systemctl stop infrahub.service
sudo systemctl start infrahub.service
sudo systemctl restart infrahub.service
Follow the logs for a single component:
cd /opt/infrahub && sudo docker compose --env-file .env logs -f infrahub-server
The stack takes a couple of minutes to become fully healthy from cold, because the graph database and the task manager both initialise before the server accepts traffic.
Where things live
| Path | Contents |
|---|---|
/opt/infrahub/docker-compose.yml |
The appliance Compose stack |
/opt/infrahub/.env |
Every generated per-VM secret (root only, 0600) |
/opt/infrahub/upstream-docker-compose.yml.reference |
Upstream's Compose file at the pinned tag, for reference |
/var/lib/infrahub |
The dedicated data disk — Docker's data-root, holding the graph, the task database, artifacts and Git repositories |
/root/infrahub-credentials.txt |
Your per-VM admin password and API token (root only, 0600) |
/usr/share/doc/cloudimg/third-party-licences/ |
Bundled component licence notices and verbatim licence texts |
Enabling HTTPS
Infrahub is served over plain HTTP on port 80 so the image works immediately on any network. For anything beyond evaluation, put TLS in front of it — either an Azure Application Gateway or Front Door terminating TLS and forwarding to port 80, or a reverse proxy on the VM itself with a certificate from your own CA or Let's Encrypt. If you terminate TLS elsewhere, restrict the NSG rule for port 80 to the proxy's address so the plain HTTP port is not reachable from the internet.
Backing up
Everything stateful lives on the data disk at /var/lib/infrahub, so an Azure disk snapshot of that volume captures your source of truth, the task history, artifacts and synchronised repositories together. Take the snapshot with the stack stopped for a consistent point in time:
sudo systemctl stop infrahub.service
# take your Azure disk snapshot here
sudo systemctl start infrahub.service
Licensing
Infrahub is licensed under the Apache License 2.0. This image also bundles third party components under their own terms, and their licence notices and verbatim licence texts ship on the image:
ls /usr/share/doc/cloudimg/third-party-licences/
- Neo4j 2025.10.1 Community Edition — GPLv3. This is the Community edition, which is freely redistributable; it is not Neo4j Enterprise Edition. The notice includes a written offer for the corresponding source.
- Redis 8.4.0 — tri licensed RSALv2 / SSPLv1 / AGPLv3. cloudimg elects SSPLv1 for this bundled component.
RabbitMQ is under MPL-2.0 and PostgreSQL under the PostgreSQL License.
Support
cloudimg provides 24/7 support for this image. Contact support@cloudimg.co.uk with your Azure subscription ID and the VM name.