Ontime on Ubuntu 24.04 on Azure User Guide
Overview
This guide covers the deployment and configuration of Ontime on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images. Ontime is an open source, self hosted application for managing rundowns, event timers and cues for live events, broadcast productions and theatre shows. An operator drives the show from a control view while a set of realtime display screens — a large stage timer for presenters, a backstage view for crew, a studio clock, a timeline and public information screens — stay in sync across the network.
The image installs the official Ontime server (the @getontime/cli headless build, pinned at v4.11.0) and runs it as the non root ontime user under systemd, bound to loopback behind an nginx reverse proxy on port 80. Ontime's entire UI and every display view are driven over a WebSocket transport, so the nginx proxy is pre-configured to forward WebSocket upgrades — the timers tick live the moment the page loads.
Security by design — the whole application is protected. Ontime is built for a trusted local network and its realtime channel is bidirectional: any client that can watch the show can also send control commands over the same WebSocket, and the HTTP control API (/api/start, /api/stop, /api/load, …) is designed to be driven without a login. The built in editor and operator PIN codes only guard the editor screens in the browser, not the API or the WebSocket. So a route by route split cannot secure a public IP. This image instead uses Ontime's own session password — the single mechanism that authenticates both the HTTP API and the WebSocket handshake:
- A unique password is generated on this VM's first boot and protects the entire application. An unauthenticated request to any API endpoint returns HTTP 401, and an unauthenticated WebSocket connection is closed immediately. Nobody can read your runtime or hijack a live show from the network.
- No credential is baked into the image. The shipped environment file carries no password and the example project's PINs are null; the first boot service writes a fresh per VM password and two 4 digit PINs, and records them in a root only file.
- Per show PIN codes are also generated per VM. An editor PIN gates the
/editorand/cuesheetscreens and an operator PIN gates the/opview, as a second layer for the people standing at the desk.
Boots into a running show. A small example rundown ships and the first boot service starts a live timer, so a fresh VM presents a populated, running stage timer rather than an empty editor — ready to project on a screen or replace with your own rundown.
What is included:
- Ontime v4.11.0 (headless server) under systemd as the non root
ontimeuser, bound to127.0.0.1:4001 - nginx reverse proxy on port 80, pre-configured to forward the realtime WebSocket
- A unique per VM session password plus editor and operator PIN codes, generated on first boot
- A seeded example rundown and a live running timer on first boot
- Ongoing unattended security updates from Ubuntu
- A paired deployment guide and 24/7 cloudimg support
Prerequisites
- An Azure subscription with permission to deploy a virtual machine
- A network security group that admits inbound TCP 22 (SSH, ideally from your management IP only) and TCP 80 (the Ontime web interface). Do not expose Ontime on a public network without TLS and a restrictive source range — Ontime itself recommends controlling access at the network layer
- Basic familiarity with SSH and a web browser
Step 1: Deploy from the Azure Portal
- Open the cloudimg Ontime on Ubuntu 24.04 LTS offer in the Azure Marketplace and choose Get It Now → Create.
- Pick your subscription, resource group and region, and a VM size — Standard_B2s (2 vCPU / 4 GB) is ample.
- Under Networking, attach a network security group that allows inbound TCP 22 and TCP 80.
- Create or select an SSH key pair, then review and create. The VM is ready within a couple of minutes.
Step 2: Deploy from the Azure CLI
az vm create \
--resource-group <your-resource-group> \
--name ontime-01 \
--image cloudimg:ontime-ubuntu-24-04:ontime:latest \
--size Standard_B2s \
--admin-username azureuser \
--generate-ssh-keys \
--public-ip-sku Standard
# open only SSH (from your IP) and HTTP 80 on the VM's network security group
az vm open-port --resource-group <your-resource-group> --name ontime-01 --port 80 --priority 900
Step 3: First boot and your per-VM credentials
On first boot the ontime-firstboot service generates a unique session password and two PIN codes for this VM, seeds the example rundown, and starts a live timer. The credentials are written to a root only file:
sudo cat /root/ontime-credentials.txt
You will see the per VM values (never baked into the image):

Keep this file secret. The ontime.password value protects the whole application; the ontime.editor_pin and ontime.operator_pin values guard the editor and operator screens.
Step 4: Confirm the server is running
SSH into the VM and check that Ontime and nginx are active, the public login page answers, and the API is closed to anonymous callers:
systemctl is-active ontime nginx
echo "login page: $(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1/login/)"
echo "anonymous API: $(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1/api/poll)"
ontime and nginx report active, the login page returns 200, and the anonymous API call returns 401 — the whole surface is protected.

Step 5: Log in and open the editor
Browse to http://<your-vm-ip>/ and enter the ontime.password from your credentials file when prompted. You land on the rundown editor, which shows the seeded example schedule and the running timer, with playback controls, Roll mode, auxiliary timers and the messaging panel:

The editor and cuesheet screens can also be protected in the browser with the ontime.editor_pin, and the operator view with the ontime.operator_pin, from Settings → App Settings inside Ontime.
Step 6: The live timer and the display views
Ontime's whole point is that every screen counts to the same clock. Open a display view on each screen you want to drive. The stage timer at http://<your-vm-ip>/timer/ shows a large countdown for presenters, with the current and next events:

The operator cuesheet at http://<your-vm-ip>/cuesheet/ gives the show caller the full rundown with start, end and duration for every cue:

The backstage view at http://<your-vm-ip>/backstage/ shows the crew the current event, the stage timer and what is coming up next:

Other views include the studio clock (/studio/), the timeline (/timeline/) and public info (/info/). All of them receive their updates live over the WebSocket at ws://<your-vm-ip>/ws, which the nginx proxy forwards for you.
Step 7: Drive the show over the HTTP and WebSocket API
Ontime exposes an HTTP and WebSocket API so other systems can read the running state and drive playback. Because the API is protected by the session password, log in once to obtain a session cookie, then read the live runtime:
# log in with the per-VM session password and save the cookie
curl -s -o /dev/null -c /tmp/ontime.cookie \
--data-urlencode "password=<ONTIME_PASSWORD>" http://127.0.0.1/login
# read the live timer through the authenticated session
curl -s -b /tmp/ontime.cookie http://127.0.0.1/api/poll \
| jq '.payload.timer | {playback, current, duration}'
The current field is the running countdown in milliseconds. Read it twice and it has advanced — the realtime engine is live:
A=$(curl -s -b /tmp/ontime.cookie http://127.0.0.1/api/poll | jq -r '.payload.timer.current')
sleep 2
B=$(curl -s -b /tmp/ontime.cookie http://127.0.0.1/api/poll | jq -r '.payload.timer.current')
echo "timer.current: $A -> $B (ticking)"

Control endpoints follow the same pattern with the cookie, for example curl -b /tmp/ontime.cookie http://127.0.0.1/api/start to run the loaded event, /api/pause, /api/stop, /api/roll and /api/load/index/2. Companion modules for vMix, OBS, QLab and disguise can drive Ontime the same way, and machine clients can pass a pre-authenticated token as ?token=<value> using the ontime.token value from the credentials file.
Step 8: The security model and recommendations
Every unauthenticated request to the API or a control endpoint is rejected, and the editor redirects to the login page:

- Open only what you need. Admit TCP 80 (and 22 from your management IP) in the network security group; the Ontime server itself only listens on loopback behind nginx.
- The password protects everything, including the public display views. Ontime's realtime channel is bidirectional, so a screen that can view the show can also control it — locking the whole surface is the only safe posture on a public IP. If you want to show a read only schedule to an untrusted audience, do it at the network layer (a separate screen device or proxy on your trusted LAN).
- Rotate the password by editing
/etc/ontime/ontime.env(setSESSION_PASSWORD=...) and runningsudo systemctl restart ontime. - Keep the OS patched — unattended security updates are enabled by default.
Step 9: Front the server with TLS for internet exposure
For any internet facing deployment, terminate TLS at nginx. Point a DNS name at the VM, then:
sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d <your-domain> --redirect --agree-tos -m <your-email>
certbot obtains a certificate, rewrites the nginx site to listen on 443 and redirects HTTP to HTTPS. Renewal is automatic.
Step 10: Support and Licensing
Ontime is open source software (GPL-3.0). This cloudimg image is a repackaged distribution of that software with additional charges for cloudimg support services; Ontime is a trademark of its respective owner and use of the name does not imply affiliation or endorsement.
- cloudimg support: 24/7, included with every deployment — www.cloudimg.co.uk
- Ontime documentation: docs.getontime.no
For help deploying or operating this image, contact cloudimg support with your VM's region and size and a description of the issue.