Caption: Grafana gives teams a private observability workspace for dashboards, alerts, and operational views across infrastructure and applications.
Introduction
Grafana is an open-source observability and data visualization platform. It is best known for dashboards, but a real Grafana deployment often becomes the place where operators review metrics, logs, traces, alert history, service health, database trends, and application signals. It connects to data sources such as Prometheus, Loki, Tempo, Elasticsearch, InfluxDB, PostgreSQL, MySQL, and many hosted monitoring backends.
Self-hosting Grafana is useful when monitoring data should stay close to your own infrastructure. A small VPS, homelab, agency platform, or internal engineering team can run Grafana behind its own domain, control who signs in, decide which plugins are installed, version-control dashboards, and back up the application database with the rest of the stack. You also avoid scattering operational dashboards across personal accounts or unmanaged SaaS workspaces.
This guide installs Grafana Open Source on Ubuntu 24.04 LTS with Docker Compose. The deployment stores Grafana data in a named Docker volume, binds the container to localhost, places Nginx in front for HTTPS, prepares provisioning directories for data sources and dashboards, and adds a practical backup routine. By the end, Grafana will be available at https://grafana.example.com with a secure admin password, a repeatable Compose file, and a checklist for creating your first monitoring dashboard.
Why Choose Grafana?
- Flexible dashboards: Grafana can combine time series panels, tables, gauges, stat cards, logs, annotations, and variables into views that make sense for your services.
- Broad data source support: Prometheus, Loki, Tempo, Elasticsearch, InfluxDB, PostgreSQL, MySQL, and many cloud services can be connected from one interface.
- Configuration as code: Data sources and dashboard providers can be provisioned from files, which is ideal for Git-managed infrastructure.
- Alerting workflows: Grafana includes alert rules, contact points, notification policies, silences, and alert state history.
- Open-source deployment option: The
grafana/grafanaimage runs the open-source edition and works well for private observability stacks. - Docker-friendly operations: The app can run as a single container with persistent storage and a reverse proxy in front.
- Strong fit for small teams: A single Grafana server can provide shared visibility for application owners, sysadmins, support engineers, and homelab operators.
Grafana is not a metrics collector by itself. It visualizes and manages data that comes from other systems. For infrastructure metrics, pair it with Prometheus, node_exporter, cAdvisor, or another collector. For logs, pair it with Loki or another supported log data source. Start with one clean data source, a few useful dashboards, and reliable backups before expanding into a full observability platform.
Prerequisites
Hardware Recommendations:
- 1 CPU core and 1 GB RAM for a personal Grafana instance with a few dashboards
- 2 CPU cores and 2 GB RAM for a small team using multiple data sources and alerts
- 20 GB free disk space to start, plus room for the Grafana database, plugins, dashboard snapshots, logs, and backups
- SSD-backed storage for smoother dashboard loading and plugin operations
- Off-server storage for configuration and volume backups
Software and Accounts:
- Ubuntu 24.04 LTS server with sudo access
- A domain such as
grafana.example.com - DNS
AorAAAArecord pointing that hostname to the server - Docker Engine and Docker Compose v2
- Nginx and Certbot for HTTPS
curl,jq, andopensslfor checks and secret generation- At least one data source to connect later, such as Prometheus
Security Notes:
- Do not expose Grafana's container port directly to the internet. Bind it to
127.0.0.1and expose only Nginx over ports80and443. - Generate a unique admin password before first start and store it in a password manager.
- Disable public sign-up unless you intentionally run an open community instance.
- Treat provisioning files as infrastructure code. Do not commit API keys, passwords, or cloud credentials to a public repository.
- Back up the Grafana data volume and deployment files together so dashboards, users, alerting configuration, and plugins can be restored.
Start with an updated host and a restrictive firewall:
sudo apt update
sudo apt upgrade -y
sudo apt install -y ca-certificates curl gnupg jq openssl ufw nginx certbot python3-certbot-nginx
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status
Installation Guide
This deployment keeps all working files under /opt/grafana. Docker Compose runs Grafana, Nginx handles HTTPS, and a named Docker volume stores persistent application data.
1. Install Docker Engine
Install Docker and confirm the Compose plugin is available:
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker "$USER"
newgrp docker
docker --version
docker compose version
If the Docker group is not active in your current shell, log out and back in before continuing.
2. Create the Project Directory
Create a private project directory with folders for provisioning, dashboards, and local backup staging:
sudo mkdir -p /opt/grafana/{provisioning/datasources,provisioning/dashboards,dashboards,backups}
sudo chown -R "$USER":"$USER" /opt/grafana
chmod 700 /opt/grafana
chmod 700 /opt/grafana/backups
cd /opt/grafana
Keep this directory private because .env contains the first admin password and may later contain data source credentials.
3. Generate Secrets and Create .env
Generate a strong initial admin password and write the environment file:
cd /opt/grafana
GRAFANA_ADMIN_PASSWORD_VALUE="$(openssl rand -base64 36 | tr -d '\n')"
cat > .env <<EOF
GF_SERVER_DOMAIN=grafana.example.com
GF_SERVER_ROOT_URL=https://grafana.example.com/
GF_SECURITY_ADMIN_USER=admin
GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD_VALUE}
GF_USERS_ALLOW_SIGN_UP=false
GF_AUTH_ANONYMOUS_ENABLED=false
GF_ANALYTICS_REPORTING_ENABLED=false
GF_LOG_LEVEL=info
EOF
chmod 600 .env
printf 'Initial Grafana admin password: %s\n' "${GRAFANA_ADMIN_PASSWORD_VALUE}"
Save the generated password in a password manager. Grafana uses these admin credentials during initial setup. After users exist, change admin credentials from Grafana itself or use the Grafana CLI inside the container.
4. Create compose.yaml
Create a Compose file for the Grafana Open Source image:
services:
grafana:
image: grafana/grafana:latest
container_name: grafana
restart: unless-stopped
env_file: ./.env
ports:
- "127.0.0.1:3000:3000"
volumes:
- grafana_data:/var/lib/grafana
- ./provisioning:/etc/grafana/provisioning:ro
- ./dashboards:/var/lib/grafana/dashboards:ro
healthcheck:
test: ["CMD-SHELL", "curl -fsS http://localhost:3000/api/health || exit 1"]
interval: 30s
timeout: 10s
retries: 5
volumes:
grafana_data:
name: grafana_data
The official Grafana documentation also documents grafana/grafana-enterprise, which is free to run and includes the OSS feature set plus optional enterprise capabilities. This guide uses grafana/grafana because it is the open-source image and is enough for a private monitoring dashboard.
Caption: Nginx publishes the HTTPS site while the Grafana container stays bound to localhost with persistent data in a named Docker volume.
5. Start Grafana
Validate the Compose file, pull the image, and start the container:
cd /opt/grafana
docker compose config
docker compose pull
docker compose up -d
docker compose ps
docker compose logs --since=2m grafana
Test the local health endpoint:
curl -s http://127.0.0.1:3000/api/health | jq
A healthy response includes a database status and Grafana version information. If the health check fails, inspect docker compose logs grafana before configuring the public proxy.
6. Configure Nginx and HTTPS
Create /etc/nginx/sites-available/grafana.example.com:
server {
listen 80;
listen [::]:80;
server_name grafana.example.com;
location / {
proxy_http_version 1.1;
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_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass http://127.0.0.1:3000;
}
}
Enable the site and request a certificate:
sudo ln -s /etc/nginx/sites-available/grafana.example.com /etc/nginx/sites-enabled/grafana.example.com
sudo nginx -t
sudo systemctl reload nginx
sudo certbot --nginx -d grafana.example.com
sudo systemctl reload nginx
Now open https://grafana.example.com in your browser and sign in with the generated admin password.
Configuration
Grafana's Docker image is configured through environment variables and files mounted into the container. The most important settings in this guide are:
GF_SERVER_ROOT_URL: the public HTTPS URL users open in the browser.GF_SECURITY_ADMIN_USERandGF_SECURITY_ADMIN_PASSWORD: the initial admin credentials.GF_USERS_ALLOW_SIGN_UP=false: prevents visitors from creating accounts without approval.GF_AUTH_ANONYMOUS_ENABLED=false: keeps dashboards private unless you explicitly enable public sharing./var/lib/grafana: persistent application data, stored in thegrafana_datavolume./etc/grafana/provisioning: file-based data source and dashboard provisioning.
Provision a Prometheus Data Source
Grafana can provision data sources from YAML. If your Prometheus server is reachable from the Grafana container, create provisioning/datasources/prometheus.yaml:
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://prometheus.example.internal:9090
isDefault: true
editable: false
Restart Grafana after changing provisioning files:
cd /opt/grafana
docker compose restart grafana
docker compose logs --since=1m grafana
If Prometheus runs in another Compose project, place both services on a shared Docker network or point url to a private DNS name that the Grafana container can resolve.
Provision Dashboard Files
Create a dashboard provider in provisioning/dashboards/default.yaml:
apiVersion: 1
providers:
- name: Local dashboards
orgId: 1
folder: Operations
type: file
disableDeletion: false
updateIntervalSeconds: 30
allowUiUpdates: false
options:
path: /var/lib/grafana/dashboards
foldersFromFilesStructure: true
Place exported dashboard JSON files under /opt/grafana/dashboards. Grafana will load them into the Operations folder. This pattern lets you review dashboard changes in Git before applying them to production.
Caption: Provisioning files let operators manage data sources and dashboard providers as repeatable configuration instead of manual click paths.
Usage
After the first login, work through a small operational checklist:
- Open Administration > Users and access and create named accounts for real operators.
- Change the initial admin password after confirming you can log in.
- Add your first data source from Connections > Data sources, or use the provisioning file above.
- Create a folder such as
Operations,Applications, orHomelabto keep dashboards organized. - Import one well-known dashboard for your data source, then customize it instead of accepting every default panel.
- Create a simple alert rule only after the dashboard panels show trustworthy data.
- Test notification delivery before relying on alerts overnight.
For a first dashboard, start with host-level metrics: CPU usage, memory usage, disk space, filesystem read and write rate, network throughput, and container restarts. Add annotations for deployments later. A small dashboard that answers common operational questions is better than a huge dashboard nobody reads during an incident.
If you need to reset the admin password from the server, run:
cd /opt/grafana
NEW_ADMIN_PASSWORD="$(openssl rand -base64 36 | tr -d '\n')"
docker compose exec grafana grafana cli admin reset-admin-password "${NEW_ADMIN_PASSWORD}"
printf 'New Grafana admin password: %s\n' "${NEW_ADMIN_PASSWORD}"
Store the new value in your password manager immediately.
Screenshots and Visuals
The diagrams in this guide are original deployment visuals, not scraped product screenshots. They show the recommended network boundary, provisioning flow, and maintenance loop for a small self-hosted Grafana service.
Caption: Keep Grafana reliable by pairing volume backups with configuration backups, restore tests, update reviews, and alert checks.
Troubleshooting
- Browser shows a bad gateway from Nginx: Run
docker compose psand confirm Grafana is healthy. Then check that Nginx proxies tohttp://127.0.0.1:3000. - Login uses the old password after editing
.env: The initial admin password is applied when the admin user is created. Reset the password from Grafana or usegrafana cli admin reset-admin-password. - Dashboard panels say no data: Open the data source settings and use Save & test. Confirm the Grafana container can resolve and reach the data source URL.
- Provisioned dashboard edits disappear:
allowUiUpdates: falsemeans file-backed dashboards should be changed in JSON files, not permanently edited in the UI. - HTTPS callback or links use the wrong host: Check
GF_SERVER_ROOT_URL,GF_SERVER_DOMAIN, and the NginxX-Forwarded-Protoheader. - Plugins disappear after recreating the container: Plugins live under
/var/lib/grafana/plugins, which is inside thegrafana_datavolume. Confirm the volume is mounted and backed up. - Container cannot write to storage: Inspect
docker compose logs grafanafor permission errors and confirm the named volume is attached to/var/lib/grafana.
Scaling, Securing, and Next Steps
For a small deployment, a single Grafana container with a named volume is simple and reliable. As usage grows, consider moving the Grafana database from the embedded SQLite file to PostgreSQL, running Prometheus and Loki on separate storage, placing Grafana behind SSO, and managing dashboards through Git review. Teams with strict access requirements should define organizations, folders, teams, and permissions carefully instead of sharing one admin account.
Back up both configuration and data. The deployment files explain how Grafana starts, while the volume contains the internal database, plugins, sessions, alerting state, and UI-created dashboards. A practical backup routine looks like this:
cd /opt/grafana
mkdir -p backups
tar czf "backups/grafana-config-$(date +%F).tgz" \
compose.yaml .env provisioning dashboards
docker run --rm \
-v grafana_data:/data:ro \
-v "$PWD/backups:/backup" \
alpine sh -c 'cd /data && tar czf /backup/grafana-data-$(date +%F).tgz .'
ls -lh backups
Copy the resulting archives to encrypted off-server storage. To restore on a replacement host, install Docker, recreate /opt/grafana, unpack the configuration archive, recreate the grafana_data volume, unpack the data archive into that volume with a temporary container, and then run docker compose up -d.
For updates, read Grafana release notes before pulling a new image. Then run:
cd /opt/grafana
docker compose pull
docker compose up -d
docker compose logs --since=2m grafana
curl -s http://127.0.0.1:3000/api/health | jq
By following this guide, you end with a private Grafana instance available over HTTPS, protected behind Nginx, backed by persistent storage, ready for provisioned data sources, and prepared for operational backups. The next useful step is to connect Prometheus or another trusted data source, build a small service-health dashboard, and test one alert from trigger to notification.