For most teams the honest answer is that Grafana Cloud’s free plan covers you until you outgrow 3 users or 10,000 active metric series, and after that a $6 to $12 per month VPS running the open source build is dramatically cheaper than the Pro plan. Grafana itself is light. The expensive part of any observability stack is never the dashboard layer, it is the metrics store behind it. So the real Grafana hosting question is where your data lives, not where the web UI runs.
If you would rather let a shared host handle the install for you, we have a separate walkthrough on installing Grafana on Hostinger that covers the panel driven route. Grafana OSS is a single Go binary with a small embedded database. It renders dashboards, evaluates alert rules and proxies queries out to data sources. That means the hosting decision splits cleanly in two: where the Grafana server runs, and where the time series data it queries is stored. You can absolutely self host Grafana and point it at Grafana Cloud metrics, or run Grafana Cloud and point it at a Prometheus you own. This guide walks both halves.
What the Grafana Cloud free tier actually includes
Grafana Labs publishes the free plan limits on its pricing page, and they are worth reading before you commit. The free plan is genuinely usable for a homelab or a small production stack, but three of the limits bite quickly.
| Resource | Free plan limit | Retention | What blows past it |
|---|---|---|---|
| Metrics | 10,000 active series per month | 14 days | One Kubernetes cluster with cAdvisor and kube state metrics |
| Logs | 50 GB ingested per month | 14 days | A chatty Nginx access log at high traffic |
| Traces | 50 GB ingested per month | 14 days | Unsampled tracing on a busy API |
| Users | 3 active users per month | n/a | A fourth engineer opening a dashboard once |
The user cap is the one that surprises people. Grafana counts an active user as anyone who signs in during the billing month, so a manager who checks a dashboard in week three counts the same as your on call engineer. On the Pro plan visualization seats are billed per active user above the three included, which turns a nine person team into a recurring line item very quickly.
http_requests_total metric with 4 labels of 10 values each is 10,000 series on its own. Cardinality, not scrape count, decides your bill.Resource requirements for self hosted Grafana
Grafana’s own installation documentation puts the absolute floor at 512 MB of memory and 1 CPU core, which is fine for kicking the tires and nothing else. For a real deployment the guidance scales like this.
| Deployment size | CPU | Memory | Disk | Backend database |
|---|---|---|---|---|
| Evaluation | 1 core | 512 MB | 10 GB | SQLite 3 |
| Small | 2 cores | 2 to 4 GB | 10 to 20 GB SSD | SQLite or Postgres |
| Medium | 4 to 8 cores | 8 to 16 GB | 20 to 50 GB SSD | MySQL 8.0+ or PostgreSQL 12+ |
Note the database column. Grafana defaults to SQLite, which is fine for a single instance, but Grafana’s docs are explicit that SQLite is not recommended for production and that high availability requires MySQL or PostgreSQL. If you plan to run two Grafana replicas behind a load balancer, plan for an external database from day one.
Self hosting Grafana with Docker Compose
This is the setup I reach for on a small VPS. It runs Grafana alongside a Prometheus instance, persists both to named volumes, and keeps Grafana bound to localhost so only the reverse proxy can reach it.
services:
prometheus:
image: prom/prometheus:v3.14.0
restart: unless-stopped
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prom_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.retention.time=30d'
ports:
- "127.0.0.1:9090:9090"
grafana:
image: grafana/grafana-oss:13.2.1
restart: unless-stopped
depends_on:
- prometheus
environment:
GF_SERVER_ROOT_URL: "https://metrics.example.com"
GF_SECURITY_ADMIN_PASSWORD__FILE: /run/secrets/gf_admin
GF_USERS_ALLOW_SIGN_UP: "false"
GF_ANALYTICS_REPORTING_ENABLED: "false"
volumes:
- grafana_data:/var/lib/grafana
secrets:
- gf_admin
ports:
- "127.0.0.1:3000:3000"
volumes:
prom_data:
grafana_data:
secrets:
gf_admin:
file: ./secrets/grafana_admin_passwordBring it up with docker compose up -d and confirm both containers are healthy with docker compose ps. Two details matter here. Pinning image tags to an exact version stops a surprise major upgrade the next time you pull. Binding the published ports to 127.0.0.1 means neither service is reachable from the internet even if your firewall rules drift, which is a mistake I have seen cost people a public Prometheus endpoint.
grafana_data volume holds your SQLite database, which contains dashboards, users, API keys and alert rules. Back it up. Losing that volume means rebuilding every dashboard by hand.Reverse proxy and TLS
Grafana can terminate TLS itself, but putting Nginx or Caddy in front gives you certificate renewal, HTTP to HTTPS redirects and a place to add rate limiting. A minimal Nginx server block looks like this.
server {
listen 443 ssl;
http2 on;
server_name metrics.example.com;
ssl_certificate /etc/letsencrypt/live/metrics.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/metrics.example.com/privkey.pem;
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;
}
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;
}
}The second location block is the one everyone forgets. Grafana Live uses WebSockets, and without the upgrade headers you get a dashboard that loads but never streams. Issue the certificate with certbot --nginx -d metrics.example.com and Let’s Encrypt handles renewal through the systemd timer certbot installs. If you would rather avoid the proxy config entirely, Caddy will fetch and renew certificates automatically from a three line Caddyfile.
Authentication once more than one person uses it
The default admin account is fine for a week. After that, set GF_USERS_ALLOW_SIGN_UP=false, which the compose file above already does, and wire up a real identity provider. Grafana OSS supports generic OAuth, GitHub, GitLab, Google and Azure AD without an Enterprise license. SAML and team sync are Enterprise features, which is one of the few genuinely hard walls between the free build and the paid product.
[auth.github]
enabled = true
allow_sign_up = true
client_id = YOUR_CLIENT_ID
client_secret = YOUR_CLIENT_SECRET
scopes = user:email,read:org
allowed_organizations = your-github-org
role_attribute_path = contains(groups[*], 'your-github-org:sre') && 'Admin' || 'Viewer'Drop that into grafana.ini or set the equivalent GF_AUTH_GITHUB_* environment variables. The allowed_organizations line is what stops any GitHub account on the internet from signing in once you enable sign up. If you are running this alongside a Kubernetes cluster, our walkthrough on installing Helm in Minikube covers the chart based route where these settings live in a values file instead.
Managed versus self hosted: how to actually decide
Ignore the marketing on both sides and answer four questions. How many humans sign in each month? How many active series do you produce? How long do you need to keep data? And do you have someone who will patch the box?
| Situation | Best fit | Why |
|---|---|---|
| Solo developer, one or two servers | Grafana Cloud Free | Zero maintenance, and you will never touch the limits |
| Team of 5 to 20, modest metric volume | Self hosted VPS | Per user billing dominates the Pro bill long before ingest does |
| Kubernetes, high cardinality, no ops headcount | Grafana Cloud Pro | Running Mimir or Thanos yourself is a full time job |
| Data residency or air gapped network | Self hosted, external database | Managed is off the table entirely |
| Need SAML, team sync, reporting | Cloud or Enterprise | Those features are not in the OSS build |
A hybrid that works well: self host Grafana on a cheap VPS for the UI and alerting, and send metrics to a managed backend so you never operate a time series database. The trade off is that your queries now cross the internet, so keep alert evaluation intervals sane. If you are still choosing where the metrics themselves live, our companion piece on where to host Prometheus works through the six realistic options and the disk math behind each.
Troubleshooting
Dashboards load but panels show “Bad Gateway”. Grafana is up and the proxy is fine, but Grafana cannot reach the data source. Inside Docker Compose use the service name, so the Prometheus URL is http://prometheus:9090, not http://localhost:9090. Localhost inside the Grafana container means the Grafana container.
Login redirect loop after adding OAuth. Almost always a mismatched root_url. Set GF_SERVER_ROOT_URL to the exact public HTTPS address including the scheme, and make sure the OAuth callback registered with the provider is that URL plus /login/github.
“Database is locked” errors under load. That is SQLite telling you it is done. Move the Grafana backend to PostgreSQL by setting GF_DATABASE_TYPE=postgres along with the host, name, user and password variables, then restore your dashboards from a backup or re provision them.
Alert rules stop firing after an upgrade. Check whether you are still on legacy alerting. Grafana Unified Alerting is the default now and legacy rules do not migrate silently in every path. Compare the alert list in the UI against your provisioning files before assuming the rules are gone.
Container restarts with a permissions error on the data volume. The official image runs as UID 472. If you bind mount a host directory instead of using a named volume, run chown -R 472:472 /path/to/grafana before starting.
Frequently asked questions
Is Grafana free to self host?
Yes. Grafana OSS is licensed under AGPLv3 and there is no user limit, dashboard limit or feature gate on the core product. You pay only for the server it runs on. Enterprise only features such as SAML, reporting and data source permissions require a paid license or Grafana Cloud.
How much RAM does Grafana need?
A small production instance runs comfortably in 2 to 4 GB with 2 CPU cores. Grafana itself is modest. If you also run Prometheus or Loki on the same box, size for those instead, because the time series database will use several times what Grafana does.
Can I use Grafana Cloud with my own Prometheus?
Yes, two ways. Point a Grafana Cloud data source at a publicly reachable Prometheus, or install Grafana Private Data Source Connect so Cloud can query an instance inside your network. The second option avoids exposing Prometheus to the internet, which is the safer default.
Does Grafana need a separate database?
Not for a single instance. SQLite ships with it and handles dashboards, users and alert rules fine. You need MySQL 8.0 or later, or PostgreSQL 12 or later, once you run more than one Grafana replica or hit lock contention under concurrent use.
What happens when I exceed the Grafana Cloud free limits?
Grafana does not delete your data without warning, but ingestion for the over limit signal is throttled or rejected and you are prompted to upgrade. The cleanest way to stay under is to drop high cardinality labels at the agent with relabel rules before they ever leave your servers.
The bottom line
Grafana hosting is a two part decision that most guides collapse into one. The Grafana server is cheap and easy to run anywhere, including a $6 VPS, and self hosting it buys you unlimited users and full plugin freedom for the price of patching a box. The free Cloud tier is excellent right up to 3 users and 10k series, and past that the per user visualization charge is usually what pushes teams back to their own hardware.
Start by measuring your active series and your real user count for a month. If both sit under the free limits, take the managed option and spend your time elsewhere. If either one is climbing, put Grafana on a small VPS behind Nginx with an external Postgres, and make a separate, deliberate choice about where the metrics live. Those two decisions are independent, and treating them that way is what keeps an observability bill from quietly tripling.
