Discourse on Ubuntu 24.04 on Azure User Guide
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) anddiscourse-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/discourseholding the PostgreSQL cluster, uploads and Redis data, captured into the image and re-provisioned with every VM - 24/7 cloudimg support

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 '

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

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.

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

Manage community members from the 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.

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 appto 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.