Up
Observability Azure

Uptrace on Ubuntu 24.04 on Azure User Guide

| Product: Uptrace on Ubuntu 24.04 LTS on Azure

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.3 from the official pinned container image uptrace/uptrace:2.0.3, baked into the image and published on loopback 127.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), PostgreSQL 17 (metadata store) and Redis 7 (query cache), on a private container network
  • nginx terminating TLS on :443 in front of Uptrace (UI + OTLP HTTP), with :80 redirecting 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.local login never exists
  • docker.service, uptrace-firstboot.service, uptrace.service and nginx.service as 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.

Uptrace login page served over HTTPS

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

Uptrace services, container stack, loopback + nginx binding, and HTTPS health on the VM

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

Secure by default: the per-VM credentials file and a live login round-trip

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:

The Uptrace traces list populated with spans from several services

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:

A distributed trace opened in Uptrace showing its span tree

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

The Uptrace metrics explorer charting an ingested metric

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

The pinned Uptrace container images baked into the VM

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.

The OS security baseline and the running Uptrace stack

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.