Di
Developer Tools Azure

Discourse on Ubuntu 24.04 on Azure User Guide

| Product: Discourse on Ubuntu 24.04 LTS on Azure

Overview

Discourse is the leading open-source platform for community discussion, forums and mailing lists. It is a modern Ruby on Rails application backed by PostgreSQL and Redis, served by a bundled nginx, and is distributed as a single official Docker container built with the discourse_docker launcher. The cloudimg image installs Discourse 3.4 the only officially supported way, pre-builds and bootstraps the container at image-bake time so your VM starts serving in under a minute instead of running a lengthy build on first boot, persists the database and uploads on a dedicated Azure data disk, and generates a unique administrator account plus an admin API key on the first boot of every VM. Backed by 24/7 cloudimg support.

What is included:

  • Discourse 3.4 (stable) running as the official single standalone Docker container (Rails, PostgreSQL, Redis and nginx bundled)
  • Docker Engine (Docker CE) with the container published on port 80
  • discourse.service (manages the container lifecycle) and discourse-firstboot.service (per-VM admin) as systemd units, enabled and active
  • A per-VM administrator account plus an admin API key generated on first boot and recorded in a root-only credentials file
  • A dedicated 40 GiB Azure data disk at /var/lib/discourse holding the PostgreSQL cluster, uploads and Redis data, captured into the image and re-provisioned with every VM
  • 24/7 cloudimg support

Discourse home page showing the latest topics list served by the VM

Prerequisites

An active Azure subscription, an SSH key pair, and a VNet plus subnet in the target region. Standard_B4ms (4 vCPU / 16 GiB RAM) is a good starting point; Discourse rebuilds and Rails are memory-hungry, so do not go below 8 GiB of RAM for production. NSG inbound: allow 22/tcp from your management network and 80/tcp for the web interface. Discourse 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 Discourse 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). Review the dedicated data disk on the Disks tab, then Review + create -> Create.

Step 2 - Deploy from the Azure CLI

az vm create \
  --resource-group <your-rg> \
  --name discourse \
  --image <marketplace-image-urn> \
  --size Standard_B4ms \
  --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 discourse --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 starts the pre-built Discourse container and creates a per-VM administrator. Confirm Docker and the Discourse service are active:

systemctl is-active docker.service discourse.service

Both report active. Inspect the running container and the port 80 listener:

sudo docker ps --format 'table {{.Names}}\t{{.Status}}'
sudo ss -tln | grep ':80 '

Discourse services, container and port 80 listener on the VM

Step 5 - Check the health endpoint

Discourse exposes an unauthenticated status endpoint that returns ok when the application is healthy:

curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1/srv/status

This prints 200. You can also view the raw status text and HTTP headers:

curl -s http://127.0.0.1/srv/status; echo

Discourse unauthenticated health endpoint and HTTP headers

Step 6 - Retrieve the per-VM admin credentials

A unique administrator password and an admin API key are generated on first boot and written to a root-only file:

sudo cat /root/discourse-credentials.txt

The file contains DISCOURSE_ADMIN_EMAIL (admin@cloudimg.local), DISCOURSE_ADMIN_USERNAME (admin), a unique DISCOURSE_ADMIN_PASSWORD and a unique DISCOURSE_ADMIN_API_KEY. Store these somewhere safe.

Step 7 - Sign in to the web interface

Open http://<vm-public-ip>/ in your browser and choose Log In. Sign in as admin@cloudimg.local with the password from Step 6. The full administrator dashboard is at http://<vm-public-ip>/admin, where you can manage users, categories, site settings, plugins, backups and more.

The Discourse administrator dashboard

Browse a category to see its discussions; the seeded General category already contains a welcome topic.

A Discourse category listing its topics

Manage community members from the users administration screen.

The Discourse users administration screen

Step 8 - Verify the admin API key

Discourse exposes a full REST API. The per-VM admin key authenticates as the admin user. A wrong key is rejected and the valid key returns the active users list:

KEY=$(sudo grep '^DISCOURSE_ADMIN_API_KEY=' /root/discourse-credentials.txt | cut -d= -f2-)
echo -n 'wrong key: '; curl -s -o /dev/null -w '%{http_code}\n' -H 'Api-Key: wrong-key-xyz' -H 'Api-Username: admin' http://127.0.0.1/admin/users/list/active.json
echo -n 'valid key: '; curl -s -o /dev/null -w '%{http_code}\n' -H "Api-Key: $KEY" -H 'Api-Username: admin' http://127.0.0.1/admin/users/list/active.json

The wrong key returns a non-200 status and the valid key returns 200.

The Discourse admin API key round-trip

Step 9 - Confirm data lives on the dedicated data disk

The PostgreSQL cluster, uploads and Redis data are stored on the dedicated Azure data disk mounted at /var/lib/discourse, so they persist independently of the OS disk:

findmnt -no SOURCE,TARGET,FSTYPE /var/lib/discourse

Set your real hostname and SMTP before production use

This appliance boots with the placeholder hostname discourse.cloudimg.local and a placeholder loopback SMTP so it can bootstrap without a real domain or mail server. Before you invite real users you must set your own DNS hostname and a working SMTP provider (Discourse relies on email for signups, password resets and notifications). Point a DNS A record at the VM's public IP, then edit /var/discourse/containers/app.yml and update these keys under env:

DISCOURSE_HOSTNAME: 'forum.example.com'
DISCOURSE_DEVELOPER_EMAILS: 'you@example.com'
DISCOURSE_SMTP_ADDRESS: smtp.example.com
DISCOURSE_SMTP_PORT: 587
DISCOURSE_SMTP_USER_NAME: 'your-smtp-user'
DISCOURSE_SMTP_PASSWORD: 'your-smtp-password'
DISCOURSE_SMTP_ENABLE_START_TLS: true

Then rebuild the container so the new configuration takes effect (this repackages the container and takes several minutes):

cd /var/discourse
sudo ./launcher rebuild app

For HTTPS, either add the templates/web.ssl.template.yml and templates/web.letsencrypt.ssl.template.yml templates plus a 443 entry to app.yml and rebuild, or terminate TLS at an Azure load balancer or reverse proxy in front of the VM.

Maintenance

  • Restart Discourse: sudo systemctl restart discourse.service
  • View container logs: sudo docker logs app
  • Back up: use the admin dashboard (Admin -> Backups) or cd /var/discourse && sudo ./launcher run app 'discourse backup'. Backups are written under /var/lib/discourse/standalone/backups.
  • Upgrade Discourse: upgrade minor versions from Admin -> Upgrade, or rebuild the container with cd /var/discourse && sudo ./launcher rebuild app to pull the latest stable build.
  • OS updates: the image ships with unattended security upgrades enabled.

Support

Backed by 24/7 cloudimg support. Discourse is licensed under the GPL-2.0 and is free; the cloudimg charge covers packaging, security patching, image maintenance and support.