Discourse Community Forum on AWS User Guide
Overview
Discourse is the leading open-source platform for community discussion, support forums and mailing lists. It is a modern Ruby on Rails and Ember application backed by PostgreSQL and Redis, served by a bundled nginx, and distributed as a single official Docker container built with the discourse_docker launcher. The cloudimg AMI installs Discourse the only officially supported way, pre-builds and bootstraps the container at image-bake time so your instance starts serving in under a minute instead of running a lengthy build on first boot, persists the database and uploads on a dedicated EBS data volume, generates a unique administrator account plus an admin API key on the first boot of every instance, and mints a per-instance self-signed TLS certificate served on port 443. Backed by 24/7 cloudimg support.
What is included:
- Discourse (stable) running as the official single standalone Docker container - Rails, PostgreSQL and nginx bundled, with a BSD-3-Clause Redis
- Docker Engine (Docker CE) with the container published on port
80 - A host nginx that terminates TLS on port
443with a per-instance self-signed certificate and proxies to the container discourse.service,discourse-firstboot.serviceandnginx.serviceas systemd units, enabled and active- A per-instance administrator account plus an admin API key generated on first boot and recorded in a root-only credentials file
- A dedicated 40 GiB EBS data volume at
/var/lib/discourseholding the PostgreSQL cluster, uploads and Redis data, captured into the image and re-provisioned with every instance - 24/7 cloudimg support

Connecting to your instance
Connect over SSH as the default login user for your AMI variant:
| OS variant | SSH login user |
|---|---|
| Ubuntu 24.04 | ubuntu |
ssh ubuntu@<public-ip>
Prerequisites
An AWS account, an EC2 key pair, and a VPC with a public subnet. The recommended instance type is m5.large (2 vCPU / 8 GiB RAM); Discourse rebuilds and Rails are memory-hungry, so do not go below 8 GiB of RAM for production. Security group inbound: allow 22/tcp from your management network, and 80/tcp and 443/tcp for the web interface. Discourse serves plain HTTP on port 80 and HTTPS (self-signed) on port 443; for production, replace the certificate with your own or front the instance with an Application Load Balancer and an ACM certificate (see the final section).
Step 1 - Launch from the AWS Marketplace
Open the product in the AWS Marketplace and choose Continue to Subscribe, then Continue to Configuration and Continue to Launch. Pick the m5.large instance type, your VPC and subnet, and a security group that allows ports 22, 80 and 443. Select your EC2 key pair and launch. The dedicated data volume is attached automatically.
Step 2 - Launch from the AWS CLI
aws ec2 run-instances \
--image-id <ami-id> \
--instance-type m5.large \
--key-name <your-key-pair> \
--security-group-ids <sg-with-22-80-443> \
--subnet-id <your-public-subnet> \
--metadata-options "HttpTokens=required" \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=discourse}]'
Step 3 - Confirm the services are running
On first boot the image starts the pre-built Discourse container, mints the per-instance TLS certificate and creates a per-instance administrator. Confirm Docker, Discourse and nginx are active:
systemctl is-active docker.service discourse.service nginx.service
All report active. Inspect the running container and the port listeners:
sudo docker ps --format 'table {{.Names}}\t{{.Status}}'
sudo ss -tln | grep -E ':(80|443) '
Step 4 - Check the health endpoint
Discourse exposes an unauthenticated status endpoint that returns ok when the application is healthy, on both HTTP and the self-signed HTTPS listener:
curl -s -o /dev/null -w 'http %{http_code}\n' http://127.0.0.1/srv/status
curl -sk -o /dev/null -w 'https %{http_code}\n' https://127.0.0.1/srv/status
Both print 200.
Step 5 - Retrieve the per-instance admin credentials
A unique administrator password and an admin API key are generated on first boot and written to a root-only file. Read it with:
sudo cat /root/discourse-credentials.txt
The file contains DISCOURSE_URL, DISCOURSE_ADMIN_EMAIL (admin@cloudimg.local), DISCOURSE_ADMIN_USERNAME (admin), a unique DISCOURSE_ADMIN_PASSWORD and a unique DISCOURSE_ADMIN_API_KEY. Confirm it is present and root-only (this prints the non-secret lines only):
sudo stat -c '%a %U' /root/discourse-credentials.txt
sudo grep -E '^DISCOURSE_(URL|ADMIN_EMAIL|ADMIN_USERNAME)=' /root/discourse-credentials.txt
Step 6 - Sign in to the web interface
Open https://<public-ip>/ in your browser (the certificate is self-signed, so accept the browser warning) or http://<public-ip>/, and choose Log In. Sign in as admin@cloudimg.local with the password from Step 5.

The full administrator dashboard is at /admin, where you manage users, categories, site settings, plugins, backups and more.

Browse a category to see its discussions and start new topics.

Step 7 - Verify the admin API and create a topic
Discourse exposes a full REST API. The per-instance admin key authenticates as the admin user. A wrong key is rejected; the valid key can read the site and create content. This creates a topic in the first public category and reads it back:
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
CAT=$(curl -s -H "Api-Key: $KEY" -H 'Api-Username: admin' http://127.0.0.1/categories.json | jq -r '[.category_list.categories[]|select(.read_restricted==false)][0].id')
TID=$(curl -s -H "Api-Key: $KEY" -H 'Api-Username: admin' -H 'Content-Type: application/json' -X POST -d "{\"title\":\"Getting started with our new forum\",\"raw\":\"Welcome. This first topic was created through the Discourse REST API to confirm the forum is working.\",\"category\":$CAT}" http://127.0.0.1/posts.json | jq -r '.topic_id')
echo "created topic $TID"; curl -s -H "Api-Key: $KEY" -H 'Api-Username: admin' http://127.0.0.1/t/$TID.json | jq -r '.title'
The wrong key returns a non-200 status; the valid key creates the topic and reads back its title.
Step 8 - Confirm data lives on the dedicated data volume
The PostgreSQL cluster, uploads and Redis data are stored on the dedicated EBS data volume mounted at /var/lib/discourse, so they persist independently of the OS disk and can be resized independently:
findmnt -no SOURCE,TARGET,FSTYPE /var/lib/discourse
test -d /var/lib/discourse/standalone/postgres_data && echo 'PostgreSQL cluster is on the data volume'
Set your real hostname, TLS and SMTP before production use
This appliance boots with the placeholder hostname discourse.cloudimg.local, a per-instance self-signed TLS certificate and a placeholder loopback SMTP so it can bootstrap without a real domain or mail server. Before you invite real users:
- Point a DNS
Arecord at the instance's public IP (or front it with a load balancer). - Edit
/var/discourse/containers/app.ymland set your real values underenv:
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
- Rebuild the container so the new configuration takes effect (this repackages the container and takes several minutes; the BSD-licensed Redis is preserved automatically by the bundled template):
cd /var/discourse
sudo ./launcher rebuild app
- For production TLS, replace the self-signed certificate at
/etc/cloudimg/discourse/tls.crtand/etc/cloudimg/discourse/tls.keywith your own and reload nginx (sudo systemctl reload nginx), or terminate TLS at an Application Load Balancer with an ACM certificate in front of the instance.
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. You can also snapshot the EBS data volume with EBS snapshots or AWS Backup. - Upgrade Discourse: upgrade minor versions from Admin -> Upgrade, or rebuild the container with
cd /var/discourse && sudo ./launcher rebuild app. - 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 hardening, image maintenance and support.