Grafana is an open source visualization tool that turns metrics, logs and traces from sources such as Prometheus, Loki, MySQL or PostgreSQL into dashboards and alerts. In this tutorial you will install Grafana OSS from the official APT repository on Ubuntu 24.04, publish it securely behind Nginx with a Let's Encrypt certificate, connect it to Prometheus through a provisioning file, import a ready-made host dashboard, build your own dashboard with a variable, and create an alert rule.
Prerequisites
To follow this tutorial you need:
- A server running Ubuntu 24.04 LTS, for example a CubePath VPS, with at least 1 vCPU and 1 GB of RAM.
- A non-root user with
sudoprivileges and UFW enabled with SSH allowed. - A domain name with an A record pointing to the server, referred to as
your_domain(for examplegrafana.example.com). - Prometheus running on the same server at
localhost:9090and scraping Node Exporter. The tutorial "How to Install and Configure Prometheus on Ubuntu 24.04" sets this up.
Step 1 - Installing Grafana from the official repository
Grafana Labs maintains an APT repository with current releases, so you get updates through apt upgrade. Install the tools needed to add it:
sudo apt update
sudo apt install apt-transport-https wget
Download the repository signing key into /etc/apt/keyrings:
sudo mkdir -p /etc/apt/keyrings
sudo wget -O /etc/apt/keyrings/grafana.asc https://apt.grafana.com/gpg-full.key
sudo chmod 644 /etc/apt/keyrings/grafana.asc
Add the stable repository, restricted to that key:
echo "deb [signed-by=/etc/apt/keyrings/grafana.asc] https://apt.grafana.com stable main" | sudo tee /etc/apt/sources.list.d/grafana.list
Install Grafana OSS:
sudo apt update
sudo apt install grafana
The package does not start the service automatically. Enable it and start it now:
sudo systemctl daemon-reload
sudo systemctl enable --now grafana-server
systemctl status grafana-server --no-pager
● grafana-server.service - Grafana instance
Loaded: loaded (/usr/lib/systemd/system/grafana-server.service; enabled; preset: enabled)
Active: active (running) since Thu 2026-09-25 10:41:03 UTC; 4s ago
Grafana listens on port 3000. Check its health endpoint, which also reports the installed version:
curl -s http://localhost:3000/api/health
{
"database": "ok",
"version": "12.x.x",
...
}
Step 2 - Binding Grafana to localhost
By default Grafana listens on all interfaces over plain HTTP. Since Nginx will handle public traffic and TLS, make Grafana listen only on localhost and tell it the public URL it is served from. Open the main configuration file:
sudo nano /etc/grafana/grafana.ini
Find the [server] section. Most lines are commented out with ;. Set these values (remove the leading ;):
[server]
protocol = http
http_addr = 127.0.0.1
http_port = 3000
domain = your_domain
root_url = https://your_domain/
While you are in the file, disable anonymous sign-up and the Gravatar lookups in the [users] and [security] sections:
[users]
allow_sign_up = false
[security]
disable_gravatar = true
Restart Grafana and confirm it now listens only on 127.0.0.1:
sudo systemctl restart grafana-server
sudo ss -ltnp | grep 3000
LISTEN 0 4096 127.0.0.1:3000 0.0.0.0:* users:(("grafana",pid=4127,fd=12))
Step 3 - Publishing Grafana over HTTPS with Nginx
Install Nginx and Certbot with its Nginx plugin:
sudo apt install nginx certbot python3-certbot-nginx
Create a server block for Grafana. Grafana Live uses WebSockets on /api/live/, so that location needs the upgrade headers:
sudo nano /etc/nginx/sites-available/grafana
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
listen [::]:80;
server_name your_domain;
location / {
proxy_set_header Host $host;
proxy_pass http://127.0.0.1:3000;
}
location /api/live/ {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_pass http://127.0.0.1:3000;
}
}
Enable the site, test the syntax and reload Nginx:
sudo ln -s /etc/nginx/sites-available/grafana /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Open HTTP and HTTPS in the firewall:
sudo ufw allow 'Nginx Full'
Request a certificate. Certbot edits the server block to add TLS and a redirect from HTTP to HTTPS:
sudo certbot --nginx -d your_domain
When it finishes, Certbot prints Successfully deployed certificate. Renewal is handled by the certbot.timer systemd timer; you can check it with sudo certbot renew --dry-run.
Step 4 - Logging in and securing the admin account
Open https://your_domain in your browser. Log in with the default credentials, user admin and password admin. Grafana immediately asks you to set a new password; choose a strong one.
Then create a personal account for daily use instead of sharing admin: go to Administration > Users and access > Users > New user, fill in the form, and on the user's page set Organization role to Admin or Editor as needed.
Step 5 - Adding Prometheus as a data source with provisioning
You can add data sources in the UI under Connections > Data sources, but defining them in a provisioning file makes the setup reproducible and survives a rebuild. Grafana reads every YAML file in /etc/grafana/provisioning/datasources/ at startup.
Create the file:
sudo nano /etc/grafana/provisioning/datasources/prometheus.yaml
apiVersion: 1
datasources:
- name: Prometheus
uid: prometheus
type: prometheus
access: proxy
url: http://localhost:9090
isDefault: true
jsonData:
timeInterval: 15s
access: proxy means the Grafana server queries Prometheus, not the browser, so Prometheus never needs to be exposed publicly. Set timeInterval to your Prometheus scrape_interval so Grafana never picks a step smaller than your data resolution.
If you enabled basic authentication on Prometheus, add the credentials to the same entry:
basicAuth: true
basicAuthUser: admin
secureJsonData:
basicAuthPassword: your_strong_password
Because the file may contain a password, make it readable only by root and the grafana group:
sudo chown root:grafana /etc/grafana/provisioning/datasources/prometheus.yaml
sudo chmod 640 /etc/grafana/provisioning/datasources/prometheus.yaml
sudo systemctl restart grafana-server
In the UI, go to Connections > Data sources > Prometheus and click Test at the bottom of the page. You should see Successfully queried the Prometheus API. Provisioned data sources are read-only in the UI; change them by editing the file.
Step 6 - Importing a community dashboard
Grafana's dashboard library has thousands of ready-made dashboards. Node Exporter Full (ID 1860) is the standard dashboard for Node Exporter metrics.
- Go to Dashboards > New > Import.
- Enter
1860in Find and import dashboards for common applications and click Load. - Select the Prometheus data source and click Import.
The dashboard opens with CPU, memory, disk, filesystem and network panels. Use the Job and Host drop-downs at the top to switch between monitored servers. If panels show No data, confirm in Prometheus that the node job is UP.
Step 7 - Building your own dashboard with a variable
Imported dashboards are a good start, but you will want a compact overview with the numbers you care about. In this step you build a dashboard with a Host variable so one dashboard works for every server.
Create the dashboard and the variable:
- Go to Dashboards > New > New dashboard and click the gear icon (Settings), then open the Variables tab and click Add variable.
- Set Variable type to
Query, Name toinstanceand Label toHost. - Choose the Prometheus data source, set Query type to
Label values, Label toinstanceand Metric tonode_uname_info. This is equivalent to the querylabel_values(node_uname_info, instance). - Enable Multi-value and Include All option, then click Back to dashboard.
A Host drop-down now appears at the top of the dashboard.
Add a CPU panel:
- Click Add > Visualization and select the Prometheus data source.
- Switch the query editor to Code and enter:
100 * (1 - avg by (instance) (rate(node_cpu_seconds_total{mode="idle", instance=~"$instance"}[$__rate_interval])))
- In the right-hand panel options, set Title to
CPU usage, set Standard options > Unit toPercent (0-100), and set Min to0and Max to100. - Set Legend > Mode to
Listand put{{instance}}in the query's Legend field (under Options below the query) so each line is labeled by host.
Two details make this query robust. instance=~"$instance" uses a regex match so the All and multi-value selections work, and $__rate_interval lets Grafana pick a rate window that is always at least four scrape intervals.
Add a memory panel the same way, this time as a Gauge visualization, with unit Percent (0-100) and thresholds at 80 (orange) and 90 (red):
100 * (1 - node_memory_MemAvailable_bytes{instance=~"$instance"} / node_memory_MemTotal_bytes{instance=~"$instance"})
Add a Stat panel for uptime, with unit seconds (s):
node_time_seconds{instance=~"$instance"} - node_boot_time_seconds{instance=~"$instance"}
Click Save dashboard, name it Server overview, and save. Change the Host drop-down to confirm every panel follows the selection.
TipTo keep dashboards in version control, open Export > Export as JSON on the dashboard and commit the file. You can load JSON files automatically with a dashboard provider in
/etc/grafana/provisioning/dashboards/.
Step 8 - Creating an alert rule
Grafana can evaluate queries on a schedule and notify you when a condition holds. Alerts need a way to deliver notifications. For email, configure SMTP in /etc/grafana/grafana.ini:
sudo nano /etc/grafana/grafana.ini
[smtp]
enabled = true
host = smtp.example.com:587
user = [email protected]
password = your_smtp_password
from_address = [email protected]
from_name = Grafana
startTLS_policy = MandatoryStartTLS
Restart Grafana to apply it:
sudo systemctl restart grafana-server
Create a contact point:
- Go to Alerting > Contact points > Create contact point.
- Name it
Ops email, choose the Email integration and enter one or more addresses. - Click Test to send a test message, then Save contact point.
Create the alert rule:
- Go to Alerting > Alert rules > New alert rule and name it
High CPU. - Select the Prometheus data source and enter the query:
100 * (1 - avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])))
- In the expressions below the query, set the threshold to Is above
90. - Choose or create a folder and an evaluation group that runs every
1m, and set the Pending period to5m, so a short spike does not page anyone. - Under Configure notifications, select the
Ops emailcontact point. - Click Save rule and exit.
Because the query aggregates by (instance), Grafana creates one alert instance per host and the notification says which server is busy. The rule list shows the state of each instance as Normal, Pending or Firing.
Step 9 - Backing up Grafana
With the default SQLite backend, all users, dashboards, alert rules and settings made in the UI live in /var/lib/grafana/grafana.db, and your configuration lives in /etc/grafana. Back up both with Grafana stopped briefly so the database file is consistent:
sudo systemctl stop grafana-server
sudo tar czf /root/grafana-backup-$(date +%F).tar.gz /var/lib/grafana/grafana.db /etc/grafana
sudo systemctl start grafana-server
Copy the archive off the server. To restore, stop Grafana, extract the archive over the same paths with sudo tar xzf ... -C /, make sure /var/lib/grafana is owned by grafana:grafana, and start Grafana again.
Troubleshooting
502 Bad Gateway from Nginx. Grafana is not running or not listening on 127.0.0.1:3000. Check systemctl status grafana-server and sudo journalctl -u grafana-server -n 50; a typo in grafana.ini usually shows up there.
Redirect loops or broken links after login. root_url does not match the URL in the browser. It must be exactly https://your_domain/.
Data source test fails with connection refused. Prometheus is not listening on localhost:9090. Check curl -s localhost:9090/-/ready. With 401 Unauthorized, the basic auth credentials in the provisioning file are wrong.
Forgotten admin password. Reset it from the server:
sudo grafana cli --homepath /usr/share/grafana --config /etc/grafana/grafana.ini admin reset-admin-password 'new_strong_password'
Conclusion
Grafana is now running on Ubuntu 24.04 behind Nginx with HTTPS, reading Prometheus through a provisioned data source, with an imported host dashboard, a custom dashboard driven by a variable, and an email alert for high CPU. Next, add Alertmanager or more contact points (Slack, PagerDuty, webhooks), provision your dashboards from Git, and add Loki as a second data source to correlate logs with metrics on the same dashboards.
