Uptrace on Ubuntu 24.04 on Azure User Guide
Overview
Uptrace is an open-source application performance monitoring and observability platform built around OpenTelemetry. Your applications and agents send traces, metrics and logs to its OTLP endpoints, and Uptrace stores them in a columnar backend so you can search spans, chart metrics and correlate a slow trace with the logs and metrics around it - all from one web interface. The cloudimg image runs the full stack behind nginx over HTTPS: the Uptrace server is published on the loopback interface only, and nginx terminates TLS in front of it. Backed by 24/7 cloudimg support.
What is included:
- Uptrace
v2.0.3from the official pinned container imageuptrace/uptrace:2.0.3, baked into the image and published on loopback127.0.0.1:8080 - Its backing stores, all pinned and baked in: ClickHouse
26.3(telemetry store, memory-capped to fit a 4 GiB VM), PostgreSQL17(metadata store) and Redis7(query cache), on a private container network - nginx terminating TLS on
:443in front of Uptrace (UI + OTLP HTTP), with:80redirecting to HTTPS, and OTLP gRPC on:4317 - A per-VM administrator account, per-VM database passwords, a per-VM OTLP ingest token and a per-VM signing secret, all generated on first boot - no default or shared credential ships in the image, and the upstream default
admin@uptrace.locallogin never exists docker.service,uptrace-firstboot.service,uptrace.serviceandnginx.serviceas systemd units, enabled and active- 24/7 cloudimg support
Uptrace is distributed under the AGPL-3.0 open-source licence.
Secure by default - per-VM credentials
This image ships with no default or shared secret: no databases, no user accounts, no ingest token and no signing secret ship in the image. On first boot a one-shot service resolves your public URL, regenerates a per-VM TLS certificate, mints a unique ClickHouse password, PostgreSQL password, administrator password, OTLP project token and service signing secret, and renders them into the configuration before anything is reachable. The Uptrace stack is deliberately held back until that has happened, so there is never a window in which a fresh VM is reachable with a well-known default login. You retrieve the credentials over SSH and change the password after your first login.

Prerequisites
An active Azure subscription, an SSH key pair, and a VNet + subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a comfortable starting point (the full stack idles at roughly 1.2 GiB with ClickHouse memory-capped); scale up for heavier ingest volumes. NSG inbound: allow 22/tcp from your management network, 80/tcp + 443/tcp from wherever you browse the UI and send OTLP-over-HTTP, and 4317/tcp from wherever your applications send OTLP-over-gRPC (:80 only redirects to :443).
Step 1 - Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Uptrace by cloudimg, and select Create. On Basics pick your subscription, resource group, region and size; under Administrator account choose SSH public key and paste your key; under Inbound port rules allow SSH (22), HTTP (80) and HTTPS (443). Then Review + create -> Create. Open 4317/tcp afterwards if you will send OTLP over gRPC.
Step 2 - Deploy from the Azure CLI
az vm create \
--resource-group <your-rg> \
--name uptrace \
--image <marketplace-image-urn> \
--size Standard_B2s \
--admin-username azureuser \
--ssh-key-values ~/.ssh/id_ed25519.pub \
--vnet-name <your-vnet> --subnet <your-subnet> \
--public-ip-sku Standard
az vm open-port --resource-group <your-rg> --name uptrace --port 80 --priority 1010
az vm open-port --resource-group <your-rg> --name uptrace --port 443 --priority 1020
az vm open-port --resource-group <your-rg> --name uptrace --port 4317 --priority 1030
Step 3 - Connect to your VM
ssh azureuser@<vm-public-ip>
The message of the day shows your VM's Uptrace URL, administrator e-mail and OTLP endpoints.
Step 4 - Confirm the services are running
Uptrace runs as systemd units - the Docker engine, the first-boot provisioner, the compose stack and nginx:
systemctl is-active docker.service uptrace.service nginx.service
active
active
active
The four containers - Uptrace and its ClickHouse, PostgreSQL and Redis backends - are managed by docker compose. Uptrace is published only on the loopback interface; the databases are on a private network with no host ports:
cd /opt/uptrace-appliance && sudo docker compose ps --format 'table {{.Service}}\t{{.Status}}'
SERVICE STATUS
clickhouse Up (healthy)
postgres Up (healthy)
redis Up (healthy)
uptrace Up
nginx is the only service bound to public web ports (:80 and :443); the Uptrace UI is on loopback, and OTLP gRPC is on :4317:
sudo ss -tln | grep -E ':(80|443|4317|8080) '
LISTEN 0 4096 127.0.0.1:8080 0.0.0.0:*
LISTEN 0 4096 0.0.0.0:4317 0.0.0.0:*
LISTEN 0 511 0.0.0.0:443 0.0.0.0:*
LISTEN 0 511 0.0.0.0:80 0.0.0.0:*
The web UI is served over HTTPS, and plain HTTP redirects to it:
curl -sk -o /dev/null -w 'https:// -> %{http_code}\n' https://127.0.0.1/
curl -s -o /dev/null -w 'http:// -> %{http_code}\n' http://127.0.0.1/
https:// -> 200
http:// -> 301

Step 5 - Read the per-VM credentials
On first boot the VM writes a root-only file with your Uptrace URL, the administrator e-mail, the OTLP ingest token and the OTLP endpoints:
sudo grep -E '^UPTRACE_URL=|^UPTRACE_ADMIN_EMAIL=|^UPTRACE_OTLP_GRPC=' /root/uptrace-credentials.txt
UPTRACE_URL=https://<vm-public-ip>
UPTRACE_ADMIN_EMAIL=admin@cloudimg.local
UPTRACE_OTLP_GRPC=<vm-public-ip>:4317
The UPTRACE_ADMIN_PASSWORD and UPTRACE_OTLP_TOKEN lines in that file hold your unique password and ingest token. Only the per-VM credential authenticates - a wrong password, and the upstream default admin@uptrace.local / admin, are both rejected:
E=$(sudo grep '^UPTRACE_ADMIN_EMAIL=' /root/uptrace-credentials.txt | cut -d= -f2-)
P=$(sudo grep '^UPTRACE_ADMIN_PASSWORD=' /root/uptrace-credentials.txt | cut -d= -f2-)
L() { curl -sk -o /dev/null -w "$1 -> HTTP %{http_code}\n" -X POST https://127.0.0.1/internal/v1/users/login \
-H 'Content-Type: application/json' --data "$2"; }
L 'per-VM password' "{\"email\":\"$E\",\"password\":\"$P\"}"
L 'wrong password ' "{\"email\":\"$E\",\"password\":\"wrong-nope\"}"
L 'upstream default' '{"email":"admin@uptrace.local","password":"admin"}'
per-VM password -> HTTP 200
wrong password -> HTTP 400
upstream default -> HTTP 400

Step 6 - First login
Open Uptrace in your browser (accept the self-signed certificate warning, or install a trusted certificate first - see below):
https://<vm-public-ip>/
Enter the administrator e-mail (admin@cloudimg.local) and the UPTRACE_ADMIN_PASSWORD from Step 5, then sign in. Change your password from your profile settings immediately after your first sign-in.
Step 7 - Send telemetry to Uptrace
Uptrace ingests OpenTelemetry data over OTLP - gRPC on :4317 and HTTP on :443 (/v1/traces, /v1/metrics, /v1/logs). Every request is authenticated by your project's DSN, which embeds the per-VM ingest token. The DSN is written to your credentials file:
sudo grep '^UPTRACE_DSN=' /root/uptrace-credentials.txt
UPTRACE_DSN=https://<uptrace-otlp-token>@<vm-public-ip>?grpc=4317
You can prove the pipeline end-to-end from the VM itself: send a test span over OTLP-HTTP with your token and a current timestamp, and Uptrace accepts it (HTTP 200):
T=$(sudo grep '^UPTRACE_OTLP_TOKEN=' /root/uptrace-credentials.txt | cut -d= -f2-)
NOW=$(date +%s)000000000
curl -sk -o /dev/null -w 'OTLP ingest -> HTTP %{http_code}\n' https://127.0.0.1/v1/traces \
-H 'Content-Type: application/json' \
-H "uptrace-dsn: https://$T@127.0.0.1?grpc=4317" \
-d "{\"resourceSpans\":[{\"resource\":{\"attributes\":[{\"key\":\"service.name\",\"value\":{\"stringValue\":\"demo\"}}]},\"scopeSpans\":[{\"spans\":[{\"traceId\":\"c0ffee00038103d269b633813f0000aa\",\"spanId\":\"dec0de7ec3c100aa\",\"name\":\"demo-span\",\"kind\":2,\"startTimeUnixNano\":\"$NOW\",\"endTimeUnixNano\":\"$NOW\"}]}]}]}"
OTLP ingest -> HTTP 200
An OTLP request sent with a bad token is rejected (HTTP 403), so telemetry ingest is authenticated:
curl -sk -o /dev/null -w 'bad token -> HTTP %{http_code}\n' https://127.0.0.1/v1/traces \
-H 'Content-Type: application/json' \
-H 'uptrace-dsn: https://deadbeefdeadbeef@127.0.0.1?grpc=4317' \
-d '{"resourceSpans":[{"resource":{"attributes":[]},"scopeSpans":[{"spans":[{"traceId":"c0ffee00038103d269b633813f0000bb","spanId":"dec0de7ec3c100bb","name":"x","kind":2,"startTimeUnixNano":"1","endTimeUnixNano":"1"}]}]}]}'
bad token -> HTTP 403
To send data from your own applications, point any OpenTelemetry SDK or the OpenTelemetry Collector at the DSN above. For example, with the OTLP exporter environment variables:
OTEL_EXPORTER_OTLP_ENDPOINT=https://<vm-public-ip>:4317
OTEL_EXPORTER_OTLP_HEADERS=uptrace-dsn=https://<uptrace-otlp-token>@<vm-public-ip>?grpc=4317
OTEL_EXPORTER_OTLP_PROTOCOL=grpc
Make sure your telemetry uses current timestamps - Uptrace applies retention to stored data, so spans stamped far in the past are dropped.
Exploring traces and metrics
Once telemetry is flowing, the Traces & Logs view lists incoming spans with their service, operation, duration and status, and lets you filter and group with Uptrace's query language:

Selecting a trace opens the full span tree, so you can see where time went across services and drill into an individual span's attributes:

The Metrics explorer charts any metric you have ingested, so you can build dashboards and alerts alongside your traces:

Step 8 - The official pinned container images
Uptrace and its backends run from official upstream container images, each pinned to an exact version and baked into the VM image (no runtime pull):
cd /opt/uptrace-appliance && sudo docker compose images
CONTAINER REPOSITORY TAG PLATFORM IMAGE ID SIZE
uptrace-clickhouse-1 clickhouse/clickhouse-server 26.3 linux/amd64 2ef11bbe2e44 266MB
uptrace-postgres-1 postgres 17-alpine linux/amd64 742f40ea20b9 117MB
uptrace-redis-1 redis 7-alpine linux/amd64 e7723ff73d96 16.3MB
uptrace-uptrace-1 uptrace/uptrace 2.0.3 linux/amd64 5e42ea5f3bb9 57.1MB

Adding projects and rotating the ingest token
Uptrace groups telemetry into projects, each with its own ingest token. The appliance ships one project ("Project1") with your per-VM token. To add projects, change token values or add users, edit the seed data in /opt/uptrace-appliance/config.yml (the seed_data: block), then apply it with cd /opt/uptrace-appliance && sudo docker compose restart uptrace. Administrators can also manage users from the UI.
Security updates
The image is captured fully patched (including Ubuntu phased updates) and unattended-upgrades stays enabled, so security patches keep flowing on your VM. There should be no held-back packages:
apt-mark showhold
The docker and nginx packages are managed by systemd units; the applications themselves are the pinned container images described above.

Your data
Uptrace stores telemetry in the ClickHouse volume and metadata in the PostgreSQL volume, both docker-managed on the VM's OS disk:
sudo docker volume ls --filter name=uptrace_
Snapshot the VM's OS disk in Azure to back up your telemetry, or use ClickHouse and PostgreSQL backup tooling for a finer-grained schedule.
Enabling a trusted TLS certificate
The image ships a per-VM self-signed certificate so HTTPS works out of the box; browsers will warn until you install a trusted certificate. For production, point a DNS A record at the VM's public IP, ensure 443/tcp is open in the NSG, then install certbot and let it manage the nginx certificate. Replace the placeholders with your own domain and email:
sudo apt-get update
sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.example.com -m you@example.com --agree-tos
certbot configures the HTTPS server block and sets up automatic renewal. After it completes, set site.url in /opt/uptrace-appliance/config.yml to https://your-domain.example.com and run cd /opt/uptrace-appliance && sudo docker compose restart uptrace, and Uptrace is available at your domain with a trusted certificate.
Upgrading Uptrace
Uptrace runs from pinned container images. To upgrade, edit the image tag in /opt/uptrace-appliance/docker-compose.yml, then cd /opt/uptrace-appliance && sudo docker compose pull uptrace && sudo docker compose up -d. The database schema migrates automatically on start. Always snapshot the OS disk first. cloudimg support can assist with planning and performing upgrades.
Support
This image is backed by 24/7 cloudimg support covering deployment, upgrades, OpenTelemetry integration, TLS termination and user administration. Contact us by email and chat.
Uptrace is a trademark of its respective owner. All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.