Grafana is an open-source visualization and analytics platform used by operations teams to monitor infrastructure and application metrics. It connects to data sources such as Prometheus, InfluxDB, Elasticsearch, PostgreSQL, and Loki, letting you build dashboards that turn raw time-series data into something you can actually act on. This guide walks through a production-ready Grafana 11.x deployment on Ubuntu 24.04 (Noble Numbat) and Debian 13 (Trixie), from package installation all the way to reverse-proxy hardening, authentication, and infrastructure-as-code provisioning.

Prerequisites

Before you start, make sure you have the following in place:

  • A server running Ubuntu 24.04 or Debian 13 with at least 1 CPU core, 1 GB RAM, and 10 GB of free disk space.
  • A non-root user with sudo privileges.
  • A working Prometheus installation if you plan to follow the data-source and dashboard sections.
  • A registered domain name pointed at your server (required for the Nginx SSL section).
  • Ports 3000/tcp (Grafana default) and 443/tcp (HTTPS) open in your firewall.

Update your package index and install a few dependencies that the rest of the guide relies on:

sudo apt update && sudo apt install -y apt-transport-https software-properties-common wget curl gnupg

Step 1: Add the Grafana APT Repository and Install Grafana OSS

Grafana Labs maintains a signed APT repository for Debian-based distributions. Start by importing the GPG signing key:

sudo mkdir -p /etc/apt/keyrings
wget -q -O - https://apt.grafana.com/gpg.key | gpg --dearmor | sudo tee /etc/apt/keyrings/grafana.gpg > /dev/null

Next, add the stable repository. This single line works on both Ubuntu 24.04 and Debian 13:

echo "deb [signed-by=/etc/apt/keyrings/grafana.gpg] https://apt.grafana.com stable main" | sudo tee /etc/apt/sources.list.d/grafana.list

Now refresh the package cache and install Grafana OSS:

sudo apt update
sudo apt install -y grafana

After the install finishes, verify the version:

grafana-server -v

You should see output that starts with Version 11. followed by the patch number. If you need the Enterprise edition instead, replace grafana with grafana-enterprise in the install command above. The Enterprise package is free for single-instance use and adds features like enhanced LDAP support and reporting.

Step 2: Enable and Start the Grafana Service

Grafana ships a systemd unit file. Enable it so the service starts on boot, then start it immediately:

sudo systemctl enable --now grafana-server

Check the status to confirm it is running without errors:

sudo systemctl status grafana-server

The output should show active (running). Grafana listens on TCP port 3000 by default. Confirm the port is open:

ss -tlnp | grep 3000

If your server runs ufw, allow the port:

sudo ufw allow 3000/tcp

Open your browser and navigate to http://<server-ip>:3000. You should see the Grafana login screen.

Step 3: Initial Login and Password Change

The default credentials are admin for both the username and the password. After your first login, Grafana forces you to set a new password. Pick something strong and store it in your password manager. If you ever need to reset a forgotten admin password from the command line:

sudo grafana-cli admin reset-admin-password YourNewSecurePassword

Once you log in, take a moment to explore the left sidebar. Grafana 11.x has reorganized the navigation. Dashboards, Alerting, and Connections (data sources) are the three areas you will use the most.

Step 4: Add Prometheus as a Data Source

If you already have Prometheus running, connecting it to Grafana takes about 30 seconds.

Navigate to Connections > Data sources > Add data source. Select Prometheus from the list. In the Prometheus server URL field, enter the address where Prometheus is reachable, for example:

http://localhost:9090

Scroll down and click Save & test. Grafana will run a quick query against the Prometheus API. If you see a green banner that says “Successfully queried the Prometheus API,” the connection is good.

For remote Prometheus instances, replace localhost with the actual hostname or IP. If Prometheus uses basic auth or sits behind a reverse proxy, expand the Auth section and fill in the credentials.

Step 5: Import Community Dashboards

The fastest way to start visualizing metrics is to import a pre-built dashboard from the Grafana community library. Two dashboards that practically every Prometheus setup benefits from are Node Exporter Full (ID 1860) and the Docker Monitoring dashboard (ID 893).

To import a dashboard, go to Dashboards > New > Import. In the Import via grafana.com field, type the dashboard ID and click Load.

For example, enter 1860 and click Load. On the next screen, select your Prometheus data source from the dropdown, then click Import. Within seconds you will have dozens of panels showing CPU, memory, disk, and network metrics collected by Node Exporter.

Repeat the same process with ID 893 if you run Docker workloads and have cAdvisor or Docker metrics exposed to Prometheus. These dashboards are fully editable, so you can rearrange panels, change thresholds, or add new queries as your monitoring needs grow.

Step 6: Create a Custom Dashboard with PromQL Panels

Imported dashboards are a great starting point, but production teams always end up building custom views tailored to their services. Here is how to create a dashboard from scratch.

Click Dashboards > New > New dashboard, then Add visualization. Select your Prometheus data source. In the query editor, switch to Code mode and enter a PromQL expression. A useful starting panel shows overall CPU usage across hosts:

100 - (avg by(instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)

Set the panel title to “CPU Usage %” and pick the Time series visualization type. Click Apply to save the panel to the dashboard.

Add another panel for memory usage:

(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100

And a third panel for disk space on the root filesystem:

100 - ((node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) * 100)

After adding your panels, click the floppy-disk icon at the top and give the dashboard a meaningful name. Use Dashboard settings > Variables to add a template variable for instance so users can filter panels by host from a dropdown at the top of the page.

Step 7: Configure Alerting

Grafana 11.x uses the Grafana Alerting engine by default. The older “legacy alerting” system that stored alert rules inside dashboard panels was removed in Grafana 10. All alerting is now centralized under the Alerting section in the sidebar.

The alerting workflow has three parts:

  1. Contact points define where notifications go (email, Slack, PagerDuty, Opsgenie, webhooks, and more).
  2. Notification policies control routing, grouping, and timing of alerts.
  3. Alert rules contain the actual PromQL (or other query language) expressions and thresholds.

To create a basic alert, go to Alerting > Alert rules > New alert rule. Give it a name like “High CPU.” Set the query to:

100 - (avg by(instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)

Under Expressions, add a Reduce expression that computes the Last value, then add a Threshold expression that fires when the value is above 90. Set the evaluation interval and the “for” duration (for example, evaluate every 1 minute and fire after 5 minutes of sustained breach). Assign the rule to a folder and an evaluation group, then save.

Before the alert can reach anyone, configure a contact point. Go to Alerting > Contact points > Add contact point. For Slack, choose the Slack integration type and paste your webhook URL. Save the contact point, then update the default notification policy to use it.

Test the full chain by temporarily lowering your threshold. Once you receive the notification, set the threshold back to a realistic value.

Step 8: Set Up Nginx Reverse Proxy with SSL

Running Grafana behind Nginx with TLS is the standard approach for production deployments. Start by installing Nginx and Certbot:

sudo apt install -y nginx certbot python3-certbot-nginx

Create an Nginx server block. Replace grafana.example.com with your actual domain:

sudo tee /etc/nginx/sites-available/grafana.conf <<'EOF'
server {
    listen 80;
    server_name grafana.example.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # WebSocket support for live features
    location /api/live/ {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
    }
}
EOF

Enable the site and test the configuration:

sudo ln -sf /etc/nginx/sites-available/grafana.conf /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

Now obtain a free TLS certificate from Let’s Encrypt:

sudo certbot --nginx -d grafana.example.com

Certbot will modify the server block to add the SSL directives and set up automatic renewal. After it finishes, update Grafana’s configuration so it knows it is behind a proxy. Edit /etc/grafana/grafana.ini and set these values under the [server] section:

[server]
domain = grafana.example.com
root_url = https://grafana.example.com/
protocol = http

Restart Grafana to apply the changes:

sudo systemctl restart grafana-server

You can now access Grafana at https://grafana.example.com. If you want to prevent direct access on port 3000, bind Grafana only to localhost by adding http_addr = 127.0.0.1 under the [server] section and restarting.

Step 9: LDAP and OAuth Authentication

For teams larger than a handful of people, local Grafana accounts become hard to manage. Grafana 11.x supports LDAP, OAuth 2.0 (Google, GitHub, GitLab, Azure AD, Okta, and generic providers), and SAML (Enterprise only).

LDAP

Enable LDAP by editing /etc/grafana/grafana.ini:

[auth.ldap]
enabled = true
config_file = /etc/grafana/ldap.toml
allow_sign_up = true

Then edit /etc/grafana/ldap.toml. A minimal Active Directory configuration looks like this:

[[servers]]
host = "ldap.example.com"
port = 636
use_ssl = true
start_tls = false
ssl_skip_verify = false

bind_dn = "cn=grafana,ou=ServiceAccounts,dc=example,dc=com"
bind_password = "securepassword"

search_filter = "(sAMAccountName=%s)"
search_base_dns = ["dc=example,dc=com"]

[servers.attributes]
name = "givenName"
surname = "sn"
username = "sAMAccountName"
member_of = "memberOf"
email = "mail"

[[servers.group_mappings]]
group_dn = "cn=GrafanaAdmins,ou=Groups,dc=example,dc=com"
org_role = "Admin"

[[servers.group_mappings]]
group_dn = "cn=GrafanaEditors,ou=Groups,dc=example,dc=com"
org_role = "Editor"

[[servers.group_mappings]]
group_dn = "*"
org_role = "Viewer"

Restart Grafana after saving both files. Users will now be able to log in with their directory credentials, and their Grafana role will be assigned based on group membership.

OAuth (GitHub Example)

To configure GitHub OAuth, register a new OAuth application in your GitHub organization settings. Set the callback URL to https://grafana.example.com/login/github. Then add the following to /etc/grafana/grafana.ini:

[auth.github]
enabled = true
allow_sign_up = true
client_id = YOUR_CLIENT_ID
client_secret = YOUR_CLIENT_SECRET
scopes = user:email,read:org
auth_url = https://github.com/login/oauth/authorize
token_url = https://github.com/login/oauth/access_token
api_url = https://api.github.com/user
allowed_organizations = your-github-org

Restart Grafana. A “Sign in with GitHub” button will appear on the login page. For Google or Azure AD, the pattern is the same: register an OAuth app, note the client ID and secret, and fill in the corresponding [auth.google] or [auth.azuread] block in grafana.ini.

Step 10: Provisioning Dashboards and Data Sources via YAML

Clicking through the UI is fine when you manage one Grafana instance. When you manage five, or when your dashboards need to be version-controlled alongside your Terraform and Ansible code, provisioning through YAML files is the way to go.

Grafana reads provisioning configuration from /etc/grafana/provisioning/. There are subdirectories for datasources, dashboards, alerting, and notifiers.

Provisioning a Data Source

Create a YAML file for your Prometheus data source:

sudo tee /etc/grafana/provisioning/datasources/prometheus.yaml <<'EOF'
apiVersion: 1

datasources:
  - name: Prometheus
    type: prometheus
    access: proxy
    url: http://localhost:9090
    isDefault: true
    editable: false
EOF

Provisioning Dashboards

First, tell Grafana where to find dashboard JSON files:

sudo tee /etc/grafana/provisioning/dashboards/default.yaml <<'EOF'
apiVersion: 1

providers:
  - name: default
    orgId: 1
    folder: Provisioned
    type: file
    disableDeletion: false
    updateIntervalSeconds: 30
    options:
      path: /var/lib/grafana/dashboards
      foldersFromFilesStructure: true
EOF

Then place your exported dashboard JSON files in /var/lib/grafana/dashboards/. You can export any dashboard from the Grafana UI by opening it, clicking the share icon, selecting Export, and toggling Export for sharing externally. Save the JSON file, commit it to your Git repository, and deploy it to the provisioning path with your configuration management tool of choice.

Restart Grafana after adding or changing provisioning files:

sudo systemctl restart grafana-server

Provisioned dashboards display a small “provisioned” badge in the UI. Users can still modify them temporarily, but changes revert on the next restart unless the JSON source file is updated. This workflow pairs well with CI/CD pipelines where dashboard changes go through pull requests and code review before landing in production.

Step 11: Upgrade Grafana

Because you installed Grafana from the official APT repository, upgrading is straightforward. Always check the Grafana release notes before upgrading, especially for major version bumps where breaking changes are documented.

Back up the Grafana database and configuration first:

sudo cp /var/lib/grafana/grafana.db /var/lib/grafana/grafana.db.bak
sudo cp /etc/grafana/grafana.ini /etc/grafana/grafana.ini.bak

Then upgrade:

sudo apt update && sudo apt install --only-upgrade grafana

Grafana handles database migrations automatically on startup. After the upgrade, restart the service and confirm the new version:

sudo systemctl restart grafana-server
grafana-server -v

Verify that your dashboards, data sources, and alert rules are intact. If anything looks off, restore the backup files and roll back to the previous package version with sudo apt install grafana=<previous-version>.

Step 12: Troubleshooting

Here are the issues that come up most often after a fresh Grafana install.

Grafana fails to start

Check the journal for detailed error messages:

sudo journalctl -u grafana-server -f

Common culprits include syntax errors in grafana.ini, incorrect file permissions on /var/lib/grafana, or port 3000 already being used by another process.

Port 3000 is already in use

Find the process occupying the port:

sudo ss -tlnp | grep 3000

Either stop the conflicting service or change Grafana’s port in grafana.ini under the [server] section by setting http_port = 3001 (or any free port).

Cannot connect to Prometheus data source

Verify that Prometheus is running and reachable from the Grafana server:

curl -s http://localhost:9090/api/v1/status/config | head -c 200

If Prometheus runs on a different host, check firewall rules and make sure the URL in Grafana matches the actual Prometheus listen address. When Prometheus is behind a reverse proxy, use the proxy URL instead.

Dashboard shows “No data”

This usually means the metric names in the dashboard do not match what your Prometheus instance actually scrapes. Open the panel, click on the query, and run it directly in the Prometheus expression browser at http://localhost:9090/graph to confirm the metric exists. Pay attention to label names. Dashboards like Node Exporter Full assume specific job labels (usually job="node"), which must match your prometheus.yml scrape config.

Nginx returns 502 Bad Gateway

This means Nginx cannot reach Grafana’s backend. Confirm Grafana is running and listening on the expected address. If you configured http_addr = 127.0.0.1, make sure the proxy_pass directive in Nginx points to http://127.0.0.1:3000 and not http://localhost:3000 (which may resolve to an IPv6 address on some systems).

Permission denied errors on provisioning files

Grafana runs as the grafana user. Make sure all files under /etc/grafana/provisioning/ and /var/lib/grafana/dashboards/ are owned by that user:

sudo chown -R grafana:grafana /etc/grafana/provisioning/
sudo chown -R grafana:grafana /var/lib/grafana/dashboards/

LDAP login fails

Enable LDAP debug logging by adding ldap_debug = true under [log] in grafana.ini. Restart Grafana and attempt a login. The log output at /var/log/grafana/grafana.log will show the exact bind and search operations, making it straightforward to spot mismatched DNs, bad credentials, or connectivity problems.

Wrapping Up

You now have a fully functional Grafana 11.x installation on Ubuntu 24.04 or Debian 13, backed by Prometheus, secured with Nginx and TLS, and configured for team access through LDAP or OAuth. The provisioning setup means your dashboards and data sources live in version control alongside your infrastructure code, which is exactly where they belong.

From here, you might look into setting up Grafana Loki for log aggregation, building SLO dashboards using recording rules in Prometheus, or exploring Grafana’s Scenes framework for building interactive app-like dashboards. The monitoring stack you have built in this guide is a solid foundation that will scale with your infrastructure.

LEAVE A REPLY

Please enter your comment!
Please enter your name here