Grafana CE with Prometheus on Ubuntu 24.04 on Azure User Guide
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 andsecret_keyto 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": "..."}.

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_'

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]}'

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

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:

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:

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:

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

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 withsudo 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