Skip to main contentSkip to navigation
[email protected]
Client AreaSupport
Hosting Mammoth
HostingMammothYour Data, Our Responsibility
Home
Solutions
Hosting Services
Store
Pricing
About
Blog
API
Contact

Stay Ahead of the Curve

Get the latest insights on cybersecurity, AI innovations, and enterprise data solutions delivered to your inbox.

Hosting Mammoth
HostingMammothEnterprise Solutions

Enterprise-grade data solutions. Hosting, recovery, cybersecurity, and AI-powered services for businesses worldwide.

[email protected]
Sun - Fri, 9:00am - 5:00pm

Services

  • Cloud Hosting
  • Data Recovery
  • Cybersecurity
  • Legal Support
  • MSP Services
  • Web Development
  • AI Services
  • Free Server Migration

Hosting

  • VPS Hosting (NVMe SSD)
  • VDS Hosting (NVMe)
  • Storage VPS (High SSD)
  • GPU Servers
  • Managed Services
  • Cloud Firewall
  • Load Balancer
  • One-Click Apps
  • n8n Hosting
  • Object Storage
  • FAQ

Company

  • Store
  • Pricing
  • About Us
  • Locations
  • Blog
  • Testimonials
  • Contact
  • Affiliate Program
  • White-Label
  • Terms of Service
  • Privacy Policy
  • Browser Cookies
  • SLA

Support

  • Client Area
  • Submit Ticket
  • Knowledge Base
  • Server Status
  • API Documentation

© 2026 Hosting Mammoth. All rights reserved.

Knowledge Base
Getting StartedAccount ManagementVPS HostingGPU ServersStorage VPSCloud FirewallLoad BalancerServer ManagementBilling & PaymentsSupport & TicketsAffiliate ProgramReseller ProgramMarketplace & Appsn8n HostingManaged ServicesServer MigrationAPI & DevelopersSecurityTroubleshootingGlossaryInstall Guides
  1. Home
  2. /
  3. Support
  4. /
  5. Install Guides
  6. /
  7. How To Install Grafana Ubuntu
GUIDEInstall Guides

How to Install Grafana on Ubuntu 24.04 VPS — Complete Setup Guide

28 min read

How to Install Grafana on Ubuntu 24.04 VPS — Complete Setup Guide

Grafana is the de facto standard open-source dashboard for time-series data, logs, traces, and alerts. This guide walks you through a production-grade install on an Ubuntu 24.04 LTS VPS, from the first apt update to OAuth single sign-on, Nginx TLS, and connected datasources for Prometheus, Loki, and InfluxDB. Every step has been tested on the CloudCore Starter plan and scales cleanly up to larger tiers.

Looking for an affordable monitoring VPS? The CloudCore Starter plan at EUR 7.99/month runs Grafana plus a single-node Prometheus with ease. Upgrade paths are frictionless.

Table of Contents

  • What is Grafana?
  • Why Self-Host Grafana on a VPS?
  • Grafana vs. Grafana Cloud vs. Datadog
  • Prerequisites
  • Step 1: Update System Packages
  • Step 2: Add the Official Grafana APT Repository
  • Step 3: Install Grafana OSS
  • Step 4: Start and Enable the grafana-server Service
  • Step 5: Configure grafana.ini
  • Step 6: Add Datasources (Prometheus, Loki, InfluxDB)
  • Step 7: Import and Build Dashboards
  • Step 8: Configure Unified Alerting
  • Step 9: Enable OAuth Login (Google & GitHub)
  • Step 10: Put Grafana Behind an Nginx Reverse Proxy
  • Step 11: Harden with UFW
  • Troubleshooting
  • FAQ
  • Next Steps
  • What is Grafana?

    Grafana is an open-source analytics and visualization platform that lets you query, graph, alert on, and explore metrics, logs, and traces from dozens of backends. Instead of every data store shipping its own UI, Grafana becomes the single pane of glass — you connect it to a time-series database (like Prometheus, InfluxDB, VictoriaMetrics), a log aggregator (like Loki, Elasticsearch), or a traditional RDBMS (MySQL, PostgreSQL), and build dashboards that combine all of them in one view.

    Grafana's dashboarding model is panel-based. Each panel is a single visualization — a time series graph, a stat, a gauge, a table, a heatmap, a logs panel, a Geomap — bound to one or more queries. Variables let you build dashboards that swap hosts, environments, or Kubernetes namespaces with a dropdown. Annotations overlay deploy events and incidents directly on your charts. Explore mode gives you an ad-hoc query workbench with automatic autocomplete for PromQL, LogQL, and FlashQL.

    The Grafana ecosystem is sizeable. The official plugin catalog includes 150+ datasources and dozens of visualization plugins. The community dashboard library at grafana.com/grafana/dashboards lists 10,000+ ready-to-import dashboards covering everything from Node Exporter to Nginx, Cloudflare, MikroTik routers, and Kubernetes workloads.

    Typical use cases include: infrastructure monitoring (CPU, memory, disk, network across a fleet), application performance monitoring (request latency percentiles, error rates, queue depths), log analytics (tailing and querying structured logs from Loki or Elasticsearch), business dashboards (revenue, signups, cohort charts pulled from PostgreSQL), SLO/SLA tracking (error budget burn rates), and network observability (SNMP polling of switches via Zabbix or Telegraf).

    Why Self-Host Grafana on a VPS?

    Running Grafana on your own VPS instead of subscribing to a SaaS observability product has real advantages:

    • Flat-rate cost -- A VPS costs the same whether you ingest 1 GB or 1 TB of metrics. SaaS plans bill per series, per host, or per log GB, which can balloon unexpectedly.
    • Data sovereignty -- Metrics, logs, and dashboards stay on infrastructure you control. Critical for GDPR, HIPAA, or contracts that forbid shipping operational data to third parties.
    • Unlimited users -- Grafana OSS has no seat cap. Invite the whole engineering org, client stakeholders, or NOC staff with no per-user fees.
    • Unlimited dashboards, datasources, and alerts -- No artificial feature gates.
    • Full plugin access -- Install any community datasource or visualization plugin. Some enterprise-only plugins remain gated, but the OSS plugin catalog is large.
    • Integration with local infrastructure -- Scrape internal services on your VPS network without opening holes to external SaaS collectors.
    • Longer retention at lower cost -- Pair with Prometheus remote-write to VictoriaMetrics or Mimir for years of retention on cheap block storage, rather than paying per-GB-month SaaS retention tiers.

    Grafana vs. Grafana Cloud vs. Datadog

    FeatureSelf-Hosted Grafana OSSGrafana Cloud FreeGrafana Cloud ProDatadog
    Base costEUR 7.99-29.99/mo (VPS)FreeStarts ~USD 49/moStarts USD 15/host/mo
    Metrics cardinalityUnlimited (your storage)10k seriesPer-series billingPer-custom-metric billing
    Log ingestUnlimited (your storage)50 GBUSD 0.50/GBUSD 1.27/GB
    UsersUnlimited33 free, then paidPer-user billing
    DashboardsUnlimitedUnlimitedUnlimitedUnlimited
    PluginsOSS catalog + communityOSS + Enterprise pluginsOSS + Enterprise pluginsDatadog-only
    SLA / supportCommunityCommunity99.5%99.9% + 24x7
    Data locationYour VPSGrafana Labs cloudGrafana Labs cloudDatadog cloud
    Typical real-world monthly cost (5 hosts, ~30 dashboards, 100 GB logs)EUR 7.99N/A (over free limit)~USD 150-250~USD 300-500+
    For most small and mid-sized teams, self-hosted Grafana on a single Starter or Professional VPS is the clear cost winner. Grafana Cloud makes sense once you need managed scaling (billions of samples, TB-scale logs) and SLA guarantees. Datadog is the fullest-featured APM/infrastructure/log platform on the market but prices aggressively once you have more than a handful of hosts.

    CloudCore Pricing Tiers for Grafana Workloads

    TiervCPU / RAM / NVMeMonthlyGood For
    Starter2 / 4 GB / 50 GBEUR 7.99Single Grafana + small Prometheus or Loki, up to 20 dashboards
    Professional6 / 12 GB / 100 GBEUR 19.99Grafana + Prometheus + Loki + Alertmanager on one box
    Business8 / 24 GB / 200 GBEUR 39.99Multi-tenant Grafana, VictoriaMetrics, dozens of scraped targets
    Enterprise16 / 64 GB / 500 GB NVMeEUR 89.99Large orgs, long-retention metrics, heavy log ingest

    Prerequisites

    Before you begin, make sure you have:

    • A VPS running Ubuntu 24.04 LTS with root or sudo access
    • SSH access to the server
    • A domain name you can point at the server (for TLS and OAuth callbacks) — optional but strongly recommended
    • At least 2 GB of RAM (4 GB recommended for production)
    • At least 10 GB free disk for Grafana itself (Grafana's SQLite DB + logs + plugins stays small; the bulk of your storage will be consumed by your metrics/log backends)
    Recommended Plan: Starter
    >
    For a single-node Grafana with a small Prometheus or Loki alongside, the CloudCore Starter plan is a strong fit:
    >
    - 2 vCPU cores
    - 4 GB RAM
    - 50 GB NVMe SSD
    - Unmetered bandwidth
    - EUR 7.99/month
    >
    Grow into CloudCore Professional (6 vCPU, 12 GB RAM) when you add heavier backends like VictoriaMetrics or ship logs from multiple hosts.

    Connect to your server via SSH to get started:

    bash
    ssh root@your-server-ip

    Step 1: Update System Packages

    Update the package index and upgrade installed packages. This keeps dependency resolution clean and picks up any kernel/security patches.

    bash
    sudo apt update && sudo apt upgrade -y

    Install a handful of prerequisites used by the repository setup:

    bash
    sudo apt install -y apt-transport-https software-properties-common wget gnupg

    If the upgrade changed the kernel, reboot before continuing:

    bash
    sudo reboot

    Step 2: Add the Official Grafana APT Repository

    Grafana provides an APT repository with GPG-signed packages. Import the signing key and add the stable channel:

    bash
    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

    Add the repository definition:

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

    If you want the beta channel (for testing upcoming releases), add beta main in addition to or instead of the stable line.

    Refresh the package index:

    bash
    sudo apt update

    Expected output (abbreviated):

    text
    Get:1 https://apt.grafana.com stable InRelease [8,107 B]
    Get:2 https://apt.grafana.com stable/main amd64 Packages [...]
    Reading package lists... Done

    Step 3: Install Grafana OSS

    Install the open-source Grafana package:

    bash
    sudo apt install -y grafana

    Expected output (abbreviated):

    text
    The following NEW packages will be installed:
      grafana
    0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
    Need to get 95.4 MB of archives.
    ...
    Adding system user grafana' (UID 113) ...
    Adding new user grafana' (UID 113) with group grafana' ...</code></pre></div>

    The package installs:

    • Binary at /usr/sbin/grafana-server
    • CLI at /usr/sbin/grafana-cli
    • Configuration at /etc/grafana/grafana.ini
    • Data directory at /var/lib/grafana/ (including the default SQLite DB grafana.db)
    • Plugin directory at /var/lib/grafana/plugins/
    • Provisioning directory at /etc/grafana/provisioning/
    • Logs at /var/log/grafana/grafana.log
    • systemd unit at /lib/systemd/system/grafana-server.service
    Verify the installed version:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">grafana-server -v</code></pre></div>

    Expected output:

    <div class="code-block" data-lang="text"><div class="code-block__header"><span class="code-block__lang">text</span></div><pre><code class="language-text">Version 11.5.0 (commit: abc123, branch: HEAD)</code></pre></div>

    Step 4: Start and Enable the grafana-server Service

    Enable the service so it survives reboots, then start it:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">sudo systemctl daemon-reload sudo systemctl enable grafana-server sudo systemctl start grafana-server</code></pre></div>

    Check the status:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">sudo systemctl status grafana-server</code></pre></div>

    Expected output:

    <div class="code-block" data-lang="text"><div class="code-block__header"><span class="code-block__lang">text</span></div><pre><code class="language-text">● grafana-server.service - Grafana instance Loaded: loaded (/lib/systemd/system/grafana-server.service; enabled; preset: enabled) Active: active (running) since Wed 2026-04-16 10:00:00 UTC; 3s ago Main PID: 1234 (grafana) Tasks: 9 Memory: 68.0M CGroup: /system.slice/grafana-server.service └─1234 /usr/sbin/grafana server --config=/etc/grafana/grafana.ini ...</code></pre></div>

    Grafana listens on port 3000 by default. Visit http://your-server-ip:3000 in a browser. The default login is admin / admin. You will be forced to set a new password on first login — pick a strong one.

    Step 5: Configure grafana.ini

    The master config lives at /etc/grafana/grafana.ini. It is 1,500+ lines of commented defaults; rather than edit it in place, we recommend dropping override files into /etc/grafana/grafana.ini.d/ or editing only the sections below.

    Open the config:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">sudo nano /etc/grafana/grafana.ini</code></pre></div>

    At minimum, change these sections:

    <div class="code-block" data-lang="ini"><div class="code-block__header"><span class="code-block__lang">ini</span></div><pre><code class="language-ini">[server] protocol = http http_port = 3000 domain = grafana.yourdomain.com root_url = https://grafana.yourdomain.com/ enforce_domain = true

    [security] admin_user = admin

    Set a strong initial password here, or set GF_SECURITY_ADMIN_PASSWORD via systemd env

    cookie_secure = true cookie_samesite = lax strict_transport_security = true

    [users] allow_sign_up = false auto_assign_org = true auto_assign_org_role = Viewer

    [auth.anonymous] enabled = false

    [smtp] enabled = true host = smtp.yourmailhost.com:587 user = [email protected] password = your-smtp-password from_address = [email protected] from_name = Grafana

    [log] mode = console file level = info

    [analytics] reporting_enabled = false check_for_updates = true</code></pre></div>

    Key settings explained:

    • root_url — Must match the public URL users hit. Wrong values break OAuth callbacks, image rendering, and embedded panels.
    • enforce_domain = true — Redirects requests that come in via IP to the configured domain.
    • cookie_secure = true — Required once you are on HTTPS; prevents session cookies from leaking over plain HTTP.
    • allow_sign_up = false — Blocks random internet visitors from registering local accounts.
    • [auth.anonymous] enabled = false — Requires login for every dashboard unless you explicitly want public dashboards.
    • [smtp] — Needed for password reset emails and SMTP-based alert notifications.
    Apply by restarting:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">sudo systemctl restart grafana-server</code></pre></div>

    Tail the log to catch any config errors:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">sudo tail -f /var/log/grafana/grafana.log</code></pre></div>

    Step 6: Add Datasources (Prometheus, Loki, InfluxDB)

    Grafana is useless without a datasource. You can add them in the UI under Connections -> Data sources, or declaratively via provisioning YAML.

    Option A: UI

  • Log in at http://your-server-ip:3000.
  • Left sidebar -> Connections -> Data sources -> Add data source.
  • Pick the datasource type.
  • Fill in the URL and auth, click Save & test.
  • Option B: Provisioning (Recommended)

    Create /etc/grafana/provisioning/datasources/datasources.yaml:

    <div class="code-block" data-lang="yaml"><div class="code-block__header"><span class="code-block__lang">yaml</span></div><pre><code class="language-yaml">apiVersion: 1

    datasources: - name: Prometheus type: prometheus access: proxy url: http://localhost:9090 isDefault: true jsonData: timeInterval: 15s httpMethod: POST

    - name: Loki type: loki access: proxy url: http://localhost:3100 jsonData: maxLines: 5000 derivedFields: - datasourceUid: prometheus matcherRegex: &quot;traceID=(\\w+)&quot; name: TraceID url: &quot;$${__value.raw}&quot;

    - name: InfluxDB type: influxdb access: proxy url: http://localhost:8086 jsonData: version: Flux organization: yourorg defaultBucket: telegraf tlsSkipVerify: false secureJsonData: token: YOUR_INFLUX_TOKEN</code></pre></div>

    Restart Grafana to pick up provisioning changes:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">sudo systemctl restart grafana-server</code></pre></div>

    Choosing a Metrics Backend

    <div class="article-table-wrap"><table><thead><tr><th>Backend</th><th>Best For</th><th>Strengths</th><th>Considerations</th></tr></thead><tbody><tr><td><a href="/kb/install-guides/how-to-install-prometheus-ubuntu">Prometheus</a></td><td>Infra/app metrics, Kubernetes</td><td>Pull model, service discovery, PromQL</td><td>Single-node; pair with Thanos/Mimir for HA</td></tr><tr><td><a href="/kb/install-guides/how-to-install-victoriametrics-ubuntu">VictoriaMetrics</a></td><td>High-cardinality, long retention</td><td>10x+ more efficient than Prometheus</td><td>Less community ecosystem</td></tr><tr><td><a href="/kb/install-guides/how-to-install-influxdb-ubuntu">InfluxDB</a></td><td>IoT, push model, Flux queries</td><td>Great with Telegraf</td><td>v2/v3 migration path is bumpy</td></tr><tr><td><a href="/kb/install-guides/how-to-install-zabbix-ubuntu">Zabbix</a></td><td>Traditional NMS, SNMP, triggers</td><td>Built-in agent + vast template library</td><td>Grafana plugin is read-only</td></tr></tbody></table></div> For logs, the obvious Grafana-native pairing is Loki. Loki uses LogQL (a close cousin of PromQL), stores compressed log streams cheaply, and integrates into the Grafana Explore view next to your metrics.

    Step 7: Import and Build Dashboards

    Import a Community Dashboard by ID

    The fastest way to get value from Grafana is to import a pre-built dashboard:

  • Left sidebar -> Dashboards -> New -> Import.
  • Enter the dashboard ID from grafana.com/grafana/dashboards — for example:
  • - 1860 — Node Exporter Full (host metrics) - 3662 — Prometheus 2.0 Overview - 12708 — Nginx exporter - 13639 — Logs via Loki (basic) - 15277 — Cadvisor Exporter
  • Click Load, pick your Prometheus datasource, Import.
  • Provision Dashboards from JSON

    For reproducible deployments, provision dashboards the same way you provision datasources.

    Create /etc/grafana/provisioning/dashboards/dashboards.yaml:

    <div class="code-block" data-lang="yaml"><div class="code-block__header"><span class="code-block__lang">yaml</span></div><pre><code class="language-yaml">apiVersion: 1

    providers: - name: 'default' orgId: 1 folder: '' type: file disableDeletion: false updateIntervalSeconds: 30 allowUiUpdates: true options: path: /var/lib/grafana/dashboards</code></pre></div>

    Drop dashboard JSON files into /var/lib/grafana/dashboards/ (create the directory first, ensure grafana:grafana ownership). Grafana reloads them every 30 seconds.

    Build a Custom Dashboard

    Click Dashboards -> New -> New dashboard -> Add visualization. Pick a datasource, write a query (PromQL, LogQL, InfluxQL, Flux, or SQL depending on the source), choose a visualization type on the right panel, and set thresholds, units, and legend formatting. Use $__rate_interval in PromQL rate() calls for dashboards that work across different zoom levels.

    Step 8: Configure Unified Alerting

    Grafana's unified alerting (the default since v9) evaluates alert rules against any datasource and routes notifications through a built-in Alertmanager-compatible engine — or an external Alertmanager if you prefer.

    Create a Contact Point

  • Alerting -> Contact points -> Add contact point.
  • Pick a type: Email, Slack, Telegram, Discord, PagerDuty, Opsgenie, or generic Webhook.
  • For email, Grafana uses the SMTP credentials you configured in grafana.ini.
  • Create a Notification Policy

    Notification policies route alerts to contact points based on labels. The default policy catches everything; add nested policies that match on severity=critical to escalate to on-call, and team=frontend to route to the right channel.

    Create an Alert Rule

  • Alerting -> Alert rules -> New alert rule.
  • Set a query (e.g. avg(rate(node_cpu_seconds_total{mode!="idle"}[5m])) by (instance) > 0.9 for high CPU).
  • Set evaluation interval (1m) and the "for" duration (5m) — the rule must be breaching for 5 minutes before firing, which suppresses noise.
  • Add labels like severity=critical, team=platform.
  • Add an annotation summary: CPU at {{ $values.B.Value }} on {{ $labels.instance }}.
  • Pick the folder and evaluation group, then Save and exit.
  • For larger setups, delegate routing and silencing to a standalone Alertmanager instance and point Grafana at it via Alerting -> Admin -> Alertmanager.

    Step 9: Enable OAuth Login (Google & GitHub)

    Password login is fine for a one-person setup, but for teams, OAuth is the right answer. You can configure Google, GitHub, GitLab, Okta, Auth0, Microsoft Entra, or any generic OIDC provider.

    Google OAuth

  • In Google Cloud Console, create an OAuth 2.0 Client ID of type "Web application".
  • Authorized redirect URI: https://grafana.yourdomain.com/login/google.
  • Copy the Client ID and Client Secret.
  • Add to grafana.ini:
  • <div class="code-block" data-lang="ini"><div class="code-block__header"><span class="code-block__lang">ini</span></div><pre><code class="language-ini">[auth.google] enabled = true name = Google client_id = YOUR_CLIENT_ID.apps.googleusercontent.com client_secret = YOUR_CLIENT_SECRET scopes = openid email profile auth_url = https://accounts.google.com/o/oauth2/v2/auth token_url = https://oauth2.googleapis.com/token api_url = https://openidconnect.googleapis.com/v1/userinfo allowed_domains = yourdomain.com allow_sign_up = true auto_login = false</code></pre></div>

    The allowed_domains directive restricts login to users whose Google email is at your company domain — essential for shared-tenant Google Workspace environments.

    GitHub OAuth

  • In GitHub -> Settings -> Developer settings -> OAuth Apps -> New OAuth App.
  • Authorization callback URL: https://grafana.yourdomain.com/login/github.
  • Copy the Client ID, generate a new Client Secret.
  • Add to grafana.ini:
  • <div class="code-block" data-lang="ini"><div class="code-block__header"><span class="code-block__lang">ini</span></div><pre><code class="language-ini">[auth.github] enabled = true name = GitHub client_id = YOUR_GITHUB_CLIENT_ID client_secret = YOUR_GITHUB_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 team_ids = allow_sign_up = true</code></pre></div>

    allowed_organizations gates login to members of your GitHub org. Use team_ids for finer control.

    Restart Grafana to pick up the OAuth config:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">sudo systemctl restart grafana-server</code></pre></div>

    Visit the login page — you should see new "Sign in with Google" and "Sign in with GitHub" buttons.

    Step 10: Put Grafana Behind an Nginx Reverse Proxy

    Nginx in front of Grafana gives you TLS, HTTP/2, request buffering, and the ability to firewall port 3000 off the public internet entirely.

    Install Nginx and Certbot:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">sudo apt install -y nginx certbot python3-certbot-nginx</code></pre></div>

    Create the site config at /etc/nginx/sites-available/grafana:

    <div class="code-block" data-lang="nginx"><div class="code-block__header"><span class="code-block__lang">nginx</span></div><pre><code class="language-nginx">upstream grafana { server 127.0.0.1:3000; }

    server { listen 80; server_name grafana.yourdomain.com; return 301 https://$host$request_uri; }

    server { listen 443 ssl http2; server_name grafana.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/grafana.yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/grafana.yourdomain.com/privkey.pem;

    ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5;

    add_header Strict-Transport-Security &quot;max-age=31536000; includeSubDomains&quot; always; add_header X-Content-Type-Options nosniff; add_header X-Frame-Options SAMEORIGIN;

    client_max_body_size 20m;

    location / { proxy_pass http://grafana; 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; }

    # Grafana Live uses WebSockets — required for live dashboards and tail logs location /api/live/ { proxy_pass http://grafana; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection &quot;upgrade&quot;; 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; proxy_read_timeout 3600s; } }</code></pre></div>

    Enable it and test the config:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">sudo ln -s /etc/nginx/sites-available/grafana /etc/nginx/sites-enabled/ sudo nginx -t</code></pre></div>

    Before reloading Nginx, point your DNS A record for grafana.yourdomain.com at your server's IP. Then issue a Let's Encrypt certificate:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">sudo certbot --nginx -d grafana.yourdomain.com</code></pre></div>

    Certbot will rewrite the config if needed, install the certificate, and set up automatic renewal via a systemd timer. Reload Nginx:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">sudo systemctl reload nginx</code></pre></div>

    Now browse to https://grafana.yourdomain.com. Confirm the padlock, and that /api/live/ works by opening a dashboard with a live tail panel.

    Because Grafana is now fronted by a proxy, tell it so in grafana.ini:

    <div class="code-block" data-lang="ini"><div class="code-block__header"><span class="code-block__lang">ini</span></div><pre><code class="language-ini">[server] protocol = http http_addr = 127.0.0.1 http_port = 3000 domain = grafana.yourdomain.com root_url = https://grafana.yourdomain.com/ serve_from_sub_path = false</code></pre></div>

    Binding to 127.0.0.1 ensures the Grafana web server is unreachable from the public internet — only Nginx on the same host can reach it.

    Restart Grafana:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">sudo systemctl restart grafana-server</code></pre></div>

    Step 11: Harden with UFW

    UFW (Uncomplicated Firewall) is the simplest way to lock down Ubuntu. Allow SSH and the Nginx ports, deny everything else:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">sudo ufw default deny incoming sudo ufw default allow outgoing sudo ufw allow OpenSSH sudo ufw allow 'Nginx Full' sudo ufw enable sudo ufw status verbose</code></pre></div>

    Expected output:

    <div class="code-block" data-lang="text"><div class="code-block__header"><span class="code-block__lang">text</span></div><pre><code class="language-text">Status: active Logging: on (low) Default: deny (incoming), allow (outgoing), disabled (routed)

    To Action From -- ------ ---- 22/tcp (OpenSSH) ALLOW IN Anywhere Nginx Full ALLOW IN Anywhere</code></pre></div>

    Port 3000 is not open to the public — only Nginx's 127.0.0.1:3000 proxy connection reaches Grafana. Similarly, if Prometheus (9090), Loki (3100), or InfluxDB (8086) run on this server, their ports stay closed; Grafana talks to them over localhost.

    If a colleague needs temporary access from a specific IP for debugging:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">sudo ufw allow from 203.0.113.42 to any port 3000 proto tcp</code></pre></div>

    Troubleshooting

    <div class="article-table-wrap"><table><thead><tr><th>Problem</th><th>Cause</th><th>Solution</th></tr></thead><tbody><tr><td><code>502 Bad Gateway</code> from Nginx</td><td>grafana-server not running or wrong upstream port</td><td><code>sudo systemctl status grafana-server</code>, check <code>/var/log/grafana/grafana.log</code>, verify <code>http_port</code> and upstream match</td></tr><tr><td>OAuth login loops or "redirect_uri mismatch"</td><td><code>root_url</code> in grafana.ini does not match OAuth callback</td><td>Set <code>root_url = https://grafana.yourdomain.com/</code> exactly, restart Grafana, update the OAuth app callback URL</td></tr><tr><td>Live dashboards / log tail stuck spinning</td><td>Nginx not proxying WebSockets on <code>/api/live/</code></td><td>Add the dedicated <code>location /api/live/</code> block with Upgrade/Connection headers</td></tr><tr><td>"Datasource is working" but panels show No Data</td><td>Datasource URL wrong, or time range outside ingested data</td><td>Check URL via curl from the Grafana host, widen the dashboard time range, verify datasource returns samples with <code>curl http://localhost:9090/api/v1/query?query=up</code></td></tr><tr><td>Password reset emails never arrive</td><td>SMTP misconfig or <code>[smtp] enabled = false</code></td><td>Tail <code>/var/log/grafana/grafana.log</code> during the request, verify SMTP credentials with <code>swaks</code> or similar</td></tr><tr><td><code>error=&quot;open /var/lib/grafana/grafana.db: permission denied&quot;</code></td><td>File perms on data dir</td><td><code>sudo chown -R grafana:grafana /var/lib/grafana</code></td></tr><tr><td>Plugins fail to install with <code>unsupported operating system</code></td><td>Running grafana-cli as wrong user or behind a proxy</td><td><code>sudo grafana-cli plugins install &lt;id&gt;</code>; for proxies, export <code>HTTP_PROXY</code> before running</td></tr><tr><td>Dashboards imported but show wrong datasource</td><td>Hard-coded datasource UID in JSON</td><td>Edit the JSON, replace the datasource UID with <code>${DS_PROMETHEUS}</code>, and define the variable in Dashboard Settings -> Variables</td></tr><tr><td>Cannot log in after enabling OAuth and <code>allow_sign_up = false</code></td><td>Local account disabled, OAuth user has no mapped org</td><td>Re-enable local login temporarily with <code>GF_AUTH_DISABLE_LOGIN_FORM=false</code>, log in as admin, then map users via <code>[users] auto_assign_org_role</code></td></tr></tbody></table></div>

    Viewing Logs

    The primary log stream is /var/log/grafana/grafana.log. Tail it live:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">sudo tail -f /var/log/grafana/grafana.log</code></pre></div>

    Or via journald if you prefer:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">sudo journalctl -u grafana-server -f</code></pre></div>

    Raise verbosity temporarily by setting level = debug in the [log] section of grafana.ini, then restart.

    FAQ

    What are the minimum hardware requirements to run Grafana?

    Grafana itself is lightweight. A 2 vCPU, 2 GB RAM VPS runs Grafana OSS comfortably with a handful of datasources and dashboards. For production workloads with 10+ users, 20+ dashboards, and high-cardinality Prometheus queries, we recommend 2 vCPU / 4 GB RAM / 50 GB NVMe as a baseline. Remember that Grafana's resource footprint is dominated by concurrent dashboard queries; heavy panels that fan out into dozens of Prometheus instant queries can spike CPU briefly.

    Is Grafana free to self-host?

    Yes. Grafana OSS is open source under the AGPLv3 license and free to self-host on your own VPS with no user limits, dashboard limits, or feature flags. Grafana Cloud and Grafana Enterprise are commercial offerings with additional features (reporting, enterprise SSO, enterprise-only datasources like Oracle and Splunk, and managed backend services), but the core product covered in this guide costs nothing beyond your VPS.

    Can I use Grafana without Prometheus?

    Absolutely. Grafana supports dozens of datasources including InfluxDB, Loki, Elasticsearch, MySQL, PostgreSQL, CloudWatch, Azure Monitor, Google Cloud Monitoring, Tempo, Zabbix, TimescaleDB, and many more. Prometheus is the most popular pairing for metrics because it is open source, pull-based, and has best-in-class service discovery, but you can start with any supported backend. Many teams run InfluxDB + Telegraf for push-based metrics, Zabbix for traditional SNMP monitoring of networking gear, or straight PostgreSQL for business dashboards.

    How does Grafana compare to Grafana Cloud and Datadog?

    Self-hosted Grafana on a VPS runs at a flat monthly cost (typically EUR 7.99 to 29.99/month) with unlimited metrics, logs, users, and dashboards; the only growth cost is upgrading your VPS or adding storage. Grafana Cloud's free tier includes 10k metric series and 50 GB logs, then charges per active series and per ingested GB; it makes sense once you need managed HA, multi-tenant scale, or Grafana's enterprise plugins without running them yourself. Datadog is the most feature-complete APM + infrastructure + log platform on the market but bills per host, per custom metric, per log event, and per APM span; small teams typically reach hundreds of USD/month within the first year.

    Should I put Grafana behind a reverse proxy?

    Yes, for any production deployment. Grafana's built-in web server does not terminate TLS out of the box, and Nginx in front gives you Let's Encrypt certificates, HTTP/2, gzip/brotli compression, rate limiting, request buffering, and a single public entry point that you can firewall. It also lets you host Grafana alongside other apps on the same domain (e.g. monitoring.yourdomain.com -> Grafana, logs.yourdomain.com -> a Loki explorer) with a clean routing layer.

    How do I back up Grafana?

    Back up /var/lib/grafana/grafana.db (the default SQLite database containing users, dashboards, datasource config, API keys, and alert rules) plus /etc/grafana/ for the grafana.ini and provisioning YAML. Stop grafana-server briefly or use sqlite3 grafana.db ".backup /tmp/grafana.db.bak" for a consistent snapshot. For high availability, migrate from SQLite to PostgreSQL or MySQL (set [database] type = postgres in grafana.ini) and rely on your database server's standard backup tooling. Dashboards can also be exported as JSON and version-controlled in Git for reproducibility.

    How do I upgrade Grafana?

    Because you installed from the APT repository, upgrades are a routine apt operation:

    <div class="code-block" data-lang="bash"><div class="code-block__header"><span class="code-block__lang">bash</span></div><pre><code class="language-bash">sudo apt update sudo apt install --only-upgrade grafana sudo systemctl restart grafana-server</code></pre></div>

    Always read the release notes at grafana.com/docs/grafana/latest/whatsnew/ before jumping major versions — Grafana 9 introduced unified alerting (migrating legacy alerts), Grafana 10 dropped several deprecated APIs, and Grafana 11 tightened auth defaults.

    Can I run Grafana in Docker instead?

    Yes. docker run -d -p 3000:3000 --name grafana -v grafana-storage:/var/lib/grafana grafana/grafana-oss` gives you a working instance in under a minute. The apt install covered in this guide is preferred for production single-host setups because systemd integration, log rotation, and package upgrades are all familiar Ubuntu workflows. Docker shines for ephemeral test environments, multi-instance setups behind Traefik, or when you are already deep in Docker Compose.

    Next Steps

    With Grafana up and running on your VPS, these are the highest-leverage follow-ups:

    • Install Prometheus — The classic pairing for infrastructure metrics. Add Node Exporter on every host to feed CPU, memory, disk, network, and process stats into Grafana.
    • Install Loki — Grafana-native log aggregation. Pair with Promtail or Vector to ship logs from every host; query with LogQL in the same Explore view as your metrics.
    • Install InfluxDB — Push-based time-series database. Perfect if you are using Telegraf for IoT, network, or application metrics.
    • Install VictoriaMetrics — A drop-in Prometheus remote-write target that handles 10x+ higher cardinality at a fraction of the RAM. The path forward when your Prometheus starts struggling.
    • Install Zabbix — Traditional network monitoring with SNMP, agents, and trigger-based alerts. Its Grafana plugin lets you embed Zabbix panels alongside Prometheus metrics.
    • Install Alertmanager — Standalone Alertmanager for large deployments, with deduplication, grouping, silencing, and routing into Slack, PagerDuty, Opsgenie, and webhooks.
    • Install Nginx properly — If you skipped the reverse proxy step, revisit it. Nginx pays for itself the first time you need to host a second service on the same box.
    • Read the Grafana documentation — The official docs are thorough, well-organized, and kept up to date with every release.

    Prefer a clean Ubuntu base to deploy on?
    >
    Our CloudCore Starter VPS (EUR 7.99/month) gives you a clean Ubuntu 24.04 box ready for this install. Grow into Professional, Business, or Enterprise tiers as your metrics footprint grows — same OS image, bigger engine.
    >
    - 2 vCPU / 4 GB RAM / 50 GB NVMe
    - Unmetered bandwidth
    - Root SSH, snapshots, rebuilds
    - Data center choices in the EU, US, and APAC
    >
    Deploy Your VPS — and have Grafana live in under 30 minutes.

    Was this article helpful?

    ← Back to Install GuidesBrowse all categories →

    Still have questions?

    Contact Support →Submit a Ticket