Developer Tools Azure

Infrahub on Ubuntu 24.04 on Azure User Guide

| Product: Infrahub 1.10.5 on Ubuntu 24.04 LTS on Azure

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 admin password 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 around docker compose up -d) and infrahub-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.

Terminal showing docker.service and infrahub.service both reporting active, followed by the Docker Engine and Docker Compose v2 plugin version strings

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).

Terminal listing the Infrahub Compose services - cache, database, infrahub-server, message-queue, task-manager, task-manager-db and two task-worker replicas - each reporting Up and healthy

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.

Terminal showing the only non-loopback listening ports are 22 and 80, confirming the Neo4j graph database, PostgreSQL, Redis and RabbitMQ are private to the container network

Step 7: Sign in to the web interface

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

The Infrahub web interface login page, showing the Infrahub logo, a Username field, a Password field and a Log in button

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

The Infrahub dashboard after signing in, showing panels for open proposed changes, git repositories, branches and recent activities - the activity feed records the admin login, the demo-change branch and the london-1 object - with the left navigation sidebar for schema, branches and object management

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.

The Infrahub schema browser listing the loaded Core schema kinds such as Account, Account group, Account role, Core Action and Artifact, each shown as a node or generic with its description

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

Terminal showing the API token reaching /api/schema with HTTP 200, the same request without the token refused with HTTP 401, and a GraphQL query reading back the london-1 site object with its code LON1 and device count 24

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.

The Infrahub object explorer showing the Demo Site list with the london-1 object - Name london-1, Code LON1 and Device Count 24 - the object created through the GraphQL API rendered in the web interface

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.

The Infrahub branches list showing the main default branch alongside the newly created demo-change branch, with columns for status, last rebase, last update and creator - the version control that lets changes be made and reviewed before they reach main

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.