Ai
Artificial Intelligence (AI) Azure

Aim ML Experiment Tracking on Ubuntu 24.04 on Azure User Guide

| Product: Aim ML Experiment Tracking on Ubuntu 24.04 LTS on Azure

Overview

Aim is an open-source ML experiment tracker: your training code logs metrics, hyperparameters and other artifacts as it runs, and Aim's fast web UI lets you browse, filter and compare those runs afterwards - a self-hosted alternative to hosted experiment trackers. The cloudimg image installs the pinned official Aim 3.29.1 pip package into a dedicated virtual environment, runs the built-in web UI/API server (aim up) under systemd, and locks it down for a marketplace appliance: Aim itself is bound to loopback only, and nginx does all customer-facing work on port 80, behind a per-VM HTTP Basic Auth gate. Aim has no user accounts of its own, so the nginx Basic Auth credential (user admin, unique password generated on first boot) is the gate on the UI and REST API. The Aim repository - the actual experiment database - lives on a dedicated 20 GiB Azure data disk and is genuinely durable across restarts and reboots. A small demo experiment with three runs is seeded on first boot so the UI shows real data immediately. Backed by 24/7 cloudimg support.

What is included:

  • Aim 3.29.1 installed via pip into a dedicated virtual environment, running as the aim systemd service
  • The Aim web UI and full REST API on :80, fronted by nginx with Aim bound to loopback only
  • Per-VM HTTP Basic Auth (user admin) protecting the UI and API, with a unique password generated on first boot
  • A dedicated Azure data disk at /var/lib/aim holding the Aim repository (RocksDB-backed, durable across restarts/reboots)
  • A seeded demo experiment (cloudimg-demo, 3 runs with varying hyperparameters and metric curves) so the runs table, metrics chart, run detail and hyperparameters views all show real data on first login
  • aim.service + nginx.service as systemd units, enabled and active
  • An unauthenticated /healthz endpoint on :80 for Azure Load Balancer health probes
  • 24/7 cloudimg support

Prerequisites

An active Azure subscription, an SSH key pair, and a VNet + subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a comfortable starting point for a small team's experiment tracking; size up the VM for larger repositories or heavier UI concurrency. NSG inbound: allow 22/tcp from your management network, 80/tcp for the UI and API, and 443/tcp if you add TLS. Aim serves plain HTTP; for production use, terminate TLS in front of it with your own domain.

Step 1 - Deploy from the Azure Marketplace

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Aim by cloudimg, and select Create. On Basics pick your subscription, resource group, region and size; under Administrator account choose SSH public key and paste your key; under Inbound port rules allow SSH (22) and HTTP (80). Review the dedicated data disk on the Disks tab, then Review + create -> Create.

Step 2 - Deploy from the Azure CLI

az vm create \
  --resource-group <your-rg> \
  --name aim \
  --image <marketplace-image-urn> \
  --size Standard_B2s \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_ed25519.pub \
  --vnet-name <your-vnet> --subnet <your-subnet> \
  --public-ip-sku Standard

az vm open-port --resource-group <your-rg> --name aim --port 80 --priority 1010

Step 3 - Connect to your VM

ssh azureuser@<vm-public-ip>

Step 4 - Confirm the services are running

systemctl is-active aim.service nginx.service

Both report active. Aim serves its web UI and full REST API on the loopback address 127.0.0.1:43800 only; nginx fronts port 80 with the per-VM HTTP Basic Auth gate. The Aim repository lives on the dedicated data disk at /var/lib/aim - this is the actual, durable experiment database (RocksDB-backed), not just a log file, so it survives service restarts and VM reboots.

The aim and nginx services active, Aim listening on loopback 127.0.0.1:43800 only, and the dedicated data disk mounted at /var/lib/aim

Step 5 - Retrieve your web UI password

nginx protects the Aim UI and API with HTTP Basic Auth. The username is admin and a unique password is generated on the first boot of your VM and written to a root-only file:

sudo cat /root/aim-credentials.txt

This file contains AIM_USERNAME, AIM_PASSWORD, the AIM_URL to open in a browser, and AIM_REPO_PATH for pointing your own training scripts at this server's repository. The password is stored on disk only as a bcrypt hash in /etc/nginx/.aim.htpasswd, so no plaintext password ships in the image. Store the password somewhere safe.

The installed Aim version, the per-VM credentials file with the generated admin password, the bcrypt nginx Basic Auth entry, and the size of the seeded Aim repository on the dedicated data disk

Step 6 - Confirm the health endpoint

nginx serves an unauthenticated health endpoint for load balancers and probes:

curl -s http://localhost/healthz

It returns ok. This endpoint never requires authentication, so it is safe for an Azure Load Balancer health probe.

Step 7 - Confirm authentication and browse the seeded demo experiment

Because a password is set on first boot, an unauthenticated request to the UI returns HTTP 401, so nobody reaches your experiment data without the password. The following reads the per-VM password from the credentials file, proves the auth round-trip, and lists the demo experiment seeded on first boot via the authenticated API:

PW=$(sudo grep '^AIM_PASSWORD=' /root/aim-credentials.txt | cut -d= -f2-)
echo "unauth  : $(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1/)"
echo "wrongpw : $(curl -s -o /dev/null -w '%{http_code}' -u admin:wrong-pw http://127.0.0.1/)"
echo "authed  : $(curl -s -o /dev/null -w '%{http_code}' -u admin:$PW http://127.0.0.1/)"
curl -s -u admin:$PW http://127.0.0.1/api/experiments/ | python3 -m json.tool

It prints unauth : 401, wrongpw : 401, authed : 200, and the API response lists the cloudimg-demo experiment with run_count: 3. Only the per-VM password reaches the Aim UI and API, because Aim itself is bound to loopback and nginx port 80 is the only way in.

The HTTP Basic Auth round-trip on port 80 returning 401 unauthenticated, 401 for a wrong password and 200 with the per-VM password, plus the authenticated API listing the seeded cloudimg-demo experiment with 3 runs

Step 8 - Sign in and browse the runs table

Browse to http://<vm-public-ip>/. Your browser prompts for a username and password: enter admin and the password from Step 5. Aim opens on the Runs explorer, listing the three demo runs seeded on first boot (baseline-lr1e-3, higher-lr3e-3, sgd-momentum), each logged against the cloudimg-demo experiment with its own hyperparameters and metric history.

The Aim Runs explorer listing the three seeded demo runs under the cloudimg-demo experiment

Step 9 - Compare metrics across runs

Open Metrics from the left navigation, click + Metrics, and select the tracked metrics you want to compare (for example loss subset="train" and accuracy subset="train"), then Search. Aim plots each selected metric as a chart with one line per run, so you can see at a glance which hyperparameter combination trained faster or reached a better result.

The Aim Metrics explorer comparing loss and accuracy curves across all three seeded runs

Step 10 - Inspect a single run's detail

Click any run to open its detail view. The Overview tab shows the run's hyperparameters (hparams.learning_rate, hparams.batch_size, hparams.optimizer), description and metadata; the Metrics, System and other tabs along the top show that run's individual metric curves, system resource usage during training, and any logged images, text or figures.

A single run's detail/overview page showing its hyperparameters and metadata

Step 11 - Compare hyperparameters across runs

Open Params from the left navigation, click + Run Params, select the hyperparameters you want to compare (hparams.batch_size, hparams.learning_rate, hparams.optimizer), then Search. Aim renders a parallel-coordinates plot alongside a table (with a Tags column) so you can see which combinations of hyperparameters were used across your runs at a glance.

The Aim Params explorer showing a parallel-coordinates comparison of hyperparameters across all three seeded runs, with the runs table and Tags column below

Step 12 - Log your own training runs

Point your own training code at this server's repository. If your training job runs on the same VM (or has the repository mounted over a shared filesystem), use the Aim Python SDK directly:

from aim import Run

run = Run(repo="/var/lib/aim", experiment="my-experiment")
run["hparams"] = {"learning_rate": 0.0003, "batch_size": 64, "optimizer": "adamw"}
for step, (loss, acc) in enumerate(your_training_loop()):
    run.track(loss, name="loss", step=step, context={"subset": "train"})
    run.track(acc, name="accuracy", step=step, context={"subset": "train"})
run.close()

pip install aim==3.29.1 in your training environment to match the server's SDK version. For training that runs on a different host and cannot reach this server's filesystem directly, use Aim's remote tracking server (aim server, a separate feature not enabled by default in this appliance) - see the upstream Aim documentation for the remote-tracking setup.

Maintenance

  • Password: the UI/API password is set on first boot and stored as a bcrypt entry in /etc/nginx/.aim.htpasswd. To change it, run sudo htpasswd -B /etc/nginx/.aim.htpasswd admin and then sudo systemctl reload nginx.
  • Durable storage: the Aim repository (/var/lib/aim/.aim) is genuinely durable - it survives aim service restarts and VM reboots. Back up the data disk (or snapshot it) like you would any production database if your experiment history matters.
  • Restrict access: Aim serves plain HTTP on port 80. For production, restrict access to trusted IP ranges in your Network Security Group, and front it with TLS (for example certbot with your own domain) terminating on :443.
  • Loopback binding: Aim is bound to 127.0.0.1:43800 in /etc/systemd/system/aim.service (--host 127.0.0.1 --port 43800), so nginx is the only path in. Keep it that way - do not change the bind address to a public interface.
  • Disk space: the Aim repository grows with the number of runs and metrics you log. Monitor /var/lib/aim usage (df -h /var/lib/aim) and resize the data disk if you log at high volume or for long-running projects.
  • Logs: journalctl -u aim shows recent Aim server output; journalctl -u nginx shows the web server's own logs.
  • Security patches: unattended-upgrades remains enabled so the OS continues to receive security updates automatically.

Support

cloudimg provides 24/7 expert support for this image. Contact support@cloudimg.co.uk.