Expose Self-Hosted Grafana behind VPN with Cloudflare Tunnel
Hosting the Grafana on the home server behind the VPN protection is safe. But it’s inconvenient when we need to access it and connect to…
Expose Self-Hosted Grafana behind VPN with Cloudflare Tunnel

Introduction
Hosting the Grafana on the home server behind the VPN protection is safe. But it’s inconvenient when we need to access it and connect to other APN server (e.g.,)
Are there any solutions to safely expose a self-hosted Grafana instance beyond the VPN boundary to the public internet?
In this article, we will go through the steps to set up the monitor system for the home server and expose it.

Agenda
- Prerequisites
- Requirements
- System Design Overview
- Monitor Home Servers with Grafana, Prometheus, Exporters
- Expose System to Internet with
CloudflareTunnel,CloudflareAccess - Architecture Decision Records
Prerequisites
- Have an account and a domain on
[Cloudflare](https://www.cloudflare.com/)
Requirements
Functional Requirements
- Monitor multiple servers and metrics
- Monitor resources of macOS server (e.g., CPU/RAM usage, storage, etc)
- Monitor resources and
SNMPdevices of the Linux VM server - Expose the monitoring system to the public internet
Non-functional Requirements
- This system won’t be a distributed system (it’s just for the home server) => No consistency, partitioning concerns
- Resource efficiency
- Maintainability
- Security: must authenticate the user after exposing
Constraints
- The target server is behind the VPN
- The client is behind VPN
- Those two network can’t reach out to each other
System Design Overview
Component Diagram
The following diagram is the architecture for this monitoring system.
- Monitor Home Servers with
Grafana,Prometheus,Exporters - Expose System to Internet with
Cloudflare Tunnel,Cloudflare Acess

Monitor Home Servers with Grafana, Prometheus, Exporters
The Prometheus is one of the datasource for Grafana. It provides the flexibility of queries and metrics.
Exporter is responsible to collect the data from our target machines, we have to install different exporters according to different purposes.
The following diagram shows the architecture of the Grafana, Prometheus, exporters. The Prometheus can poll the data from multiple exporter.

Create Exporters for Resources and SNMP Devices of the Linux VM Server
- Create the following folder structure
node-exporter: store docker compose file for theNode exportersmnp-exporter: store docker compose file for theSNMP exporter
.
├── node-exporter
└── snmp-exporter
- Create
node-exporter/docker-compose.ymlto launchNode exporteras docker container
[embed]
- Launch the
Node exporter
sudo docker-compose up -d
- Create
snmp-exporter/docker-compose.ymlto launchSNMP exporteras docker container
[embed]
- Launch the
SNMP exporter
sudo docker-compose up -d
Monitor Resources of the macOS Server (CPU, RAM, etc)
- Install
Node exporter
brew install node_exporter
brew services start node_exporter
- Create the following folder structure
grafana/dashboard: store theGrafanadashboard filesgrafana/provisioning/dashboards: store dashboard configs forGrafanagrafana/provisioning/datasources: storePrometheusconfigs forGrafanaprometheus: store configs forPrometheusand the IPs ofSNMPdevices
.
├── grafana
│ ├── dashboards
│ └── provisioning
│ ├── dashboards
│ └── datasources
└── prometheus
- Create
docker-compose.ymlto launchGrafanaandPromethuesas docker containers
[embed]
- Create
.envto storeenvironment variablesforGrafana
[embed]
- Create
prometheus/prometheus.ymlto manage monitored targets as jobs PrometheusitselfNode exporteron the host machineNode exporteron the Linux serverSNMP exporteron the Linux server
[embed]
- Create
prometheus/snmp_targets.jsonfor theSNMP exporterto traceSNMPdevices
[embed]
- Create
grafana/provisioning/datasources/prometheus.ymlto setPrometheusasGrafanadatasource
[embed]
- Create
grafana/provisioning/dashboard/default.ymlto set dashboard configurations
[embed]
- Launch the
GrafanaandPrometheus
sudo docker-compose up -d
Import Dashboard for Node Exporter
- Open the
Grafanaonlocalhost:3000

- Download the dashbard template for the
Node exporter - Check the node exporter dashboard

Expose System to Internet with Cloudflare Tunnel, Cloudflare Access
To expose the local port to the internet without exposing IP address, we can use Cloudflare Tunnel secured by the Claudflare Access.
Even though the Grafana has been exposed to the internet, Cloudflare Access will only allow the specific email to login.

Establish the Cloudflare Tunnel between Server and Cloudflare
- Install
Cloudflared
# 1. Install cloudflared
brew install cloudflared
# 2. Login to Cloudflare
cloudflared tunnel login
# 3. Establish Tunnel
cloudflared tunnel create home-grafana
- Copy the
idand thefile pathfrom the response
Tunnel credentials written to
/Users/hsiehsean/.cloudflared/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.json.
cloudflared chose this file based on where your origin certificate was found.
Keep this file secret. To revoke these credentials, delete the tunnel.
Created tunnel home-grafana with id xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
- Create the configuration file with the following format by using the
nano ~/.cloudflared/config.yml
tunnel: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
credentials-file: /Users/hsiehsean/.cloudflared/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.json
ingress:
- hostname: grafana.alayman.io
service: http://localhost:3000 # Forward to the local service
- service: http_status:404 # Other traffic
- Create a DNS record
cloudflared tunnel route dns home-grafana grafana.{your domain}
- Check the DNS records on
Cloudflare

Set up the Cloudflare Access
- Check the
Cloudflare Access

- Create a new policy to only for the personal email to login

- Create a new application


- Open
grafana.{your domain}on the browser to access the login page

Architecture Decision Records
ADR 1. Use Cloudflare Tunnel to Enable Access to Home Grafana from Corporate Network
Consider we can’t install software on the company’s notebook, we decide to use the Cloudflare Tunnel to expose Grafana to the company laptop via browser.
The following table is our trade-off.
[embed]
ADR 2. Add Zero Trust Identity Verification Layer in Front of Grafana Using Cloudflare Access
Grafana Native Auth and Grafana + OAuth protect Grafana after the client reaches out to the server. Cloudflare Access can reject the request at the Cloudflare level.
The following table is our trade-off.
[embed]
Summary
Thanks for your patient. I am Sean. I work as a software engineer.
This article is my note. Please feel free to give me advice if any mistakes. I am looking forward to your feedback.
- Subscribe me
- Recommended places for digital nomads
- The Daily Learning website
메타데이터
- post_id
- 7e5c0b024d67
- slug
- expose-self-hosted-grafana-behind-vpn-with-cloudflare-tunnel-7e5c0b024d67
- url
- https://medium.com/a-layman/expose-self-hosted-grafana-behind-vpn-with-cloudflare-tunnel-7e5c0b024d67
- canonical_url
- https://medium.com/a-layman/expose-self-hosted-grafana-behind-vpn-with-cloudflare-tunnel-7e5c0b024d67
- author_url
- https://medium.com/@seanhsieh_63050
- status
- ok
- fetched_at
- 2026-06-10 08:17:25