Perses on Ubuntu 24.04 on Azure User Guide
Overview
Perses is an open source dashboard and observability visualization platform and a Cloud Native Computing Foundation project. Like Grafana, it renders rich dashboards over your observability data, currently supporting Prometheus metrics, Tempo traces, Loki logs and Pyroscope profiles. What sets Perses apart is its dashboards as code approach: dashboards follow an open, standardized specification and can be authored, validated and version controlled as code with its native CLI and SDKs, making it a natural fit for GitOps workflows. It ships as a single self contained Go server with the React user interface embedded and a file backend for projects and dashboards, so it stays lightweight and starts instantly.
The cloudimg image installs Perses 0.53.1 and puts it behind an nginx reverse proxy that binds the server to loopback. The appliance is secure by default: because a dashboard platform should not be left open, Perses is reachable only through nginx on port 80 behind an HTTP Basic Auth gate whose password is generated uniquely and at random on the first boot of every VM, so no shared or default credential ever ships in the image. A per instance at rest encryption key is generated the same way. A demo project and a sample dashboard are seeded so the interface is populated on first boot, and you can immediately replace them with your own datasources and dashboards.
What is included:
- Perses 0.53.1 single Go server with the React UI embedded (
/opt/perses/perses) perclicommand line client for dashboards as code (/opt/perses/percli)- File backend for projects and dashboards at
/opt/perses/data(no external database to maintain) perses.servicerunning asperses, bound to loopback127.0.0.1:8080nginx.servicereverse proxy on TCP 80 with a per VM HTTP Basic Auth gate, TLS readyperses-firstboot.servicegenerating the per VM password and encryption key on first boot- A seeded
demoproject and awelcomedashboard so the UI is populated on first boot - An unauthenticated
/healthzendpoint for load balancer probes - Ubuntu 24.04 LTS base, latest patches, unattended security upgrades enabled
- 24/7 cloudimg support, 24h response SLA
Prerequisites
An active Azure subscription, an SSH key, and a VNet with a subnet. Recommended VM size: Standard_B2s (Perses is light; 4 GB RAM is plenty).
Step 1: Deploy from the Azure Portal
Search the Azure Marketplace for Perses, choose the plan, and create the VM. In the networking step attach an NSG that allows inbound TCP 22 (SSH) and TCP 80 (the UI, behind Basic Auth) from your client networks only. For production, put a TLS reverse proxy in front of port 80 (Step 16).
Step 2: Deploy from the Azure CLI
RG="perses-prod"; LOCATION="eastus"; VM_NAME="perses-01"
GALLERY_IMAGE_ID="/subscriptions/<sub-id>/resourceGroups/azure-cloudimg/providers/Microsoft.Compute/galleries/cloudimgGallery/images/perses-ubuntu-24-04/versions/<version>"
SSH_KEY="$(cat ~/.ssh/id_rsa.pub)"
az group create --name "$RG" --location "$LOCATION"
az network vnet create -g "$RG" --name ps-vnet --address-prefix 10.108.0.0/16 --subnet-name ps-subnet --subnet-prefix 10.108.1.0/24
az network nsg create -g "$RG" --name ps-nsg
az network nsg rule create -g "$RG" --nsg-name ps-nsg --name allow-ssh --priority 100 \
--source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 22 --access Allow --protocol Tcp
az network nsg rule create -g "$RG" --nsg-name ps-nsg --name allow-web --priority 110 \
--source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 80 --access Allow --protocol Tcp
az vm create -g "$RG" --name "$VM_NAME" --image "$GALLERY_IMAGE_ID" \
--size Standard_B2s --storage-sku StandardSSD_LRS \
--admin-username azureuser --ssh-key-values "$SSH_KEY" \
--vnet-name ps-vnet --subnet ps-subnet --nsg ps-nsg --public-ip-sku Standard
Step 3: Connect via SSH
ssh azureuser@<vm-ip>
perses.service, nginx.service and perses-firstboot.service all start automatically on first boot.
Step 4: Verify the Service
sudo systemctl is-active perses nginx perses-firstboot
sudo test -f /var/lib/cloudimg/perses-firstboot.done && echo FIRSTBOOT_DONE
sudo ss -tlnp | grep -E ':80 |8080'
Expected output — all three services are active, nginx is bound to the public port 80 and Perses is bound to loopback 127.0.0.1:8080 only:
active
active
active
FIRSTBOOT_DONE
LISTEN 0 4096 127.0.0.1:8080 0.0.0.0:* users:(("perses",...))
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",...))

Step 5: Retrieve Your Per-VM Password
The web UI is protected by an HTTP Basic Auth password that is generated uniquely on this VM's first boot. Read it, along with the URL and username, from the root only credentials file:
sudo cat /root/perses-credentials.txt
The file holds the username (admin), this VM's URL and the per VM password. No credential is baked into the image — every VM gets its own — and Perses itself is bound to loopback so the only way in is through the nginx password gate:
PERSES_URL=http://<vm-ip>/
PERSES_USERNAME=admin
PERSES_PASSWORD=<PERSES_PASSWORD>
An unauthenticated request is rejected, and Perses listens only on loopback:

Step 6: Open the Perses UI
Open http://<vm-ip>/ in a browser and sign in as admin with the password from Step 5. The home page lists your projects and recently viewed dashboards, and the demo project seeded by the image is already there:

Step 7: Explore the Demo Project
Click the demo project. A project groups dashboards, variables, datasources and secrets. The seeded Welcome dashboard is listed and ready to open:

Step 8: Open the Welcome Dashboard
Open the Welcome dashboard. It renders the sample Markdown panels seeded by the image, so the interface is populated the moment the VM boots. This is the dashboard view you will use for your own metrics, traces, logs and profiles:

Step 9: Edit Dashboards in the Browser
Click Edit to open the dashboard in edit mode. From here you add panels and panel groups, wire them to variables and datasources, and save — the same dashboard model you can also manage as code with percli (Step 12):

Step 10: Use the REST API
Everything in the UI is also available through the JSON REST API, behind the same per VM password. List your projects and a project's dashboards:
curl -s -u admin:<PERSES_PASSWORD> http://127.0.0.1/api/v1/projects | jq '[.[].metadata.name]'
curl -s -u admin:<PERSES_PASSWORD> http://127.0.0.1/api/v1/projects/demo/dashboards | jq '[.[].metadata.name]'
The seeded demo project and its welcome dashboard are returned:
[
"demo"
]
[
"welcome"
]

Step 11: Create a Project and Dashboard via the API
You can create resources entirely through the API. This example creates a myteam project and a first dashboard with a Markdown panel, then reads it back:
PW='<PERSES_PASSWORD>'
curl -s -u admin:$PW -H 'Content-Type: application/json' -X POST http://127.0.0.1/api/v1/projects \
-d '{"kind":"Project","metadata":{"name":"myteam"}}' -o /dev/null -w 'create project: %{http_code}\n'
curl -s -u admin:$PW -H 'Content-Type: application/json' -X POST http://127.0.0.1/api/v1/projects/myteam/dashboards \
-d '{"kind":"Dashboard","metadata":{"name":"first","project":"myteam"},"spec":{"duration":"1h","panels":{"n":{"kind":"Panel","spec":{"display":{"name":"Notes"},"plugin":{"kind":"Markdown","spec":{"text":"# My first dashboard"}}}}},"layouts":[{"kind":"Grid","spec":{"items":[{"x":0,"y":0,"width":24,"height":6,"content":{"$ref":"#/spec/panels/n"}}]}}]}}' \
-o /dev/null -w 'create dashboard: %{http_code}\n'
curl -s -u admin:$PW http://127.0.0.1/api/v1/projects/myteam/dashboards/first | jq '.metadata.name'
Both create calls return 200 and the dashboard reads back as "first".
Step 12: Dashboards as Code with percli
Perses ships percli, its command line client, so you can manage dashboards as code and wire them into CI. Point it at your instance (through the password gate) and apply resources from files:
/opt/perses/percli login http://admin:<PERSES_PASSWORD>@127.0.0.1
/opt/perses/percli apply -f my-dashboard.json
/opt/perses/percli get dashboards --project demo
Because a Perses dashboard is a plain declarative document, you keep it in Git, validate it in CI and apply it on merge — the core of the dashboards as code workflow.
Step 13: Add a Datasource
To visualize live data, add a datasource to a project (for example a Prometheus endpoint) from the project's Datasources tab in the UI, or as a resource file. A project scoped Prometheus datasource looks like this:
kind: Datasource
metadata:
name: prometheus
project: demo
spec:
default: true
plugin:
kind: PrometheusDatasource
spec:
directUrl: http://your-prometheus:9090
Once a datasource exists, add a TimeSeriesChart, StatChart or GaugeChart panel to a dashboard and query it — the panel plugins for metrics, traces, logs and profiles all ship in the image.
Step 14: Server Components
| Component | Path |
|---|---|
| Perses server binary | /opt/perses/perses |
| percli client | /opt/perses/percli |
| Config file | /etc/perses/config.yaml |
| File database (projects/dashboards) | /opt/perses/data |
| Bundled plugin archives | /opt/perses/plugins-archive |
| Systemd unit | /etc/systemd/system/perses.service |
| Firstboot script | /usr/local/sbin/perses-firstboot.sh |
| Per VM credentials note | /root/perses-credentials.txt (mode 0600) |
| nginx Basic Auth file | /etc/nginx/.perses.htpasswd (per VM bcrypt) |
ls -1 /opt/perses/perses /opt/perses/percli /etc/perses/config.yaml /opt/perses/data
curl -sI http://127.0.0.1/healthz | head -1

Step 15: Managing the Service
sudo systemctl restart perses.service
sudo journalctl -u perses.service --no-pager -n 15
Perses is a single binary with a local file database, so it restarts in a second or two. The service is hardened with NoNewPrivileges, ProtectSystem=full and ProtectHome, and only has write access to its own directory.
Step 16: Add TLS (Optional)
For production, terminate TLS at nginx. Point a DNS record at the VM, then use the packaged nginx with a certificate from Let's Encrypt:
sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d <your-domain>
certbot installs the certificate, rewrites the nginx server block to listen on 443 and sets up automatic renewal. Keep the Basic Auth gate in place so the UI stays private over HTTPS.
Step 17: Change or Rotate the Password
The password lives in the nginx Basic Auth file. To set a new one, regenerate the entry with htpasswd (bcrypt) and reload nginx:
sudo htpasswd -B /etc/nginx/.perses.htpasswd admin
sudo systemctl reload nginx
Update /root/perses-credentials.txt if you want the note to reflect the new value. The password is only ever stored on this VM.
Step 18: Security Recommendations
- Restrict the NSG so ports 80 and 22 only reach trusted networks.
- Terminate TLS at nginx (Step 16) so the UI and API travel encrypted.
- Keep Perses on loopback — it is served only through the nginx password gate; do not bind it to a public interface.
- Rotate the password (Step 17) and store it in your secrets manager.
- Back up
/opt/perses/datato Azure Blob so your projects and dashboards survive a rebuild. - Patch the OS monthly with
sudo apt-get update && sudo apt-get upgrade; unattended security upgrades are already enabled.
Step 19: Support and Licensing
Perses is licensed under the Apache License 2.0 — no per CPU or per user fee. cloudimg provides commercial support separately.
- Email: support@cloudimg.co.uk
- Website: www.cloudimg.co.uk
- Support hours: 24/7, 24h response SLA
Deploy on Azure
Launch Perses on Ubuntu 24.04 with 24/7 support from cloudimg.
View on Marketplace
Need Help?
Our support team is available 24/7. support@cloudimg.co.uk