Grafana Pyroscope
Overview
Grafana Pyroscope is an open-source continuous-profiling platform. Your applications continuously send profiling data (CPU time, memory allocations, goroutines, locks and more) to Pyroscope. It stores the data efficiently and renders it as interactive flamegraphs in a built-in web UI, so you can see exactly which functions consume your resources and how that changes over time.
This SecureAnyCloud image runs Grafana Pyroscope 2.2.1 on Ubuntu 24.04 on Azure. Pyroscope runs in single-binary (monolithic) mode and stores all profile data on the VM's own disk. There is no external object store, no separate database and no extra network port to secure.
What you get:
- The official Grafana Pyroscope 2.2.1 release binary (a single Go binary) at
/usr/local/bin/pyroscope, verified by SHA-256 at install time - A
pyroscope.servicesystemd unit that runs the server in single-binary mode (--target=all) with local filesystem storage under/var/lib/pyroscope/data - An
nginxreverse proxy that terminates TLS on port 443 (per-VM self-signed certificate) with HTTP Basic Auth in front of every route. Port 80 redirects to HTTPS - A
grafana-pyroscope-firstboot.servicesystemd one-shot that creates the per-VM Basic Auth password and the per-VM TLS certificate on first boot - A
ufwhost firewall that seals Pyroscope's port 4040 off-box (only 22, 80 and 443 plus loopback are allowed) - A credential self-test at
/usr/local/sbin/pyroscope-credcheck.sh - The AGPL-3.0 licence text at
/usr/local/share/pyroscope/LICENSE - An Ubuntu 24.04 LTS base with the latest security patches applied at build time
- 24/7 SecureAnyCloud support with a 24-hour response SLA
Security model. Pyroscope OSS has no authentication of its own, so nginx fronts the entire surface. It terminates TLS on port 443 with a certificate generated uniquely for your instance on first boot, and it guards every route with HTTP Basic Auth using a per-VM password. Port 80 redirects to HTTPS.
Pyroscope's HTTP server listens on port 4040. Single-binary mode requires it to bind all interfaces, because its in-process components discover and dial each other over that port. An in-image ufw host firewall therefore enforces isolation: only SSH (22), HTTPS (443) and the HTTP redirect (80) are accepted from off-box, and loopback is allowed.
The Linux kernel routes the VM's own traffic to port 4040 through the loopback device. nginx and Pyroscope's own components can reach the port, but no external host can, even if you open 4040 in your network security group. The only route that answers without authentication is a static /healthz probe.
Prerequisites
You need:
- An active Azure subscription
- A subscription to the Grafana Pyroscope on Ubuntu 24.04 listing on Azure Marketplace
- An SSH public key for VM authentication
- A virtual network with a subnet in the target region
Recommended VM size: Standard_B2s (2 vCPU, 4 GB RAM). Scale up CPU, memory and disk as the number of profiled services and your retention grow.
Deploy from the Azure Portal
Open Marketplace in the Azure Portal, search for Grafana Pyroscope, select the SecureAnyCloud publisher entry, and click Create.
On the Networking tab, attach a network security group that allows inbound TCP 22 from your management IP range. Also allow TCP 443 (and 80 for the HTTP-to-HTTPS redirect) from the networks that will send profiles and use the UI. The per-VM Basic Auth credential protects every route and Pyroscope's own port is firewalled off-box, but you should still restrict source ranges.
Click Review + create and wait for validation, then click Create. Deployment takes around two minutes.
Deploy from the Azure CLI
RG="pyroscope-prod" LOCATION="eastus" az group create --name "$RG" --location "$LOCATION"
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:grafana-pyroscope-ubuntu-24-04:default:latest
az vm create \
--resource-group "$RG" \
--name pyroscope-01 \
--image sac1647283583153:grafana-pyroscope-ubuntu-24-04:default:latest \
--size Standard_B2s \
--admin-username azureuser \
--generate-ssh-keys \
--public-ip-sku Standard
# Allow SSH (22), HTTPS (443) and the HTTP->HTTPS redirect (80) from your trusted ranges az vm open-port --resource-group "$RG" --name pyroscope-01 --port 22 --priority 1001 az vm open-port --resource-group "$RG" --name pyroscope-01 --port 443 --priority 1002 az vm open-port --resource-group "$RG" --name pyroscope-01 --port 80 --priority 1003Replace <publisher>:<offer>:<sku> with the URN shown on the Marketplace listing's Usage Information tab.
Connect via SSH
ssh azureuser@<vm-public-ip>Use the private key that matches the public key you supplied at deploy time. The login user for this image is azureuser.
Verify the Pyroscope service
Confirm that the Pyroscope server, the nginx proxy and the ufw firewall are running, and check the listening sockets. Pyroscope binds port 4040 on all interfaces, as single-binary mode requires, but ufw seals the port off-box. You reach Pyroscope only through nginx on port 443. The AGPL-3.0 licence ships with the image.
pyroscope --version 2>/dev/null | head -1 systemctl is-active pyroscope nginx ufw
sudo ss -ltn | grep -E ':443 |:4040 ' head -2 /usr/local/share/pyroscope/LICENSEExpected output:
pyroscope, version 2.2.1 (branch: HEAD, revision: df3c0391b) active active active LISTEN 0 511 0.0.0.0:443 0.0.0.0:* LISTEN 0 511 [::]:443 [::]:* LISTEN 0 4096 *:4040 *:* GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007Retrieve your per-VM credentials
On the first boot of your VM, grafana-pyroscope-firstboot.service generated a Basic Auth password unique to your instance and a per-instance TLS certificate. It wrote the credentials to a root-only file. Read the file with:
sudo cat /root/grafana-pyroscope-credentials.txtThe file reports the HTTPS URL and the login:
pyroscope.url=https://<vm-public-ip>/ pyroscope.admin.user=admin pyroscope.admin.pass=<generated-per-vm>A shipped self-test confirms the whole credential model without printing any secret. It proves the HTTP-to-HTTPS redirect and the static /healthz probe, that missing, wrong and default credentials are rejected, and that the per-VM credential is accepted:
sudo bash /usr/local/sbin/pyroscope-credcheck.shExpected output:
OKUnderstand the access model
The per-VM Basic Auth credential guards every Pyroscope route. Only the static /healthz probe answers without authentication. There is no default or shared login, and no password file ships in the image, so admin/admin is rejected.
You can see the gate with curl. The -k flag accepts the per-VM self-signed certificate:
AP=$(sudo grep '^pyroscope.admin.pass=' /root/grafana-pyroscope-credentials.txt | cut -d= -f2-)
curl -ks -o /dev/null -w 'healthz (no auth) -> HTTP %{http_code}\n' https://127.0.0.1/healthz
curl -ks -o /dev/null -w 'UI no auth -> HTTP %{http_code}\n' https://127.0.0.1/
curl -ks -o /dev/null -w 'UI default admin -> HTTP %{http_code}\n' -u 'admin:admin' https://127.0.0.1/
curl -ks -o /dev/null -w 'UI per-VM admin -> HTTP %{http_code}\n' -u "admin:$AP" https://127.0.0.1/Expected output:
healthz (no auth) -> HTTP 200 UI no auth -> HTTP 401 UI default admin -> HTTP 401 UI per-VM admin -> HTTP 200You can also confirm that the host firewall seals Pyroscope's own port off-box:
sudo ufw status | grep -E 'Status|22|80|443|lo'Expected output:
Status: active 22/tcp ALLOW Anywhere 80/tcp ALLOW Anywhere 443/tcp ALLOW Anywhere Anywhere on lo ALLOW AnywherePort 4040 is deliberately absent from the allow list. It is reachable only through loopback and nginx.
Sign in to the web UI
In a browser, go to https://<vm-public-ip>/. The certificate is self-signed and generated per VM, so your browser warns on the first visit. Accept the warning, or install a CA-signed certificate as described in Step 11.
When prompted for HTTP Basic Auth, enter user admin and the pyroscope.admin.pass value from Step 5.
After you authenticate, the Pyroscope UI opens. Choose a service and a profile type, then explore the flamegraph.
See the live self-profiling flamegraph
The image runs Pyroscope in single-binary mode, so Pyroscope profiles itself and pushes those profiles back to its own store. Real profiling data is present immediately, with no external agent to install.
Confirm that the profile-type catalogue is populated, and render a CPU flamegraph over the HTTP API:
AP=$(sudo grep '^pyroscope.admin.pass=' /root/grafana-pyroscope-credentials.txt | cut -d= -f2-)
# The profile types Pyroscope is collecting about itself curl -ks -u "admin:$AP" -XPOST https://127.0.0.1/querier.v1.QuerierService/ProfileTypes \
-H 'Content-Type: application/json' -d '{}' | jq -r '.profileTypes[].ID' | head
# Render a CPU flamegraph and summarise it curl -ks -u "admin:$AP" -G https://127.0.0.1/pyroscope/render \
--data-urlencode 'query=process_cpu:cpu:nanoseconds:cpu:nanoseconds{}' \
--data-urlencode 'from=now-1h' --data-urlencode 'until=now' \
--data-urlencode 'format=json' \
| jq '{numTicks: .flamebearer.numTicks, frames: (.flamebearer.names|length), units: .metadata.units}'Expected output (values grow as the instance runs):
block:contentions:count:contentions:count block:delay:nanoseconds:contentions:count goroutines:goroutine:count:goroutine:count memory:alloc_objects:count:space:bytes memory:alloc_space:bytes:space:bytes memory:inuse_objects:count:space:bytes memory:inuse_space:bytes:space:bytes mutex:contentions:count:contentions:count mutex:delay:nanoseconds:contentions:count process_cpu:cpu:nanoseconds:cpu:nanoseconds { "numTicks": 1350000000, "frames": 662, "units": "samples" }In the web UI, select the pyroscope service and the process_cpu profile type to explore the same data as an interactive flamegraph.
Send profiles from your own applications
To profile your own services, instrument them with the Grafana profiling SDKs, or scrape existing pprof endpoints with Grafana Alloy. Point either one at your instance with the Basic Auth credential. For example, a Go application can use the Pyroscope Go SDK:
import "github.com/grafana/pyroscope-go" pyroscope.Start(pyroscope.Config{ ApplicationName: "my.go.app", ServerAddress: "https://<vm-public-ip>", BasicAuthUser: "admin", BasicAuthPassword: "<pyroscope.admin.pass>", })SDKs exist for Go, Java, Python, Ruby, .NET, Node.js and Rust. Alloy's pyroscope.scrape and pyroscope.write components can pull pprof endpoints and forward them here. Each service appears as its own entry in the UI's service selector, alongside the built-in pyroscope self-profile.
Change the Basic Auth password
The password is stored as a bcrypt hash in the nginx htpasswd file. To rotate it, regenerate the entry, which prompts for the new password, and reload nginx:
sudo htpasswd -B /etc/nginx/.pyroscope.htpasswd admin
sudo systemctl reload nginxhtpasswd -B prompts for the new password and writes a fresh bcrypt hash. If you rely on /root/grafana-pyroscope-credentials.txt, update the record there. Otherwise, keep your new password in your own secret store.
Add a custom domain and a CA-signed certificate
The image ships a per-VM self-signed certificate, so TLS works out of the box. For production, point a DNS name at the VM and install a CA-signed certificate, for example with Let's Encrypt:
sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d pyroscope.example.comCertbot obtains a certificate, rewrites the nginx TLS configuration to use it, and sets up automatic renewal. Make sure port 80 is reachable from the internet for the ACME HTTP-01 challenge.
Scale out to object storage
Single-binary mode stores profiles on the VM's local disk, which suits a self-contained instance. To retain more data or run a distributed deployment, point Pyroscope at S3-compatible object storage. Edit the service's storage flags in /etc/systemd/system/pyroscope.service, for example -storage.backend=s3 with the matching -storage.s3.* settings. Then run:
sudo systemctl daemon-reload
sudo systemctl restart pyroscopeSee the Pyroscope configuration reference for the full set of object-storage and component options.
Uninstall
To remove the VM and its resource group entirely, run the command below from a workstation with the Azure CLI:
az group delete --name pyroscope-prod --yes --no-waitSupport
SecureAnyCloud provides 24/7 technical support for this Grafana Pyroscope image by email (sales@secureanycloud.com) and live chat, with a 24-hour response SLA.
Support covers deployment and initial configuration, retrieving and rotating the Basic Auth password, adding a custom domain and CA-signed certificate, and instrumenting your applications with the Grafana profiling SDKs and Alloy. It also covers reading and interpreting flamegraphs, scaling out to object storage, retention and performance tuning, upgrades and patch management, and troubleshooting.
All product and company names are trademarks or registered trademarks of their respective holders. Their use does not imply any affiliation with or endorsement by them.
Deploy on Azure
Launch Grafana Pyroscope on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com