Skip to main content
RunBook Academy

ObservabilityXXIV · Grafana InstallationGrafanaInstall

Grafana Installation Methods

Foundation⏱ ~18 minbash

What you'll learn

  • Compare the four supported Grafana 11.x install paths and select one for a given environment
  • Verify a downloaded tarball against the Grafana GPG signature before first execution
  • Configure the apt and rpm repositories with the correct signing key and version pinning on Ubuntu 24.04, Debian 12, and RHEL 9
  • Run the official Docker image with a persistent data volume and a working healthcheck
  • Articulate the trade-off between host package, container, and source tarball for upgrade ergonomics, isolation, and backup procedure

Prerequisites

Verified against Prometheus 2.55.x · Alertmanager 0.28.x · node_exporter 1.8.x · blackbox_exporter 0.26.x · Grafana 11.x · Loki 3.x · Tempo current · OpenTelemetry Collector 0.110.x · Grafana Alloy current · Docker Engine 28.x · Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL / Rocky / AlmaLinux 9.x · 2026-08-13

Not yet marked complete on this device.

A new region needs Grafana in two hours. The package manager on the target host does not match the one your previous installation used. You pick one of four paths on instinct — and lock the team into an upgrade procedure that nobody has written down yet. Six months later, the next upgrade is the one that reopens the question.

The Grafana install method is a small decision at first and a large one later. It dictates which directory owns the binary, which user owns the process, which lifecycle hooks fire, how an upgrade performs, and how a backup reads. The four supported paths in Grafana 11.x are the standalone tarball, the apt repository, the rpm repository, and the official Docker image. This lesson walks each one, weighs the trade-offs, and shows the verification step that catches a half-finished install before it pages anyone.

What it is

A Grafana “install method” is the choice of how the grafana-server binary, the default configuration, and the lifecycle hooks are deployed onto a host. The four paths ship the same code; they differ in who owns upgrades, who owns dependencies, and which filesystem paths the binary touches by default.

                  Grafana 11.x install paths
                          |
        +-----------------+------------------+-----------------+
        |                 |                  |                 |
     Tarball         apt repo          rpm repo           Docker
        |                 |                  |                 |
   /usr/local/share   /usr/share/      /usr/share/       /var/lib/grafana
    /grafana          grafana           grafana            (image-internal)
        |                 |                  |                 |
     Manual           apt upgrade      dnf upgrade        docker pull

Every path converges on the same runtime surface: a binary that reads /etc/grafana/grafana.ini, writes to /var/lib/grafana, and logs to /var/log/grafana/grafana.log. What differs is which package manager, container runtime, or sysadmin owns those directories after install.

Why a sysadmin cares

The path you pick is the path you have to maintain for the lifetime of that Grafana instance. Concrete consequences:

  • Upgrade procedure. Apt path: an apt upgrade rolls a new minor version onto the host. Tarball path: a tarball extraction, a binary swap, and a manual journal note. Container path: a new image tag, a recreate, and a verification that the named volume survived. Each one is a script the team needs.
  • Backup procedure. Container path: the named volume is the backup unit. Tarball path: /var/lib/grafana is the backup unit. Apt path: same directory, same backup unit, but you also have to remember the apt package version to roll back cleanly.
  • Failure domain. A misbehaving apt mirror can replace the binary. A misconfigured Docker daemon can leave the data volume behind after docker rm. A tarball sitting in /opt for two minor versions will be removed by the next operator who tidies up.

The lesson that returns at incident review is: pick a path, write its upgrade and backup runbook before the first alert fires, and verify the install with the same checks every time.

How it works: the runtime surface

Whichever path you pick, Grafana 11.x puts the same artefacts in the same logical places:

/etc/grafana/grafana.ini      # main config (INI, key=value)
                               # -or- /usr/share/grafana/conf/defaults.ini
                               #     (read-only defaults, overridden by grafana.ini)
/etc/grafana/                 # provisioning drop-ins
   provisioning/
      datasources/            # *.yaml files; auto-loaded
      dashboards/             # *.yaml + *.json
      alerting/               # *.yaml
      plugins/                # *.yaml
/var/lib/grafana/             # sqlite db, plugin store, file uploads
/var/log/grafana/grafana.log  # default log target
/var/run/grafana.pid          # pidfile (legacy)
/usr/sbin/grafana-server      # the binary
/usr/share/grafana/           # static assets, public/, defaults.ini

The apt and rpm packages create the grafana user and group, set ownership on /var/lib/grafana, install the systemd unit, and seed /etc/grafana/grafana.ini from a .rpmnew / .dpkg-dist template. The tarball ships a copy of the same templates into conf/; copying them onto the host is the operator’s job. The Docker image runs the same binary as user grafana (uid 472) and writes to a directory you bind-mount or name as a volume.

How to configure each path

Option A — apt repository (Debian / Ubuntu)

# READ-ONLY: inspect current sources first
ls -la /etc/apt/sources.list.d/ | grep grafana

# CONFIGURATION: install prerequisites and the signing key.
# Grafana 11.x signs packages with key 0x240a4da01d4d05f2 / gpg.key
sudo apt-get install -y apt-transport-https software-properties-common wget
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 stable repo, pointing at the named key.
echo "deb [signed-by=/etc/apt/keyrings/grafana.gpg] \
https://apt.grafana.com stable main" \
  | sudo tee /etc/apt/sources.list.d/grafana.list

sudo apt-get update
sudo apt-get install -y grafana

The package installs /usr/sbin/grafana-server, the systemd unit grafana-server.service, and seeds /etc/grafana/grafana.ini. It creates the grafana user with nologin as its shell. Nothing further is required; sudo systemctl enable --now grafana-server brings the service up.

Option B — rpm repository (RHEL 9 / Rocky / Alma)

# READ-ONLY: inspect current repos
dnf repolist | grep -i grafana

# CONFIGURATION: add the rpm repo and the signing key.
sudo tee /etc/yum.repos.d/grafana.repo <<'EOF'
[grafana]
name=grafana
baseurl=https://rpm.grafana.com
repo_gpgcheck=1
enabled=1
gpgcheck=1
gpgkey=https://rpm.grafana.com/gpg.key
sslverify=1
sslcacert=/etc/pki/tls/certs/ca-bundle.crt
EOF

sudo dnf install -y grafana
sudo systemctl enable --now grafana-server

On RHEL 9 the unit file ships with ProtectSystem=full. The lesson on systemd in module IV talks through the additions that turn the shipped unit into a hardened one.

Option C — standalone tarball (air-gapped or pinned)

The tarball path is the right answer when there is no internet access, when the host runs an unrecognised distribution, or when the team needs a version that the package repo does not yet carry. It is also the wrong answer for a fleet of more than a handful of hosts; the manual upgrade procedure does not scale.

# READ-ONLY: confirm the version, the architecture, and the
# expected sha256 from the release notes.
TARBALL="grafana-enterprise-11.3.0.linux-amd64.tar.gz"
curl -fsSL "https://dl.grafana.com/oss/release/${TARBALL}" \
  -o "/tmp/${TARBALL}"

# CONFIGURATION: verify the signature before extraction.
# Grafana publishes a .sha256 alongside each tarball.
curl -fsSL "https://dl.grafana.com/oss/release/${TARBALL}.sha256" \
  -o "/tmp/${TARBALL}.sha256"
sha256sum -c "/tmp/${TARBALL}.sha256"
# Expected output (illustrative): /tmp/grafana-...tar.gz: OK

# Stronger: GPG signature verification against the Grafana key.
curl -fsSL "https://dl.grafana.com/oss/release/${TARBALL}.asc" \
  -o "/tmp/${TARBALL}.asc"
gpg --verify "/tmp/${TARBALL}.asc" "/tmp/${TARBALL}"
# Expect: Good signature from "Grafana <ops@grafana.com>"

After verification:

# CONFIGURATION: extract to /usr/local and symlink.
sudo tar -xzf "/tmp/${TARBALL}" -C /usr/local/
sudo ln -sfn "/usr/local/grafana-11.3.0" /usr/local/grafana
sudo cp /usr/local/grafana/conf/defaults.ini /etc/grafana/grafana.ini
sudo install -d -o grafana -g grafana /var/lib/grafana /var/log/grafana

# Enable a unit you will write yourself (lesson 03 covers the body).
sudo systemctl enable --now grafana-server

Option D — Docker image

# READ-ONLY: confirm the version tag you intend to pin.
docker manifest inspect grafana/grafana:11.3.0 \
  | grep -E '"digest"|"architecture"'

# CONFIGURATION: run the image with a named volume for state.
docker volume create grafana-data

docker run -d \
  --name grafana \
  --restart=unless-stopped \
  -p 3000:3000 \
  -v grafana-data:/var/lib/grafana \
  -v /etc/grafana:/etc/grafana:ro \
  -e "GF_SECURITY_ADMIN_PASSWORD__FILE=/run/secrets/grafana_admin" \
  --health-cmd "curl -fsS http://localhost:3000/api/health" \
  --health-interval 30s \
  --health-timeout 5s \
  --health-retries 3 \
  grafana/grafana:11.3.0

How to validate it

Run these on the host that just had Grafana installed. They cover all four paths.

# READ-ONLY: the binary responds and reports its version.
grafana-server -v
# grafana-server version 11.3.0 (commit: <hash>, branch: HEAD)

# READ-ONLY: config files exist and are owned by grafana.
ls -la /etc/grafana/ /var/lib/grafana/
# drwxr-xr-x grafana grafana /var/lib/grafana
# -rw-r--r-- grafana grafana /etc/grafana/grafana.ini

# READ-ONLY: the unit is loaded and active (apt / rpm paths).
systemctl is-active grafana-server
# active
systemctl is-enabled grafana-server
# enabled

# READ-ONLY: the HTTP listener is up locally.
curl -fsS http://localhost:3000/api/health
# {"database":"ok","version":"11.3.0"}

# READ-ONLY: the container is up and healthy (docker path).
docker inspect --format '{{ .State.Health.Status }}' grafana
# healthy

# READ-ONLY: the package matches the version we expected.
dpkg -l grafana | tail -1   # Debian / Ubuntu
rpm -q grafana              # RHEL / rpm
# grafana-11.3.0-1

A clean validation is: version reported, config readable by the grafana user, unit active, /api/health returns 200 with a database field. Any one of those missing is a half install.

How it can fail

The four paths fail differently; some overlap, but the failure shapes are distinct enough to deserve their own list.

  1. Apt path: missing signed-by= clause on the source line. On modern Debian and Ubuntu, an unsigned .list entry is refused by apt. The install proceeds from somewhere else (or not at all). The symptom is an apt update that prints The following signatures couldn't be verified.
  2. Rpm path: corporate proxy intercepts the dnf request. RHEL 9 with a TLS-inspecting proxy will produce a mirror response with a foreign CA. gpgcheck=1 plus repo_gpgcheck=1 means dnf refuses the response. The symptom is a clean GPG key import failed line in the install log.
  3. Tarball path: extraction without signature verification. A typo in the URL or a stale mirror returns a different tarball. sha256sum -c would have caught it before the unit ever started; the symptom is later: the unit starts but the binary crashes on first request with a cannot find symbol error from libc.
  4. Docker path: anonymous volume recreated on docker rm. Without -v grafana-data:/var/lib/grafana, Docker creates an anonymous volume that persists across recreates unless explicitly removed. The symptom is “I deleted the container and the data is still there.” Conversely, a bind mount that is not recreated will leave the container with an empty data directory on first run.
  5. All paths: TLS certificate validation disabled system-wide. When a hardened dnf / apt policy is bypassed (e.g. setting gpgcheck=0 to make the install “work”), the chain of trust collapses for every future package on that host. The symptom appears months later: a tampered upstream package would have been accepted by this host.
  6. All paths: SELinux denial on the rpm path. RHEL 9 with enforcing SELinux will block grafana-server from writing to a non-default data path. The symptom is a per-second AVC denial in /var/log/audit/audit.log and a grafana.log line beginning permission denied.

How to troubleshoot it

The diagnostic order is identical across the four paths; only the specific commands differ.

  1. Did install finish? dpkg -l grafana, rpm -q grafana, ls /usr/local/grafana, docker image inspect grafana/grafana:11.3.0.
  2. Is the process running? systemctl status grafana-server, ps -fC grafana-server, docker ps --filter name=grafana.
  3. Does the binary report its version? grafana-server -v.
  4. Is the listener up? curl -fsS http://localhost:3000/api/health and read the database field.
  5. Did the unit log a startup error? journalctl -u grafana-server -n 200 --no-pager.
  6. Are filesystem permissions correct? ls -ld /var/lib/grafana /var/log/grafana, id grafana. The data path must be writable by the grafana user.

Security implications

  • Tarball path has the largest attack surface: a manual download, a manual key import, a manual place-in-path. One missed sha256sum -c undoes the trust.
  • Apt / rpm paths lean on the upstream signing keys (Grafana’s GPG key plus the host’s existing package trust). A host whose gpgcheck has been disabled to make a single install succeed accepts every later package without verification.
  • Docker path relies on the registry you pull from. The official grafana/grafana image is signed; third-party mirrors are not. Pinning by digest and verifying with docker trust inspect removes the largest remaining risk.
  • All four paths expose the same HTTP endpoint at port 3000 by default. Lesson 05 covers the reverse proxy and TLS story.

Performance implications

  • Tarball and host packages share the host’s resources with everything else. A grafana-server starved for file descriptors under load behaves identically regardless of install path.
  • Docker path inherits the cgroup, the network namespace, and any --memory / --cpus limits the operator set. A common mistake is to run the container unconstrained on a host where the application stack also runs. The lesson on systemd adds equivalent limits for the host-package path.
  • The four paths are not different in CPU / memory profile once the binary is running. The choice is about who enforces the limits, not what the limits are.

Production guidance

  • Pick a path per region and stick to it. A consistent install surface is the smallest possible mistake space.
  • On the apt / rpm path, freeze to a minor version. Pin to grafana=11.3.0 (apt) or dnf install grafana-11.3.0 rather than grafana. Upgrades in Grafana 11.x can change feature flags; a surprise upgrade at 02:00 is how dashboards break.
  • On the Docker path, pin by digest and rebuild the docker-compose.yml (or equivalent) against the new digest in the same change.
  • On the tarball path, write the upgrade procedure as a runbook before the first upgrade is needed.
  • All four paths: validate the install with /api/health after every change. The validation step is the same regardless of how Grafana arrived.

Verification

You should now be able to answer:

  • Which of the four Grafana 11.x install paths leaves you with the smallest manual upgrade procedure, and which leaves you with the largest?
  • What is the first command you would run after a tarball download to catch a corrupted or substituted archive?
  • Why does pinning to grafana=11.3.0 matter on the apt path when grafana would otherwise track the latest 11.x minor?
  • Which file system path is the backup unit on each of the four install paths, and which one is the rollback unit?

Quiz

Knowledge check · 8 questions

  1. Q1. Which install path leaves a Grafana 11.x instance with the smallest manual upgrade procedure on a fleet of Debian 12 hosts?

  2. Q2. Pin a Grafana Docker image to a specific tag such as `grafana/grafana:11.3.0` for production deployments.

  3. Q3. Which commands are part of the install validation step on a fresh host?

  4. Q4. A host with the official Grafana apt source has just run `apt-get update`. The output prints `The following signatures could not be verified`. What is the most likely cause?

  5. Q5. Name one shell command you would run after downloading a Grafana tarball to confirm the file was not corrupted or substituted in transit.

  6. Q6. On the Docker path, what single configuration line turns `/var/lib/grafana` into a unit you can back up independently of the container image?

  7. Q7. Why freeze apt to `grafana=11.3.0` rather than letting the meta-package `grafana` track the latest 11.x minor?

  8. Q8. Mixing install methods across a Grafana fleet (apt on one region, tarball on another) is an acceptable design choice for production.

Passing score: 75%. Answers are checked in this browser.