Deployment guide

Discourse

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 and served by a bundled nginx. It is distributed as a single official Docker container built with the discourse_docker launcher.

This SecureAnyCloud image installs Discourse 3.4 the only officially supported way. It 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. The database and uploads live on a dedicated Azure data disk, and each VM generates a unique administrator account and admin API key on first boot.

What you get:

  • 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 enabled, active systemd units
  • A per-VM administrator account and 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 SecureAnyCloud support

Prerequisites

You need an active Azure subscription, an SSH key pair, and a VNet with a subnet in the target region.

Standard_B4ms (4 vCPU / 16 GiB RAM) is a good starting size. Discourse rebuilds and Rails are memory-hungry, so do not go below 8 GiB of RAM for production.

Configure the network security group (NSG) to allow inbound 22/tcp from your management network and 80/tcp for the web interface. Discourse serves plain HTTP on port 80, so 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 SecureAnyCloud, and select Create.

On the Basics tab, 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 select Review + create and Create.

Step 2 - Deploy from the Azure CLI

# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:discourse-ubuntu-24-04:default:latest
az vm create \
  --resource-group <your-rg> \
  --name discourse \
  --image sac1647283583153:discourse-ubuntu-24-04:default:latest \
  --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

Connect to the VM over SSH with the admin user you created:

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 that Docker and the Discourse service are active:

systemctl is-active docker.service discourse.service

Both report active. Then 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. Request it and print the HTTP status:

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

The command prints 200. To see the raw status text, run:

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

Step 6 - Retrieve the per-VM admin credentials

Each VM generates a unique administrator password and admin API key on first boot and writes them to a root-only file:

sudo cat /root/discourse-credentials.txt

The file contains DISCOURSE_ADMIN_EMAIL (admin@sac.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@sac.local with the password from Step 6.

The full administrator dashboard is at http://<vm-public-ip>/admin. There 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, and the per-VM admin key authenticates as the admin user. Run these commands to confirm that 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. Confirm the mount:

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

Set your real hostname and SMTP for production

This appliance boots with the default hostname discourse.sac.local and a loopback SMTP setting, so it can bootstrap without a real domain or mail server. Before you invite real users, 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. The rebuild 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

SecureAnyCloud provides 24/7 support for this image. Discourse is licensed under the GPL-2.0 and is free. The SecureAnyCloud charge covers packaging, security patching, image maintenance and support.

Deploy on Azure

Launch Discourse on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure