Skip to content

How to Deploy Grafana on a VPS

Updated Aug 2026

Self-host Grafana on your own VPS — the standard open-source dashboarding layer for metrics, logs, and traces, querying Prometheus, Loki, InfluxDB, and dozens of other data sources.

Before you start
  • A VPS with 1+ GB RAM
  • A metrics or logs data source (e.g. Prometheus) — or use Grafana's built-in SQL sources
  • A fresh Ubuntu 24.04 or 26.04 server with root/sudo SSH access

What Grafana is

Grafana is the standard open-source dashboarding layer for metrics, logs, and traces. It doesn't collect data itself — it connects: Prometheus for metrics, Loki for logs, InfluxDB, Postgres, and dozens of other sources plug in as data sources, and Grafana turns them into unified dashboards and alerting in one pane of glass.

The appeal of self-hosting it is control and breadth: no per-user SaaS seat costs, your queries and dashboards live on your own box, and one Grafana instance can visualize everything from server health to application business metrics. It's a Go backend with a TypeScript frontend, rated 2 / 5 to deploy, and comfortable on 1 GB of RAM — a light, reliable service that runs on a modest VPS for years.

Server sizing — Grafana is light, the data sources aren't

Grafana itself needs surprisingly little. It stores dashboards and users in an embedded database and spends most of its time serving queries over the network.

Standalone sizing:

  • 1 GB RAM — plenty for dashboards, users, and alerting on a small setup
  • 2 GB RAM — if you're running many concurrent dashboard users or large queries against heavy data sources

Running data sources on the same box: add the backends on top. A Prometheus server wants another 1–2 GB, Loki similar, so a combined observability VPS is happier at 2–4 GB total. A Hetzner CX22 (2 vCPU / 4 GB) runs Grafana plus Prometheus and Loki comfortably.

Prepare the server

Start from a fresh Ubuntu 24.04 or 26.04 server. Update and create a non-root user:

apt update && apt upgrade -y
adduser deploy
usermod -aG sudo deploy

Lock down the firewall:

ufw allow OpenSSH
ufw allow 80
ufw allow 443
ufw enable

Install Docker:

curl -fsSL https://get.docker.com | sh
usermod -aG docker deploy

Log out and back in as deploy so the docker group takes effect.

Install Grafana

Create a working directory:

mkdir ~/grafana && cd ~/grafana

Create a compose file:

services:
  grafana:
    image: grafana/grafana-oss:latest
    restart: unless-stopped
    ports:
      - "127.0.0.1:3000:3000"
    volumes:
      - grafana_data:/var/lib/grafana

volumes:
  grafana_data:

The 127.0.0.1 bind keeps Grafana private — a reverse proxy handles public access, which also protects you from an open, default-admin dashboard on the open internet.

Start it:

docker compose up -d

Grafana listens on port 3000.

Connect a data source

Grafana is only as useful as the data behind it. Add a first source via Connections → Data sources → Add data source. The classic pairing is Prometheus; if you don't have one yet, the quick container version in its own directory:

mkdir ~/prometheus && cd ~/prometheus
cat > compose.yaml <<EOF
services:
  prometheus:
    image: prom/prometheus:latest
    restart: unless-stopped
    ports:
      - "127.0.0.1:9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - prometheus_data:/prometheus
volumes:
  prometheus_data:
EOF

Then reload Prometheus from your host as needed (or keep it static with a few scrape targets in prometheus.yml), start it, and add it as a data source in Grafana at http://127.0.0.1:9090. Grafana queries the data source over the Docker network or host network, so the URL must resolve from Grafana's container — on the same server, http://host.docker.internal:9090 is the reliable pick.

HTTPS + domain

Grafana carries login credentials and, increasingly, alerts — it needs HTTPS in production. Point a reverse proxy at 127.0.0.1:3000 and terminate TLS on 443.

The simplest path is Automatic HTTPS with Caddy. Point an A record for your hostname (say monitor.example.com) at the server's public IP, then have Caddy reverse-proxy that hostname to 127.0.0.1:3000.

If you use the Caddy container approach, put Grafana and Caddy in the same compose file and proxy to the Grafana service name:

reverse_proxy grafana:3000

First login and hardening

Load https://monitor.example.com. On a fresh install, the default credentials are admin / admin — Grafana forces a password change on first login.

Change it immediately. An unauthenticated Grafana is a read of all your metrics and, with alerting enabled, a foothold. After that:

  1. Set the admin email under Administration → Users → Main admin, so password resets reach you.
  2. Create a data source and a first dashboard to confirm the chain works.
  3. Turn off anonymous access unless you want public dashboards (Configuration → Users → Anonymous).

Backups

The important data — dashboards, users, and data-source configs — lives in the Grafana volume:

docker run --rm -v grafana_data:/data -v $(pwd):/backup alpine \
  tar czf /backup/grafana-$(date +%F).tar.gz -C /data .

This backs up everything you've configured. Restore it to a fresh Grafana instance and your dashboards come back. For an even simpler safety net, use a provider's daily snapshot (a Hetzner CX22 at $4.59/mo includes backups) — but note snapshots restore the whole box, not just Grafana.

Upgrades

Pull the newer image and recreate:

docker compose pull
docker compose up -d

Grafana runs migrations automatically on startup. Check the Grafana releases page before a major version bump, since plugin and dashboard schema changes occasionally need attention.

Troubleshooting

Dashboard loads but no data. The data source isn't reachable from the Grafana container. Verify the URL resolves from inside the container — on the same host, prefer http://host.docker.internal:PORT over 127.0.0.1, which would point at the container itself.

Can't log in / forgotten admin password. Grafana resets the admin password from the GF_SECURITY_ADMIN_PASSWORD environment variable on startup; set it in the compose file, recreate the container, log in, then change it in the UI.

Alerts not firing. Check that alerting is enabled for the data source and that the alert's evaluation interval is set. Test with a query you know returns data.

Page loads slowly. Usually a heavy query against a remote data source. Add Grafana's caching or point it at a closer/leaner backend before sizing up the VPS.

Verification + next steps

You're done when you can: load the dashboard over HTTPS, log in with a strong password, add a data source, build a dashboard that shows live data, and create an alert. You're ready to use it as the one window over your whole fleet.

From here, add Loki for logs, Prometheus node_exporter agents on your other servers, or the Netdata agent — Grafana vs Netdata explains how the two fit together. For a monitoring-first VPS, see Best VPS for Monitoring & Uptime for the ranked picks.

Next steps

Search SelfHost Atlas

Search apps, comparisons, guides, and categories.

We use analytics cookies (Google Analytics, PostHog) to see which guides are useful. No ad networks, no cross-site tracking. See our privacy policy.