Deployment guide

Grafana CE with Prometheus

Overview

This guide covers the deployment and configuration of Grafana CE with Prometheus on Ubuntu 24.04 on Azure using SecureAnyCloud 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 (SecureAnyCloud) 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 SecureAnyCloud support with guaranteed 24 hour response SLA

Prerequisites

You need:

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

1

Deploy from the Azure Portal

Open Marketplace in the Azure Portal and search for Grafana CE with Prometheus. Select the SecureAnyCloud 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 listens on plain HTTP by design, so 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 and wait for validation. Then click Create. Deployment takes around two minutes.

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-sac/providers/Microsoft.Compute/galleries/sacGallery/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
3

Connect via SSH

Connect to the VM:

ssh azureuser@<vm-ip>

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

4

Verify the Services

Confirm all three services are active:

sudo systemctl is-active grafana-server prometheus node_exporter

Expect three lines, each reading active. Next, confirm the listeners. Grafana is on 3000 (all interfaces), and Prometheus and node_exporter are on loopback only:

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

Check the first-boot sentinel and Grafana health:

sudo test -f /var/lib/sac/data/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": "..."}.

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 that node_exporter is exporting host metrics. The count is non-zero:

curl -s http://127.0.0.1:9100/metrics | grep -c '^node_'
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 and readable by root only.

7

Verify the End-to-End Metric Path

Prove that 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/sac-prometheus/api/v1/query?query=up" | jq '.data.result[]? | {job: .metric.job, instance: .metric.instance, value: .value[1]}'
8

First Login

Open http://<vm-ip>:3000 in your browser. The Grafana sign-in page appears:

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

9

The Pre-Provisioned Node Metrics Dashboard

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

The dashboard is defined at /var/lib/grafana/dashboards/node-metrics.json and reloads automatically. Edit it in the UI, or drop additional dashboard JSON files into that directory.

10

The Prometheus Datasource

Open Connections > Data sources. The Prometheus datasource is already configured as the default and points at the loopback Prometheus on http://127.0.0.1:9090.

You do not need to add or configure it. It is provisioned from /etc/grafana/provisioning/datasources/sac-prometheus.yaml.

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.

Try these starter queries:

  • 100 - (avg(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) for CPU busy %
  • node_memory_MemAvailable_bytes for available memory
  • rate(node_network_receive_bytes_total[5m]) for network throughput
12

Add More Scrape Targets

To monitor additional hosts, install node_exporter on them. Then 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
13

Server Components

The key files and directories are:

  • 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/sac-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
  • First-boot sentinel: /var/lib/sac/data/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
14

Managing the Services

Status:

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

Stop, start or 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 if you lose it:

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

Put TLS in front for production

Grafana listens on plain HTTP by design. For production, terminate TLS at a reverse proxy. Use Nginx or Caddy on the same VM, proxying https://:443 to http://localhost:3000. Alternatively, use an Azure Application Gateway with TLS termination that routes to the VM private IP on port 3000.

Restrict the NSG so that port 3000 is reachable only from the proxy. The SecureAnyCloud nginx-ssl-certbot-ubuntu-24-04 image is a purpose-built companion reverse proxy.

Prometheus and node_exporter are loopback-only, so they need no additional network hardening. When you need the Prometheus UI, reach it over an SSH tunnel:

ssh -L 9090:127.0.0.1:9090 azureuser@<vm-ip>
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 that the NSG allows TCP 3000 from your client source IP

Dashboard shows "No data"

  • Confirm that 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 first boot: sudo journalctl -u grafana-firstboot.service --no-pager
  • Read the per-VM password in /stage/scripts/grafana-credentials.log 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
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, and pair it with TLS as in Step 15.
  • Leave Prometheus and node_exporter on loopback. Surface metrics through Grafana rather than exposing 9090 or 9100.
  • Enable Grafana alerting and route alerts to PagerDuty, Opsgenie, Slack or email.
  • Shred the credentials file once the password is in your secret store: sudo shred -u /stage/scripts/grafana-credentials.log
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.

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

Email: sales@secureanycloud.com

Website: secureanycloud.com

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

View on Marketplace

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure

Launch Grafana CE with Prometheus on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure