Grafana CE with Prometheus on AWS User Guide
Overview
Grafana is the open source standard for visualising time series data, and Prometheus is the industry standard engine for collecting and storing it. This cloudimg image ships both, already wired together, alongside node_exporter for detailed host metrics. Prometheus scrapes node_exporter and itself, Grafana has Prometheus set as its default datasource, and a starter host metrics dashboard is provisioned, so live charts render the first time you sign in rather than an empty datasource picker.
The appliance is secure by default. Grafana will not serve at all until first boot has generated a unique admin password and a fresh encryption secret for that specific instance, so no shared or default credential is ever baked into the image, and the well known default login is actively rejected. Prometheus and node_exporter bind to the loopback interface only, so neither is reachable from the network; you reach Prometheus data through Grafana, or over an SSH tunnel. The Prometheus time series database and Grafana's dashboard state each live on their own EBS volume, so retention and application data can be resized independently of the OS disk.
What is included:
- Grafana Community Edition 12.4.6, installed from Grafana's official apt repository, serving the UI on TCP 3000
- Prometheus 3.13.1 (
/usr/local/bin/prometheus), bound to127.0.0.1:9090, with its tsdb on a dedicated EBS volume at/var/lib/prometheus - node_exporter 1.12.1 (
/usr/local/bin/node_exporter), bound to127.0.0.1:9100, running as a dedicated non root user under a hardened systemd unit - A provisioned Prometheus datasource (set as default) and a starter "Node Metrics (cloudimg)" dashboard
grafana-firstboot.service, which rotates the admin password and the[security] secret_keyto fresh per instance values before Grafana ever serves- Dedicated EBS volumes for
/var/lib/prometheusand/var/lib/grafana - Ubuntu 24.04 LTS base, latest patches, unattended security upgrades enabled
Prerequisites
An active AWS account, an EC2 key pair, and a VPC with a subnet. Recommended instance type: m5.large. Prometheus memory use grows with the number of active time series, so scale up as you add scrape targets.
Step 1: Launch from the AWS Marketplace
Find Grafana CE with Prometheus on the AWS Marketplace, subscribe, and launch an instance. In the launch wizard, attach a security group that allows inbound TCP 22 (SSH) and TCP 3000 (the Grafana UI) from your client networks only. Prometheus and node_exporter are loopback bound and need no inbound rules of their own.
Step 2: Launch from the AWS CLI
AMI_ID="<marketplace-ami-id>" # from the Marketplace listing, in your region
KEY_NAME="my-key"
SUBNET_ID="subnet-xxxxxxxx"
SG_ID="sg-xxxxxxxx" # allows 22 + 3000 from your networks only
aws ec2 run-instances \
--image-id "$AMI_ID" \
--instance-type m5.large \
--key-name "$KEY_NAME" \
--subnet-id "$SUBNET_ID" \
--security-group-ids "$SG_ID" \
--associate-public-ip-address \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=grafana-prometheus}]'
Connecting to your instance
Connect over SSH as the login user for your image's operating system. grafana-server.service, prometheus.service and node_exporter.service all start automatically on first boot.
| Operating system | SSH login user | Connect |
|---|---|---|
| Ubuntu 24.04 LTS | ubuntu |
ssh -i my-key.pem ubuntu@<instance-public-ip> |
Step 3: Retrieve the Per-Instance Admin Password
First boot generates a random admin password for this instance alone and writes it to a root only file. Read it with:
sudo cat /stage/scripts/grafana-credentials.log
The file records the username, the generated password, and the URL to open:
# Grafana CE with Prometheus - Per-VM Credentials
#
ADMIN_USER=admin
ADMIN_PASSWORD=<ADMIN_PASSWORD>
GRAFANA_PORT=3000
GRAFANA_URL=http://<instance-public-ip>:3000
Step 4: Verify the Services
All three units should report active:
systemctl is-active grafana-server prometheus node_exporter
active
active
active
Confirm the versions that shipped in this image:
/usr/local/bin/prometheus --version 2>&1 | head -1
/usr/local/bin/node_exporter --version 2>&1 | head -1
dpkg-query -W -f='grafana ${Version}\n' grafana
prometheus, version 3.13.1 (branch: HEAD, revision: 73ff57ce2b8161059ac7fe5188f03f1c3d22b29a)
node_exporter, version 1.12.1 (branch: HEAD, revision: 6044da783597cc3b57aef7580ddcdcff58a4ee99)
grafana 12.4.6
Grafana's own health endpoint should report its database as ok:
curl -s http://127.0.0.1:3000/api/health
{
"database": "ok",
"version": "12.4.6",
"commit": "16fbc24965a6dbeb7ed7facafb090062214ba33e"
}
Step 5: Confirm Prometheus and node_exporter Are Loopback Only
This is the security posture of the appliance: only the authenticated Grafana UI is exposed. Prometheus and node_exporter listen on 127.0.0.1 and are not reachable from the network.
sudo ss -tlnp | grep -E '3000|9090|9100' | sed 's/users:.*//'
LISTEN 0 4096 127.0.0.1:9090 0.0.0.0:*
LISTEN 0 4096 127.0.0.1:9100 0.0.0.0:*
LISTEN 0 4096 *:3000 *:*
Only port 3000 is bound to all interfaces. To reach the Prometheus expression browser from your workstation, use an SSH tunnel rather than opening the port:
ssh -i my-key.pem -L 9090:127.0.0.1:9090 ubuntu@<public-ip>
Then browse to http://localhost:9090 on your own machine.
Step 6: Verify Both Scrape Targets Are Up
Prometheus is pre configured to scrape node_exporter and itself. Both targets should report up:
curl -s 'http://127.0.0.1:9090/api/v1/targets?state=active' | jq -r '.data.activeTargets[] | "\(.labels.job)\t\(.scrapeUrl)\t\(.health)"'
node http://127.0.0.1:9100/metrics up
prometheus http://127.0.0.1:9090/metrics up
Step 7: Confirm the Dedicated Data Volumes
The Prometheus tsdb and Grafana's state are each on their own EBS volume, mounted by filesystem UUID so the layout survives reboots and re launches:
df -h /var/lib/prometheus /var/lib/grafana
Filesystem Size Used Avail Use% Mounted on
/dev/nvme1n1 49G 360K 47G 1% /var/lib/prometheus
/dev/nvme2n1 20G 49M 19G 1% /var/lib/grafana
Either volume can be grown in the EC2 console and extended with resize2fs without touching the OS disk.
Step 8: Sign In to Grafana
Open http://<instance-public-ip>:3000/ in your browser. Sign in as admin with the password from Step 3.

Step 9: Open the Provisioned Dashboard
Go to Dashboards and open Node Metrics (cloudimg) in the cloudimg folder. It renders live host metrics immediately: CPU busy, memory used, root filesystem used, load average, CPU usage broken down by mode, and network throughput.

Step 10: Review the Prometheus Datasource
Go to Connections > Data sources > Prometheus. The datasource is already provisioned, set as the default, and pointed at the loopback Prometheus on http://127.0.0.1:9090.

Step 11: Prove the Datasource Round-Trip
Grafana proxies queries to Prometheus on the server side. This runs a PromQL query through Grafana's datasource proxy and returns a live value, which proves the whole chain: your credential, Grafana's auth, the proxy, and Prometheus itself.
PW=$(sudo grep '^ADMIN_PASSWORD=' /stage/scripts/grafana-credentials.log | cut -d= -f2-)
curl -s -u "admin:$PW" 'http://127.0.0.1:3000/api/datasources/proxy/uid/cloudimg-prometheus/api/v1/query?query=node_load1' | jq -r '.data.result[0].value[1]'
A numeric load average is returned, for example:
0.89
Step 12: Confirm the Default Login Is Rejected
No shared credential ships in this image. The well known admin/admin default is rejected, while your per instance password is accepted:
PW=$(sudo grep '^ADMIN_PASSWORD=' /stage/scripts/grafana-credentials.log | cut -d= -f2-)
echo "default admin/admin -> $(curl -s -o /dev/null -w '%{http_code}' -u 'admin:admin' http://127.0.0.1:3000/api/user)"
echo "per-instance password -> $(curl -s -o /dev/null -w '%{http_code}' -u "admin:$PW" http://127.0.0.1:3000/api/user)"
default admin/admin -> 401
per-instance password -> 200
Step 13: Add Your Own Scrape Targets
Prometheus reads its configuration from /etc/prometheus/prometheus.yml. Add a job under scrape_configs for any application /metrics endpoint, another host's node_exporter, or any community exporter:
- job_name: 'my-application'
static_configs:
- targets: ['10.0.1.25:8080']
Validate the file, then hot reload Prometheus by sending it SIGHUP. This re reads the configuration in place, so the tsdb stays open and no metrics are dropped:
sudo promtool check config /etc/prometheus/prometheus.yml
sudo systemctl kill -s HUP prometheus.service
Checking /etc/prometheus/prometheus.yml
SUCCESS: /etc/prometheus/prometheus.yml is valid prometheus config file syntax
Confirm the reload landed in the journal:
sudo journalctl -u prometheus -n 3 --no-pager | grep -i "loading configuration\|Completed loading"
New targets appear in Grafana immediately and can be charted with PromQL.
Step 14: Managing the Services
sudo systemctl status grafana-server
sudo systemctl restart grafana-server
sudo journalctl -u grafana-server -n 50 --no-pager
sudo journalctl -u prometheus -n 50 --no-pager
Grafana's own log lives at /var/log/grafana/grafana.log. Prometheus and node_exporter log to the journal.
Step 15: Import a Community Dashboard
Grafana has a large library of ready made dashboards. In the UI choose Dashboards > New > Import, enter a dashboard ID from grafana.com, and select the Prometheus datasource when prompted. Dashboard 1860 (Node Exporter Full) is a comprehensive host metrics dashboard that works with the node_exporter in this image.
Step 16: Add TLS (Optional)
Grafana serves plain HTTP on port 3000 by default. For production, put your own domain and a TLS certificate in front of it. Either terminate TLS at an AWS Application Load Balancer and forward to port 3000, or install a reverse proxy on the instance and point it at 127.0.0.1:3000. If you terminate TLS upstream, set root_url in /etc/grafana/grafana.ini to your public HTTPS URL so generated links and redirects are correct.
Step 17: Security Recommendations
- Restrict TCP 3000 and TCP 22 in your security group to known client networks only; never leave them open to the whole internet
- Leave Prometheus and node_exporter loopback bound, and reach them over an SSH tunnel as in Step 5
- Change the generated admin password to one your team manages, and add individual Grafana users rather than sharing the admin account
- Put TLS in front of the UI before exposing it beyond a trusted network (Step 16)
- Keep the OS patched; unattended security upgrades are enabled by default
- Take EBS snapshots of the Prometheus and Grafana volumes on a schedule that matches your retention needs
Step 18: Support and Licensing
Grafana is a trademark of Raintank, Inc. dba Grafana Labs and is distributed under the GNU Affero General Public License v3.0. Prometheus and node_exporter are Cloud Native Computing Foundation projects distributed under the Apache License 2.0. All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.
Deploy on AWS
Find Grafana CE with Prometheus on the AWS Marketplace to launch this image in your own account.
Need Help?
cloudimg provides 24/7 technical support for this image, covering deployment, upgrades, adding scrape targets and exporters, dashboard and alert configuration, and fronting the UI with your own domain and TLS. Email support@cloudimg.co.uk with your instance ID and a description of the issue.