Developer Tools Azure

Temporal on Ubuntu 24.04 on Azure User Guide

| Product: Temporal on Ubuntu 24.04 LTS on Azure

Overview

Temporal is an open source durable execution engine. Your application defines Workflows and Activities and runs them against the Temporal server, which persists every execution's full event history so work survives process and host failures and resumes exactly where it left off, with no bespoke retry, queue or state machine code. The cloudimg image ships the MIT licensed Temporal server and its MIT licensed Web UI, backed by a bundled PostgreSQL, run the officially supported way as upstream containers orchestrated by Docker Compose under systemd and fronted by nginx. All images are pinned by digest and captured into the VM, so your instance starts in seconds.

Temporal's open source Web UI has no built in authentication, so cloudimg fronts it with nginx HTTP Basic Auth using a unique password generated for each VM on first boot, before the port is reachable. The Temporal frontend and the bundled PostgreSQL are bound to the loopback interface only. Backed by 24/7 cloudimg support.

Temporal is a trademark of Temporal Technologies Inc. This image is produced by cloudimg and is not affiliated with, endorsed by, or sponsored by Temporal Technologies Inc. It ships only the MIT licensed open source Temporal server and Web UI.

The Temporal Web UI workflows list showing completed workflow executions in the default namespace

What is included:

  • Temporal server 1.31.2 (the MIT licensed engine: frontend, history, matching and worker services), pinned by image digest
  • Temporal Web UI 2.52.1 (namespaces, workflows, event history and schedules), pinned by image digest
  • A bundled PostgreSQL 16 database, pinned by image digest, reachable only inside a private Docker network and never published to a host port
  • Docker Engine (Docker CE) with the Temporal frontend and Web UI published to the loopback interface only, fronted by nginx on port 80
  • temporal.service, temporal-firstboot.service and nginx.service as systemd units, enabled and active on boot
  • A unique Web UI password (nginx HTTP Basic Auth, bcrypt) and a unique PostgreSQL password generated per VM on first boot, never baked into the image
  • A clean, freshly initialised database and a pre registered default namespace on first boot: no default login, no shipped secret, no prior data
  • 24/7 cloudimg support

Prerequisites

An active Azure subscription, an SSH key pair, and a VNet plus subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a sensible starting point; increase the size for heavier workloads. NSG inbound: allow 22/tcp from your management network and 80/tcp (and 443/tcp once you add TLS) for the Web UI. The Web UI serves plain HTTP on port 80; for production, put it behind TLS with your own domain (see the final section).

Step 1 - Deploy from the Azure Marketplace

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Temporal 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) and HTTP (80). Then Review + create and Create.

Step 2 - Deploy from the Azure CLI

az vm create \
  --resource-group <your-rg> \
  --name temporal \
  --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 temporal --port 80 --priority 1010

Step 3 - Connect to your VM

ssh azureuser@<vm-public-ip>

Step 4 - Confirm the services are running

On first boot the image generates a unique Web UI password and database password, initialises a clean PostgreSQL database, applies the Temporal schema, registers the default namespace and starts the stack. Confirm the systemd services are active:

systemctl is-active docker temporal nginx

All three services report active. The public liveness endpoint returns 200 with no credentials, while the Temporal frontend and the bundled PostgreSQL stay bound to the loopback interface only:

curl -s -o /dev/null -w 'health: HTTP %{http_code}\n' http://127.0.0.1/healthz

The full stack runs as containers under one Docker Compose project (the server, the Web UI, the admin tools and the bundled PostgreSQL):

The Temporal server, Web UI, admin tools and bundled PostgreSQL containers running under systemd

Step 5 - Secure by default: the Web UI password gates everything

Temporal's open source Web UI has no built in authentication, so cloudimg fronts it with nginx HTTP Basic Auth. Any request to the Web UI or its API without the exact per-VM credential is rejected with 401. Confirm it yourself - this request sends no credential:

curl -s -o /dev/null -w 'no credential: HTTP %{http_code}\n' http://127.0.0.1/api/v1/namespaces

It returns HTTP 401. Guessable defaults such as admin:admin are rejected too. Your unique credential authenticates and returns 200; the block below reads the password from the notes file (so it never appears on screen):

P=$(sudo grep '^TEMPORAL_UI_PASSWORD=' /root/temporal-notes.txt | cut -d= -f2-)
curl -s -o /dev/null -w 'authorized: HTTP %{http_code}\n' -u "admin:$P" http://127.0.0.1/api/v1/namespaces

The Web UI API rejects no credential and a guessable default (401) and accepts only the per-VM credential (200); health stays public

Your unique Web UI user and password are written to /root/temporal-notes.txt, readable only by root:

sudo stat -c '%a %U:%G' /root/temporal-notes.txt

It reports 600 root:root. View the notes (which include the Web UI user, the Web UI password and the URL) with:

sudo cat /root/temporal-notes.txt

The per-VM access notes, with the Web UI password and database password masked

Step 6 - Open the Temporal Web UI

Browse to http://<vm-public-ip>/. Your browser prompts for the Basic Auth credential - enter the user admin and the password from /root/temporal-notes.txt. The Web UI opens on the workflows list for the default namespace, showing every running and closed workflow execution.

The Temporal Web UI workflows list for the default namespace

Select any workflow to open its detail page: its status, input and result, the full event history, and a timeline of the activities it ran. This history is what makes execution durable - it is persisted to PostgreSQL and replayed to resume a workflow exactly where it left off.

A completed workflow's detail page showing status, input and result, event history and the activity timeline

The Namespaces page lists the namespaces on the server. A namespace is an isolation boundary for your workflows; the image registers a default namespace for you on first boot.

The Temporal Web UI namespaces page showing the pre registered default namespace

Step 7 - Run and inspect workflows

Point your Temporal SDK workers and clients at the frontend on port 7233 (bound to the loopback interface; reach it over an SSH tunnel or your own private network). The bundled temporal command line, available through the admin tools container, lists and describes executions. For example, to list recent workflows in the default namespace:

docker compose -f /etc/temporal-appliance/compose.yaml exec temporal-admin-tools \
  temporal workflow list --namespace default

Each row shows the workflow's status, id, type and start time - durable, queryable execution state served from PostgreSQL.

Completed workflow executions listed from the command line through the admin tools container

Step 8 - Schedules

The Schedules page manages recurring workflows. A schedule runs a workflow on an interval or a calendar spec and records each run, so you can drive nightly jobs and periodic pipelines without an external cron.

The Temporal Web UI schedules page showing a recurring schedule and its upcoming runs

Step 9 - Rotate the Web UI password

The Web UI password is your access key. To set your own, rewrite the Basic Auth file and reload nginx:

sudo htpasswd -B /etc/nginx/temporal.htpasswd admin
sudo systemctl reload nginx

Step 10 - Production: your own domain with TLS

For production, front the Web UI with your own domain and TLS. Point a DNS record at the VM public IP, then install a certificate with your preferred tool (for example Certbot with the nginx plugin) and reload nginx. Restrict inbound access in your NSG to 443/tcp (and 22/tcp from your management network) once TLS is in place, and keep the Web UI credential confidential - it grants full access to your namespaces, workflows and their inputs and results.

Support

This image is supported 24/7 by cloudimg. For help with deployment or configuration, contact support through cloudimg.co.uk.