Developer Tools Azure

Grafana CE with Prometheus on Ubuntu 24.04 on Azure User Guide

| Product: Grafana CE with Prometheus on Ubuntu 24.04 LTS on Azure

Overview

This guide covers the deployment and configuration of Grafana CE with Prometheus on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images. This is a ready-to-use monitoring appliance that bundles three components on one virtual machine:

  • Grafana Community Edition — the observability dashboard UI, on TCP 3000
  • Prometheus — a time-series metrics server that scrapes and stores metrics
  • node_exporter — a host-metrics agent that exposes CPU, memory, disk, and network metrics

Everything is wired together on first boot: Prometheus scrapes node_exporter and itself, and Grafana ships with Prometheus already configured as its default datasource plus a starter Node Metrics dashboard, so live host metrics appear the moment you sign in — no setup required.

The stack is secure by default. Prometheus (127.0.0.1:9090) and node_exporter (127.0.0.1:9100) bind to loopback only — they are never exposed on the network. Grafana is the single public entry point on TCP 3000. On every fresh virtual machine, grafana-firstboot.service generates a unique administrator password and a fresh Grafana secret_key before Grafana ever serves a request; the default admin/admin login is rejected and no credential is baked into the image. Credentials are written to /stage/scripts/grafana-credentials.log (mode 0600, root only).

What is included:

  • Grafana Community Edition (pinned 12.x, held against major-version upgrade) on TCP 3000

  • Prometheus (latest 3.x release binary) bound to 127.0.0.1:9090, pre-provisioned to scrape node_exporter and itself

  • node_exporter (latest 1.x release binary) bound to 127.0.0.1:9100

  • A provisioned Prometheus datasource and a starter Node Metrics (cloudimg) dashboard, live from first boot

  • grafana-firstboot.service — rotates the admin password and secret_key to per-VM values before Grafana serves

  • Dedicated non-root systemd services: grafana-server, prometheus, node_exporter

  • Ubuntu 24.04 LTS base with latest security patches applied at build time

  • Azure Linux Agent for seamless cloud integration and SSH key injection

  • 24/7 cloudimg support with guaranteed 24 hour response SLA

Prerequisites

  • An active Azure subscription

  • A subscription to the Grafana CE with Prometheus on Ubuntu 24.04 listing on Azure Marketplace

  • An SSH public key for VM authentication

  • A virtual network and subnet in the target region

Recommended virtual machine size: Standard_B2s (2 vCPU, 4 GB RAM) for a single host or small fleet. Scale up to Standard_D2s_v3 for larger metric volumes or many concurrent dashboard users.

Step 1: Deploy from the Azure Portal

Navigate to Marketplace in the Azure Portal, search for Grafana CE with Prometheus, select the cloudimg publisher entry, and click Create.

On the Networking tab attach a network security group that allows inbound TCP 22 from your management IP range and TCP 3000 from your trusted client networks. Do not expose port 3000 to the public internet without TLS — Grafana ships listening on plain HTTP by design (terminate TLS at a reverse proxy). Ports 9090 (Prometheus) and 9100 (node_exporter) are loopback-only and never need an NSG rule.

Click Review + create, wait for validation, then Create. Deployment takes around two minutes.

Step 2: Deploy from the Azure CLI

RG="monitoring-prod"
LOCATION="eastus"
VM_NAME="monitor-01"
ADMIN_USER="azureuser"
GALLERY_IMAGE_ID="/subscriptions/<sub-id>/resourceGroups/azure-cloudimg/providers/Microsoft.Compute/galleries/cloudimgGallery/images/grafanacewithprometheus-ubuntu-24-04/versions/<version>"
SSH_KEY="$(cat ~/.ssh/id_rsa.pub)"

az group create --name "$RG" --location "$LOCATION"

az network vnet create \
  --resource-group "$RG" \
  --name monitor-vnet --address-prefix 10.94.0.0/16 \
  --subnet-name monitor-subnet --subnet-prefix 10.94.1.0/24

az network nsg create --resource-group "$RG" --name monitor-nsg

az network nsg rule create \
  --resource-group "$RG" --nsg-name monitor-nsg \
  --name allow-ssh --priority 100 \
  --source-address-prefixes "<your-mgmt-cidr>" \
  --destination-port-ranges 22 --access Allow --protocol Tcp

az network nsg rule create \
  --resource-group "$RG" --nsg-name monitor-nsg \
  --name allow-grafana --priority 110 \
  --source-address-prefixes "<your-mgmt-cidr>" \
  --destination-port-ranges 3000 --access Allow --protocol Tcp

az vm create \
  --resource-group "$RG" --name "$VM_NAME" \
  --image "$GALLERY_IMAGE_ID" \
  --size Standard_B2s --storage-sku StandardSSD_LRS \
  --admin-username "$ADMIN_USER" --ssh-key-values "$SSH_KEY" \
  --vnet-name monitor-vnet --subnet monitor-subnet --nsg monitor-nsg \
  --public-ip-sku Standard

Step 3: Connect via SSH

ssh azureuser@<vm-ip>

On first boot, node_exporter and Prometheus start automatically, and grafana-firstboot.service rotates the Grafana admin credentials and starts Grafana.

Step 4: Verify the Services

Confirm all three services are active:

sudo systemctl is-active grafana-server prometheus node_exporter

Expected: three lines each reading active. Confirm the listeners — Grafana on 3000 (all interfaces), Prometheus and node_exporter on loopback only:

sudo ss -tlnp | grep -E ':(3000|9090|9100) '

Confirm the firstboot sentinel and Grafana health:

sudo test -f /var/lib/cloudimg/grafana-firstboot.done && echo FIRSTBOOT_DONE
curl -s http://localhost:3000/api/health

You should see FIRSTBOOT_DONE and {"database": "ok", "version": "12.4.x", "commit": "..."}.

All three services active; Grafana on 3000, Prometheus and node_exporter loopback-only

Step 5: Confirm Prometheus Is Scraping

Prometheus has no authentication and binds to loopback only, so query it directly from the VM. Both the node (node_exporter) and prometheus (self) targets should report health=up:

curl -s http://127.0.0.1:9090/api/v1/targets | jq -r '.data.activeTargets[] | "job=\(.labels.job)  instance=\(.labels.instance)  health=\(.health)"'

Confirm node_exporter is exporting host metrics (the count is non-zero):

curl -s http://127.0.0.1:9100/metrics | grep -c '^node_'

Prometheus scrape targets — node_exporter and self both UP; Prometheus bound to loopback

Step 6: Retrieve the Admin Password

sudo cat /stage/scripts/grafana-credentials.log

You will see:

ADMIN_USER=admin
ADMIN_PASSWORD=<ADMIN_PASSWORD>
GRAFANA_PORT=3000
GRAFANA_URL=http://<vm-ip>:3000

Store the password in your secret store. The file is mode 0600 (root only).

Step 7: Verify the End-to-End Metric Path

Prove metrics flow all the way through: this query runs up through Grafana's provisioned Prometheus datasource proxy and returns a live value for every target (both series report 1):

curl -s -u "admin:<ADMIN_PASSWORD>" "http://localhost:3000/api/datasources/proxy/uid/cloudimg-prometheus/api/v1/query?query=up" | jq '.data.result[]? | {job: .metric.job, instance: .metric.instance, value: .value[1]}'

A live metric round-trips through Grafana's Prometheus datasource proxy

Step 8: First Login

Open http://<vm-ip>:3000 in your browser. You will see the Grafana sign in page:

Grafana sign in page on a freshly deployed cloudimg VM

Sign in with admin and the password from Step 6. The default admin/admin login has been rotated away and will be rejected.

Step 9: The Pre-Provisioned Node Metrics Dashboard

Grafana ships with a Node Metrics (cloudimg) dashboard already provisioned under the cloudimg folder. Open it from Dashboards and you will see live host metrics rendering immediately — CPU busy, memory used, root filesystem used, system load, CPU usage by mode, and network traffic:

The provisioned Node Metrics dashboard rendering live host metrics out of the box

This dashboard is defined at /var/lib/grafana/dashboards/node-metrics.json and is reloaded automatically — edit it in the UI or drop additional dashboard JSON files into that directory.

Step 10: The Prometheus Datasource

Under Connections → Data sources the Prometheus datasource is already configured as the default, pointing at the loopback Prometheus on http://127.0.0.1:9090:

The pre-provisioned Prometheus datasource, set as default

You never need to add or configure it — it is provisioned from /etc/grafana/provisioning/datasources/cloudimg-prometheus.yaml.

Step 11: Explore Metrics with PromQL

Use Explore to run ad-hoc PromQL against the Prometheus datasource. For example, node_load1 graphs the 1-minute load average of the host:

Explore running a live node_load1 PromQL query against the provisioned Prometheus datasource

Useful starter queries: 100 - (avg(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) (CPU busy %), node_memory_MemAvailable_bytes (available memory), rate(node_network_receive_bytes_total[5m]) (network throughput).

Step 12: Add More Scrape Targets

To monitor additional hosts, install node_exporter on them and add a scrape job to /etc/prometheus/prometheus.yml:

sudo cat /etc/prometheus/prometheus.yml

Add a job under scrape_configs, for example:

  - job_name: 'remote-nodes'
    static_configs:
      - targets: ['10.0.1.20:9100', '10.0.1.21:9100']

Validate the config, then reload Prometheus without a restart:

sudo -u prometheus /usr/local/bin/promtool check config /etc/prometheus/prometheus.yml
curl -s -X POST http://127.0.0.1:9090/-/reload && echo reloaded

Step 13: Server Components

Component Path

Grafana server binary /usr/sbin/grafana-server

Grafana CLI /usr/sbin/grafana-cli

Grafana config /etc/grafana/grafana.ini

Provisioned datasource /etc/grafana/provisioning/datasources/cloudimg-prometheus.yaml

Provisioned dashboards /var/lib/grafana/dashboards/

Prometheus binary /usr/local/bin/prometheus

Prometheus config /etc/prometheus/prometheus.yml

Prometheus data /var/lib/prometheus/data/

node_exporter binary /usr/local/bin/node_exporter

Credentials file /stage/scripts/grafana-credentials.log

Firstboot sentinel /var/lib/cloudimg/grafana-firstboot.done

Inspect installed versions:

dpkg-query -W -f='grafana ${Version}\n' grafana
/usr/local/bin/prometheus --version 2>&1 | head -1
/usr/local/bin/node_exporter --version 2>&1 | head -1

Per-VM credentials file (0600 root only) and the pinned component versions

Step 14: Managing the Services

Status:

sudo systemctl status grafana-server.service --no-pager

Stop / Start / Restart (each service independently):

sudo systemctl restart prometheus.service
sudo systemctl restart node_exporter.service
sudo systemctl restart grafana-server.service

View logs:

sudo journalctl -u prometheus.service --no-pager -n 50
sudo tail -n 50 /var/log/grafana/grafana.log

Reset the Grafana admin password (e.g. if you lose it):

sudo grafana-cli admin reset-admin-password '<new-password>'

Step 15: Put TLS in Front (Production)

Grafana listens on plain HTTP by design. For production, terminate TLS at a reverse proxy — e.g. Nginx or Caddy on the same VM proxying https://:443 to http://localhost:3000, or an Azure Application Gateway with TLS termination routing to the VM private IP on port 3000. Restrict the NSG so port 3000 is reachable only from the proxy. The cloudimg nginx-ssl-certbot-ubuntu-24-04 image is a purpose-built companion reverse proxy.

Because Prometheus and node_exporter are loopback-only, they need no additional network hardening; reach the Prometheus UI when needed over an SSH tunnel:

ssh -L 9090:127.0.0.1:9090 azureuser@<vm-ip>

Step 16: Troubleshooting

Cannot reach Grafana on port 3000

  • Confirm the service: sudo systemctl status grafana-server.service

  • Confirm the listener: sudo ss -tln | grep 3000

  • Check the log: sudo tail -n 50 /var/log/grafana/grafana.log

  • Confirm the NSG allows TCP 3000 from your client source IP

Dashboard shows "No data"

  • Confirm Prometheus targets are up: curl -s http://127.0.0.1:9090/api/v1/targets | jq '.data.activeTargets[].health'

  • Confirm node_exporter: sudo systemctl status node_exporter.service

  • Widen the dashboard time range (top right) to the last 15 minutes

Grafana did not rotate the admin password

  • Check firstboot: sudo journalctl -u grafana-firstboot.service --no-pager

  • The per-VM password is in /stage/scripts/grafana-credentials.log (read with sudo cat)

Prometheus fails to start

  • Check the journal: sudo journalctl -u prometheus.service --no-pager -n 100

  • Validate the config: sudo -u prometheus /usr/local/bin/promtool check config /etc/prometheus/prometheus.yml

  • Check disk space: df -h /var/lib/prometheus

Step 17: Security Recommendations

  • Change the admin password again from the UI after first login (Profile → Change Password)

  • Create per-user accounts under Administration → Users; never share the admin login

  • Restrict port 22 to your management IP ranges only

  • Restrict port 3000 to trusted client networks; pair with TLS via Step 15

  • Leave Prometheus and node_exporter on loopback — surface metrics through Grafana rather than exposing 9090/9100

  • Enable Grafana alerting and route to PagerDuty, Opsgenie, Slack, or email

  • Shred the credentials file once the password is stored in your secret store: sudo shred -u /stage/scripts/grafana-credentials.log

Step 18: Support and Licensing

Grafana Community Edition is licensed under the GNU Affero General Public License version 3 (AGPLv3). Prometheus and node_exporter are licensed under the Apache License 2.0. There is no per-user, per-dashboard, or per-server fee. Grafana is a registered trademark of Grafana Labs; Prometheus is a trademark of The Linux Foundation.

cloudimg provides commercial support for this image separately from the upstream projects.

  • Email: support@cloudimg.co.uk

  • Website: www.cloudimg.co.uk

  • Support hours: 24/7 with guaranteed 24 hour response SLA

Deploy on Azure

Launch Grafana CE with Prometheus on Ubuntu 24.04 with 24/7 support from cloudimg.

View on Marketplace

Need Help?

Our support team is available 24/7.

support@cloudimg.co.uk