all posts

The Best Grafana Hosting Platforms in 2026

Ajay Kumar··9 min read

Grafana is one of the few pieces of infrastructure where the thing you are shopping for is not the thing that costs money. It is a Go binary that renders charts. It holds almost no data of its own. You can run it on a machine you would be embarrassed to describe out loud. And yet 'Grafana pricing' is a phrase that makes finance teams flinch, because what people actually mean when they say it is metrics ingest and log retention pricing wearing a dashboard as a disguise.

I'm Ajay, I build PandaStack. We run a lot of Grafana internally and we host other people's Grafana instances as ordinary apps, so I have a mild interest in you self-hosting — which is exactly why I want to be straight about where self-hosting is the wrong call. This is a qualitative comparison, not a price sheet: vendor pricing and free-tier limits change constantly, so verify every number against their current docs before you commit to anything.

Grafana is the cheap part. The data is the expensive part.

Grafana is, architecturally, a read layer. It authenticates a human, loads a dashboard definition, fans out queries to data sources, and draws the results. It stores dashboards, users, org settings, API keys, annotations and alert rules — and that's it. Your metrics do not live in Grafana. Your logs do not live in Grafana. A Grafana instance you have never backed up is annoying to lose, not catastrophic, which is a sentence you cannot say about almost anything else in your stack.

So when you are choosing 'where to host Grafana', you are really answering two separate questions that people jam together:

  1. Where does the query/render layer run, and who patches it? This is a small, boring, cheap decision.
  2. Where do Prometheus/Mimir, Loki, ClickHouse, Postgres or whatever else you query actually live, and what does ingest and retention cost there? This is the decision that determines your bill by an order of magnitude.

Bundled offerings answer both at once, which is convenient and also how you end up with a per-series or per-GB-ingested bill you did not model. The single most useful exercise before you pick a host is to write down your active series count, your daily log volume, and your required retention window. Everything else follows from those three numbers.

A dashboard nobody has opened in six months is not observability, it is wallpaper. Before you pay to retain more data, audit which dashboards and which alert rules anyone actually looked at last quarter. Retention is the biggest lever on your bill and the one nobody wants to touch.

Grafana's own state: SQLite by default, and why that bites

Out of the box, Grafana keeps its state in a SQLite file at /var/lib/grafana/grafana.db. This is a genuinely good default for a laptop and a genuinely bad default for anything you care about, for three reasons that all show up at the same awkward moment.

  • It ties the instance to a disk. Container restarts on ephemeral storage take your dashboards with them. This is the classic 'we redeployed and Grafana came back empty' incident.
  • It prevents horizontal scaling. Two Grafana replicas pointed at the same SQLite file over a network filesystem is not a supported configuration, it is a locking bug waiting for a busy afternoon.
  • It makes backups your problem in an awkward format. Copying a live SQLite file is not a backup; you need a proper snapshot or a stopped process.

For anything real, point Grafana at Postgres. It supports Postgres and MySQL as its backend store and the switch is a config block, not a migration project — though note that Grafana does not migrate your existing SQLite contents for you, so do it on day one or export your dashboards first.

# /etc/grafana/grafana.ini -- move Grafana's own state off SQLite.
[database]
type = postgres
host = my-db-id.db.example.com:5432
name = grafana
user = grafana
password = ${__env:GF_DATABASE_PASSWORD}
ssl_mode = require

[server]
# Set this correctly or OAuth redirects and alert notification links break.
root_url = https://grafana.internal.example.com/

[security]
admin_user = admin
admin_password = ${__env:GF_SECURITY_ADMIN_PASSWORD}
# Encrypts data-source credentials at rest in the database. Rotate it like a secret,
# because everything encrypted with the old value becomes unreadable if you lose it.
secret_key = ${__env:GF_SECURITY_SECRET_KEY}
cookie_secure = true

[users]
allow_sign_up = false

[auth.anonymous]
enabled = false

Every setting in grafana.ini also has an environment-variable form — GF_DATABASE_TYPE, GF_DATABASE_HOST and so on — which is usually the nicer way to do it on a platform where config is env vars. Same result, no file to template.

secret_key is the one that surprises people. Grafana uses it to encrypt data-source passwords stored in its database. If you regenerate it on a redeploy — or let a platform inject a fresh random value each time — every saved data-source credential becomes undecryptable and your dashboards go blank with authentication errors. Pin it in a secret store, not in your deploy script.

Provision dashboards as code, not by clicking

The second thing that separates a Grafana you can rebuild from a Grafana you are afraid of is provisioning. Grafana watches a provisioning directory at startup and on an interval, and will create data sources, dashboards, alert rules, contact points and notification policies from YAML and JSON on disk. Dashboards defined this way can be made read-only in the UI, which is a feature rather than an annoyance: it forces changes through review.

# /etc/grafana/provisioning/datasources/datasources.yaml
apiVersion: 1

datasources:
  - name: Prometheus
    type: prometheus
    access: proxy
    uid: prom-main
    url: http://prometheus.internal:9090
    isDefault: true
    jsonData:
      timeInterval: 15s
      httpMethod: POST

  - name: Loki
    type: loki
    access: proxy
    uid: loki-main
    url: http://loki.internal:3100
    jsonData:
      maxLines: 1000

  - name: App Postgres
    type: postgres
    uid: app-pg
    url: my-db-id.db.example.com:5432
    user: grafana_ro
    jsonData:
      database: appdb
      sslmode: require
      postgresVersion: 1600
    secureJsonData:
      # Read from the environment so no credential lands in git.
      password: $APP_PG_READONLY_PASSWORD

---
# /etc/grafana/provisioning/dashboards/dashboards.yaml
apiVersion: 1

providers:
  - name: git-dashboards
    orgId: 1
    folder: Platform
    type: file
    # Dashboards live as JSON in the repo; the UI cannot silently drift from them.
    allowUiUpdates: false
    updateIntervalSeconds: 30
    options:
      path: /var/lib/grafana/dashboards
      foldersFromFilesStructure: true

Note the access: proxy on the HTTP data sources. That means Grafana's backend makes the query, not the browser — so the data source does not need to be reachable from your users' laptops, and its credentials never leave the server. Browser-side access exists for a reason but it is rarely the reason you think.

The payoff for provisioning is that your Grafana becomes reproducible. Losing the instance stops being an incident and becomes a redeploy. That, more than any hosting choice, is what makes cheap hosting safe.

Alerting is the part that makes Grafana stateful again

Grafana's unified alerting evaluates rules on a schedule inside Grafana itself, keeps alert state, and dispatches through contact points and notification policies. That has two consequences worth planning for. First, alerting turns your 'stateless read layer' into something with a clock and a memory — an instance that sleeps will not page you, so anything that must fire at 3am cannot live on a scale-to-zero instance. Second, alert rules and notification policies are provisionable too, which means your paging config can live in the same repo as your dashboards instead of in one staff engineer's browser history.

The clean split most teams land on: alert evaluation stays in Prometheus/Alertmanager or in an always-on Grafana, and the human-facing dashboard instances are free to be cheap and sleepy.

Anonymous access, SSO, and the classic exposed-Grafana incident

There is a recurring genre of security disclosure that goes: an internal Grafana was published to the internet, anonymous access was enabled 'temporarily' so a colleague could see a chart, and the dashboards turned out to contain hostnames, internal topology, customer identifiers and occasionally a data source whose query editor was a fine way to run arbitrary SQL against a production replica. Grafana is not a public web app. Its threat model is that it is behind something.

The practical rules are short. Turn off sign-up. Turn off anonymous auth unless you are deliberately running a public status dashboard, and if you are, put that on a separate instance with a separate read-only data source. Wire real SSO — OIDC, SAML or your identity provider's proxy — rather than shared admin credentials. Give data sources read-only database users, because the query editor is a query editor. And put the whole thing behind your identity-aware proxy or VPN so that a misconfiguration inside Grafana is not immediately a public one.

Every Grafana that got breached was, five minutes before, an internal tool someone needed to share quickly.

The hosting options

Grafana Cloud

The first-party managed offering, and the one to beat. You get Grafana itself plus the backing stores — Mimir for metrics, Loki for logs, Tempo for traces, Pyroscope for profiles — as a single product, with the newest Grafana features arriving there first. It has a free tier that is generous enough that a lot of small teams never leave it, and paid tiers priced primarily on ingest and retention rather than on the dashboard layer. Verify the current limits and rates against their docs; this is the pricing that moves most often.

Pick it when you want one vendor for the whole observability stack and your data is allowed to leave your infrastructure. The thing to model before committing is your active series count and log volume, because that is what the bill is indexed to. The thing people underestimate is how quickly a well-meaning Kubernetes metrics exporter produces series — high-cardinality labels are the observability equivalent of a memory leak, and they are billable.

Amazon Managed Grafana

AWS runs the Grafana workspace for you and, crucially, wires it into IAM Identity Center and AWS data sources — CloudWatch, Amazon Managed Service for Prometheus, OpenSearch, Athena, Timestream — with permissions handled through roles rather than long-lived credentials. Pricing is per active user rather than per unit of data, which inverts the usual model: the dashboard layer is what you pay for, and the data costs land on the underlying AWS services separately.

It is the obvious choice when your telemetry already lives in AWS and your identity already lives in AWS, and it is a poor choice when neither does. Two things to check against current AWS docs before you commit: which Grafana version the service is pinned to (managed services lag upstream, sometimes meaningfully), and how plugin installation is restricted, because you cannot simply drop an arbitrary plugin binary into a managed workspace.

Self-hosted on a PaaS or microVM platform

This is the option people dismiss too fast, because they are imagining a Kubernetes cluster. Grafana is one binary with one config file and one database. On any platform that can run a Linux process from git and hand you a managed Postgres, self-hosting it is an afternoon and then approximately zero maintenance beyond version bumps.

This is where PandaStack fits, so treat the following as an interested party's description. An app on our platform is a full Ubuntu userspace inside a Firecracker microVM, deployed from a git push, with a managed Postgres attachable by environment variable. That maps onto Grafana almost too neatly: the microVM runs grafana-server, the managed Postgres holds the dashboards and users, and provisioning YAML lives in the same repo as everything else. Managed Postgres creation takes 30–90 seconds; app instances restore from a snapshot with a p50 of 179ms and a p99 around 203ms, with a first cold boot around 3 seconds.

The genuinely interesting property is scale-to-zero. An internal Grafana is used by a handful of people during working hours and stares into space for the other sixteen. On a platform that sleeps idle apps and wakes them on the next request, you stop paying for the wallpaper hours. The caveat I flagged above applies with full force: a sleeping Grafana does not evaluate alert rules, so keep paging in Prometheus/Alertmanager or on an always-on instance, and let the sleepy one be the dashboards humans open.

Where this is a bad fit: if you want the metrics and log stores managed for you too, we do not do that — we run managed Postgres, not Mimir or Loki. Self-hosting Grafana on us and pointing it at someone else's metrics backend is a perfectly sensible architecture, but it is two vendors, and if you wanted one, Grafana Cloud is the honest recommendation.

Kubernetes with the Grafana Operator

If you already run Kubernetes, the Grafana Operator turns dashboards, data sources and folders into custom resources, so GitOps applies to your observability the same way it applies to everything else. Combined with kube-prometheus-stack it is the standard, well-trodden path, and the ecosystem of pre-built dashboards for Kubernetes workloads is unmatched.

The rule is the same one that applies to every 'run X on Kubernetes' answer: this is correct if Kubernetes is already load-bearing for you, and wrong if adopting Kubernetes is something you would be doing for Grafana. Grafana is not a workload that justifies a control plane. Also remember that the operator makes Grafana declarative but does not make its database disappear — you still need a real Postgres if you want more than one replica, or a StatefulSet with a volume you actually back up.

Just use your APM vendor's dashboards

The option nobody puts in a Grafana comparison, which is why it belongs here. If your telemetry already ships to Datadog, New Relic, Honeycomb, Grafana's competitors or your cloud provider's native monitoring, those products all have dashboarding built in. Adding Grafana on top gives you a second UI, a second auth surface, a second thing to upgrade, and a data-source plugin doing query translation that will eventually confuse you at the worst moment.

The legitimate reasons to add Grafana anyway are real: you have several backends and want one pane of glass, you want dashboards that survive changing vendors, you need a specific panel or plugin, or you want dashboards as code in a format that is not vendor-proprietary. If none of those apply to you, the cheapest Grafana is the one you do not run.

The options side by side

  • Grafana Cloud — Model: fully managed Grafana plus managed Mimir/Loki/Tempo, priced mainly on ingest and retention. Ops burden: essentially none; upgrades and backups are theirs. Best for: teams who want one vendor for the entire observability stack and can send telemetry off-premises.
  • Amazon Managed Grafana — Model: managed workspace priced per active user, with IAM Identity Center auth and native AWS data sources; data costs land on the underlying AWS services. Ops burden: low, at the cost of version lag and plugin restrictions. Best for: shops whose telemetry and identity both already live in AWS.
  • Self-hosted on a PaaS or microVM (PandaStack, Render, Fly, Railway) — Model: you run grafana-server from git with a managed Postgres behind it; you bring your own metrics backend. Ops burden: low but non-zero — you own version bumps, provisioning and auth config. Best for: internal dashboards that should cost nothing while idle, and teams who want their config in their own repo.
  • Kubernetes plus the Grafana Operator — Model: dashboards and data sources as custom resources, reconciled by GitOps alongside kube-prometheus-stack. Ops burden: inherits whatever your cluster already costs you; high if the cluster is new. Best for: teams already running Kubernetes with a working GitOps pipeline.
  • Your APM vendor's built-in dashboards — Model: no Grafana at all; you use the dashboarding inside Datadog, New Relic, Honeycomb or your cloud's native monitoring. Ops burden: zero extra. Best for: single-backend shops with no need for cross-vendor panes of glass or portable dashboard definitions.
Every pricing model, free-tier limit and included-feature list above changes on the vendors' schedule, not mine. Treat this as a map of the shapes, then verify pricing and limits against their current docs before you commit — especially the per-active-user definition on Amazon Managed Grafana and the ingest/retention units on Grafana Cloud, both of which have caught people out.

Running it cheaply on a small VM that sleeps

If you land on self-hosting, the shape is small. Grafana is not memory-hungry for a handful of concurrent viewers; the heavy lifting happens in the data sources it queries. A modest VM with a couple of gigabytes of RAM is comfortable for an internal instance, and the only real resource spikes come from expensive dashboards fanning out many queries at once — which is a query problem, not a hosting problem.

#!/usr/bin/env bash
# Build step: install Grafana into the app's own Linux userspace.
set -euo pipefail

apt-get update
apt-get install -y adduser libfontconfig1 musl wget
wget -q https://dl.grafana.com/oss/release/grafana_11.6.0_amd64.deb
dpkg -i grafana_11.6.0_amd64.deb

# Provisioning + dashboard JSON come from the repo, so the instance is reproducible.
mkdir -p /etc/grafana/provisioning /var/lib/grafana/dashboards
cp -r ./grafana/provisioning/. /etc/grafana/provisioning/
cp -r ./grafana/dashboards/.   /var/lib/grafana/dashboards/
#!/usr/bin/env bash
# Start step. State lives in the attached managed Postgres, not on this disk,
# so the instance is disposable and can be slept when nobody is looking at it.
set -euo pipefail

export GF_DATABASE_TYPE=postgres
export GF_DATABASE_HOST="$PANDASTACK_DB_HOST:5432"
export GF_DATABASE_NAME=grafana
export GF_DATABASE_USER="$PANDASTACK_DB_USER"
export GF_DATABASE_PASSWORD="$PANDASTACK_DB_PASSWORD"
export GF_DATABASE_SSL_MODE=require

export GF_SERVER_ROOT_URL="$APP_URL"
export GF_SERVER_HTTP_PORT="${PORT:-3000}"
export GF_USERS_ALLOW_SIGN_UP=false
export GF_AUTH_ANONYMOUS_ENABLED=false
# Pinned in the secret store. Regenerating this orphans every saved data-source credential.
export GF_SECURITY_SECRET_KEY="$GRAFANA_SECRET_KEY"

exec /usr/share/grafana/bin/grafana server \
  --homepath=/usr/share/grafana \
  --config=/etc/grafana/grafana.ini

The pattern generalises to any platform that runs a real Linux process: install in the build step, export config as environment variables in the start step, and keep every byte of state in Postgres so the compute is disposable. Once nothing important lives on the instance's disk, sleeping it while idle is a free optimisation rather than a risk.

How to choose in ten minutes

  1. Write down where your metrics and logs already live, and what ingest and retention cost there today. If that number dwarfs the hosting decision — and it usually does — optimise it first and pick the Grafana host second.
  2. Decide whether alerting runs in Grafana or in Prometheus/Alertmanager. This determines whether your instance is allowed to sleep.
  3. Check your identity requirement. If SSO through your existing provider is mandatory, confirm the option supports it on the tier you would actually buy.
  4. Commit to Postgres for Grafana's own state and to provisioning from git, whichever host you choose. Both are cheap now and expensive to retrofit after someone has hand-built forty dashboards.
  5. If you already have one telemetry vendor with decent dashboards and no cross-backend requirement, seriously consider not running Grafana at all.

The short version

Grafana Cloud if you want one vendor for the whole stack and your data can leave. Amazon Managed Grafana if your telemetry and identity are already AWS-shaped and per-user pricing suits your headcount. A PaaS or microVM app with managed Postgres if you want an internal instance that costs nothing while nobody is looking and lives in your own repo. Kubernetes with the operator if the cluster already exists. And your APM vendor's built-in dashboards if you only have one backend, because the cheapest Grafana is the one you never deploy.

Whichever you pick, the two decisions that will actually matter in a year are the same on every platform: Grafana's state belongs in Postgres, and your dashboards belong in git. Get those right and the hosting question stops being interesting — which, for a chart renderer, is exactly what you want.

Frequently asked questions

Does Grafana need a database?

It ships with SQLite at /var/lib/grafana/grafana.db, which is fine for a laptop and risky for anything else. SQLite ties the instance to a specific disk, so an ephemeral container comes back with no dashboards, and it rules out running more than one replica. For anything real, point Grafana at Postgres or MySQL through the [database] section of grafana.ini or the equivalent GF_DATABASE_* environment variables. Do it on day one — Grafana does not migrate existing SQLite contents for you, so retrofitting means exporting dashboards first.

Why is Grafana Cloud expensive when Grafana itself is free?

Because you are not really paying for Grafana. The dashboard layer is a Go binary that renders charts and holds almost no data; the bill is indexed to metrics ingest, active series count and log retention in the backing stores — Mimir, Loki and Tempo. High-cardinality labels from a well-meaning exporter can multiply active series quickly, and retention is usually the single biggest lever. Model your series count and daily log volume before choosing any host, and verify current rates against Grafana's docs since this pricing moves often.

Should I use Amazon Managed Grafana or Grafana Cloud?

It mostly comes down to where your telemetry and identity already live and which pricing shape suits you. Amazon Managed Grafana prices per active user and integrates natively with IAM Identity Center, CloudWatch and Amazon Managed Service for Prometheus, with data costs landing on those AWS services separately. Grafana Cloud bundles the managed backing stores and prices primarily on ingest and retention, and gets new Grafana features first. Check the pinned Grafana version and plugin restrictions on the AWS side before committing, and verify both vendors' current pricing pages.

Is it safe to expose Grafana to the internet?

Treat it as an internal tool that must sit behind something. The recurring incident pattern is an internal instance published publicly with anonymous access enabled 'temporarily', where the dashboards leak internal hostnames and topology and a data source's query editor becomes a way to run arbitrary queries against a production replica. Disable sign-up and anonymous auth, wire real SSO through OIDC or SAML, give every data source a read-only database user, and put the whole instance behind a VPN or identity-aware proxy.

Can I run Grafana on PandaStack?

Yes — a PandaStack app is a full Ubuntu userspace in a Firecracker microVM deployed from git, so you install grafana-server in the build step and start it with GF_DATABASE_* environment variables pointing at a managed Postgres, which takes 30 to 90 seconds to create. Provisioning YAML and dashboard JSON live in the same repo, so the instance is reproducible. Apps scale to zero when idle and restore from snapshot with a p50 of 179ms. The caveat: a sleeping instance does not evaluate alert rules, so keep paging in Prometheus/Alertmanager or on an always-on instance. We run managed Postgres, not Mimir or Loki, so you bring your own metrics backend.

Keep reading

Run code in a microVM in one API call.

49ms p50 cold start. Fork, snapshot, and scale to zero.

Start free
Written by Ajay Kumar, Founder, PandaStack.