Target stack: Ubuntu 24.04 LTS, Pyroscope server as a single-binary (monolithic) systemd service, fronted by Caddy for TLS, with optional Grafana integration.
Pyroscope is Grafana's continuous profiling backend. For a single VPS, run it in monolithic mode (all components in one process) — this is fine up to moderate scale and is what Grafana recommends for anything short of a dedicated cluster.
1. Prerequisites
sudo apt update && sudo apt upgrade -y
sudo apt install -y curl wget tar ufwsudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enablePyroscope's own HTTP API/UI listens on port 4040 by default — we'll keep that bound to localhost and reverse-proxy it.
2. Create a system user and directories
sudo groupadd --system pyroscope
sudo useradd --system --gid pyroscope --no-create-home \
--shell /usr/sbin/nologin pyroscope
sudo mkdir -p /etc/pyroscope /var/lib/pyroscope
sudo chown -R pyroscope:pyroscope /var/lib/pyroscope3. Download and install the Pyroscope binary
Check the Pyroscope releases page for the latest version and substitute it below.
cd /tmp
PYROSCOPE_VERSION="1.9.2" # check for latest before running
wget "https://github.com/grafana/pyroscope/releases/download/v${PYROSCOPE_VERSION}/pyroscope_${PYROSCOPE_VERSION}_linux_amd64.tar.gz"
tar -xzf "pyroscope_${PYROSCOPE_VERSION}_linux_amd64.tar.gz"
sudo mv pyroscope /usr/local/bin/pyroscope
sudo chmod +x /usr/local/bin/pyroscope
pyroscope --version4. Configure Pyroscope
sudo tee /etc/pyroscope/config.yaml > /dev/null <<'EOF'
target: all # monolithic mode: runs all components in one process
server:
http_listen_address: "127.0.0.1:4040"
storage:
type: filesystem
filesystem:
dir: /var/lib/pyroscope
# Retention: keep 14 days of profiles by default
limits:
compaction-block-selection-max-series-count: 100000
max-profile-size-bytes: 20000000
pyroscopedb:
retention-policy:
default-limit: 336h # 14 days
EOF
sudo chown -R pyroscope:pyroscope /etc/pyroscopeFor real workloads, consider object storage (S3-compatible) instead of the local filesystem backend — add a
storage.s3(or compatible provider) block if you want profiles to survive disk loss.
5. Create the systemd service
sudo tee /etc/systemd/system/pyroscope.service > /dev/null <<'EOF'
[Unit]
Description=Grafana Pyroscope (continuous profiling)
After=network.target
[Service]
Type=simple
User=pyroscope
Group=pyroscope
ExecStart=/usr/local/bin/pyroscope --config.file=/etc/pyroscope/config.yaml
Restart=on-failure
RestartSec=5
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now pyroscope
sudo systemctl status pyroscope6. Front it with Caddy (TLS + reverse proxy)
If Caddy isn't already installed:
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update && sudo apt install -y caddyEdit /etc/caddy/Caddyfile:
pyroscope.example.com {
reverse_proxy 127.0.0.1:4040
}sudo systemctl reload caddyNow https://pyroscope.example.com serves the Pyroscope UI over TLS with an automatically issued cert.
Security note: Pyroscope has no built-in authentication in OSS mode. Put it behind Caddy's
basicauthdirective or an SSO proxy before exposing it publicly:shellpyroscope.example.com { basicauth { admin $2a$14$hashedpasswordhere } reverse_proxy 127.0.0.1:4040 }Generate the hash with
caddy hash-password.
7. Send profiles to Pyroscope
Option A — Pull-based agent (recommended for whole-system/eBPF profiling)
Grafana Alloy can scrape profiles from your applications and this host and push them in. Install Alloy:
curl -O https://raw.githubusercontent.com/grafana/alloy/main/scripts/install.sh
sudo apt install -y ./alloy_*.deb # or follow the official Alloy install docs for the repo methodIn your Alloy config, add a pyroscope.write block pointing at http://127.0.0.1:4040 and a pyroscope.scrape or pyroscope.ebpf component to collect from your services.
Option B — Push-based SDK (per-language)
Add the Pyroscope client library to your app and point it at the server, e.g. for a Go service:
import "github.com/grafana/pyroscope-go"
pyroscope.Start(pyroscope.Config{
ApplicationName: "my.service",
ServerAddress: "https://pyroscope.example.com",
})Similar SDKs exist for Python, Ruby, Java, .NET, Node.js, and Rust — see the Pyroscope docs for the exact package per language.
8. Connect Grafana (optional, if you run Grafana separately)
In Grafana: Connections → Data sources → Add data source → Pyroscope, and set the URL to https://pyroscope.example.com. You can then explore flame graphs directly in Grafana Explore, or add profiling panels to dashboards.
9. Verify
curl -s http://127.0.0.1:4040/ready
# Should return: readyOpen https://pyroscope.example.com in a browser — you should see the Pyroscope UI, and once an app is instrumented, its profiles under Applications.
10. Backups and updates
- Back up
/var/lib/pyroscope(or your object storage bucket) and/etc/pyroscope/config.yaml. - To upgrade: download the new release binary, replace
/usr/local/bin/pyroscope, thensudo systemctl restart pyroscope.
