This is an old revision of the document!
Table of Contents
Discourse
Discourse is a modern, open-source discussion platform built for the next decade of the Internet. It functions as a mailing list, discussion forum, and long-form chat room, designed from the ground up to address the challenges of community management and online discussion.
Overview
Discourse is written in Ruby on Rails with an Ember.js frontend. It runs inside Docker containers, which simplifies deployment but creates opacity when deviating from the standard configuration. The platform emphasizes civilized discussion through features like trust levels, flagging systems, and moderation tools.
Key Characteristics:
- Container-based deployment via Docker
- Built-in PostgreSQL and Redis
- Real-time updates via WebSockets
- Plugin architecture for extensibility
- Comprehensive backup/restore functionality
System Requirements
| Component | Minimum | Recommended |
|---|---|---|
| RAM | 1 GB | 2+ GB |
| CPU | 1 core | 2+ cores |
| Storage | 10 GB | 20+ GB |
| OS | Ubuntu 20.04+ | Ubuntu 24.04 LTS |
Architecture
Discourse ships as a monolithic Docker container containing:
- PostgreSQL database
- Redis for caching and background jobs
- Sidekiq for job processing
- Nginx (internal) for request handling
- Ruby on Rails application server
The container exposes ports for HTTP/HTTPS traffic and handles TLS termination internally by default. However, production deployments frequently place Discourse behind a reverse proxy for flexibility in certificate management, load balancing, or multi-application hosting.
Installation
This section documents installation on a fresh Ubuntu server with backup restoration and nginx reverse proxy configuration.
Prerequisites
- Fresh Ubuntu Server installation (24.04 LTS recommended)
- Root or sudo access
- Existing Discourse backup file (.tar.gz) if restoring
- Domain name with DNS A record pointed to server IP
- SMTP credentials for outbound email
Initial System Setup
sudo -s apt-get update && apt-get install -y git
Clone Discourse Docker Repository
git clone https://github.com/discourse/discourse_docker.git /var/discourse cd /var/discourse chmod 700 containers
Configure for Reverse Proxy
Before running the setup script, create the container configuration. This step is critical—configuring after initial build requires a full rebuild.
Create /var/discourse/containers/app.yml with reverse proxy settings:
Comment out SSL templates:
templates: - "templates/postgres.template.yml" - "templates/redis.template.yml" - "templates/web.template.yml" - "templates/web.ratelimited.template.yml" ## SSL templates - COMMENTED OUT for reverse proxy setup # - "templates/web.ssl.template.yml" # - "templates/web.letsencrypt.ssl.template.yml"
Modify port exposure:
expose: - "8080:80" # expose container port 80 on host port 8080 # - "443:443" # https - commented out
<WRAP important>
The 8080:80 mapping is not well-documented. The reverse proxy (nginx) handles TLS termination and forwards plaintext HTTP to port 8080.
</WRAP>
Run Discourse Setup
./discourse-setup --skip-connection-test
The setup wizard prompts for configuration values:
| Parameter | Example Value | Notes |
|---|---|---|
| Hostname | forum.example.com | Must match your DNS record |
| Developer Email | admin@example.com | Receives admin notifications |
| SMTP Server | smtp.provider.com | Your mail provider |
| SMTP Port | 587 | TLS submission port |
| SMTP Username | smtp-user | Provider credentials |
| SMTP Password | smtp-pass | Provider credentials |
| Notification Email | noreply@example.com | From address for emails |
| Let's Encrypt Email | OFF | Critical: Enter OFF for reverse proxy |
<WRAP tip> The build process takes 5-15 minutes depending on hardware. </WRAP>
Restore from Backup
If restoring from an existing Discourse instance:
Transfer backup file to server:
scp -P 22 discourse-backup.tar.gz user@server:/var/discourse/shared/standalone/backups/default/
<WRAP info>
Adjust the port (-P), username, and hostname to match your environment. Do NOT change the name of the file, as it is metadata necessary for the restore.
</WRAP>
Restore via Admin Panel:
- Access Discourse at
http://server-ip:8080(or domain if DNS configured) - Navigate to Admin → Backups
- Locate the uploaded backup file
- Click Restore and confirm
The restoration restarts Discourse and may take several minutes depending on backup size.
Install and Configure Nginx
apt-get install -y nginx
Create site configuration at /etc/nginx/sites-available/forum.example.com:
server { listen 80; server_name forum.example.com; location / { proxy_pass http://localhost:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $host; # WebSocket support (required for Discourse) proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; # Timeouts proxy_connect_timeout 60s; proxy_send_timeout 60s; proxy_read_timeout 60s; # Buffering proxy_buffering off; proxy_redirect off; } client_max_body_size 10m; }
Required headers for Discourse:
X-Forwarded-ForandX-Forwarded-Host- Discourse uses these for client IP detection- WebSocket headers - Real-time updates require
UpgradeandConnectionheaders proxy_buffering off- Prevents response buffering issues
Enable the configuration:
rm /etc/nginx/sites-enabled/default ln -s /etc/nginx/sites-available/forum.example.com /etc/nginx/sites-enabled/ nginx -t systemctl reload nginx
Configure TLS with Certbot
snap install --classic certbot certbot
Certbot automatically:
- Detects the nginx configuration
- Prompts for domain selection
- Generates Let's Encrypt certificates
- Modifies nginx for HTTPS
- Configures automatic renewal
Reload nginx after certificate installation:
systemctl reload nginx
Configuration
Environment Variables
Key environment variables in app.yml:
| Variable | Purpose |
|---|---|
DISCOURSE_HOSTNAME | Primary domain name |
DISCOURSE_DEVELOPER_EMAILS | Admin notification recipients |
DISCOURSE_SMTP_ADDRESS | Mail server hostname |
DISCOURSE_SMTP_PORT | Mail server port |
DISCOURSE_SMTP_USER_NAME | SMTP authentication user |
DISCOURSE_SMTP_PASSWORD | SMTP authentication password |
DISCOURSE_DB_SHARED_BUFFERS | PostgreSQL memory allocation |
Rebuild Container
After modifying app.yml:
cd /var/discourse ./launcher rebuild app
Maintenance
Common Commands
# Enter container shell ./launcher enter app # View logs ./launcher logs app # Stop/start container ./launcher stop app ./launcher start app # Full rebuild ./launcher rebuild app
Backup Configuration
Backups can be configured in Admin → Backups → Settings:
- Enable automatic backups
- Set retention period
- Configure S3 upload (optional)
Troubleshooting
Nginx Shows Default Page
- Verify default site removed:
ls /etc/nginx/sites-enabled/ - Confirm symlink exists:
ls -la /etc/nginx/sites-enabled/forum.example.com - Test configuration:
nginx -t - Reload:
systemctl reload nginx
SSL Certificate Errors
- Run nginx test with sudo:
sudo nginx -t - Check for configs referencing non-existent certificates
- Ensure only HTTP config exists before running certbot
Backup Not Visible
- Verify file location:
/var/discourse/shared/standalone/backups/default/ - Check permissions:
ls -la /var/discourse/shared/standalone/backups/default/ - Confirm filename ends in
.tar.gz
SCP Syntax
- Port flag is capital
-P(not lowercase-p) - Syntax:
scp -P <port> <source> <user>@<host>:<destination>
API
<WRAP center round important 60%> AUTO-GENERATED DOCUMENTATION
This section was automatically generated and is subject to change upon human review. Last updated: 2026-01-19 </WRAP>
Channel Operations
- List channels:
GET /chat/api/channels - Get specific channel:
GET /chat/api/channels/{channel_id} - Send message:
POST /chat/api/channels/{channel_id}/messages - List messages:
GET /chat/api/channels/{channel_id}/messages
Authentication Headers
All API requests require the following headers:
Api-Key: {your_api_key}
Api-Username: {username}
Content-Type: application/json
Webhook Event Types
Available webhook triggers include:
- Chat message created
- Chat message edited
- Chat message trashed
- Chat message restored
- Post events (for forum integration)
- User events (login/logout/created)
Avatar URL Extraction
Avatar URLs are provided in the avatar_template field of user objects in API responses.
Response Structure
{
"user": {
"id": 2,
"username": "Chelsea",
"name": "Chelsea Lee ᶘ ᵒᴥᵒᶅ",
"avatar_template": "/user_avatar/meant2be.me/chelsea/{size}/3_2.png"
}
}
Building Avatar URLs
Pattern:
{base_url}{avatar_template with {size} replaced}
Example:
https://baseurl.tld/user_avatar/baseurl.tld/username/48/3_2.png
Supported sizes: 20, 25, 32, 45, 48, 60, 90, 120, 135, 240, 360, 480
Recommended for Discord: 128 or 256
Python Implementation
def get_avatar_url(user_obj, size=48): """Extract and construct full avatar URL from Discourse user object""" template = user_obj['avatar_template'] avatar_path = template.replace('{size}', str(size)) return f"https://meant2be.me{avatar_path}" # Usage: avatar_url = get_avatar_url(message['user'], size=128)
Bridge Architecture
Data Flow
Discourse → Discord (via webhooks):
- Webhook events trigger on chat message actions
- Webhook receiver endpoints process incoming events
- Forward to Discord via webhook or bot API
Discord → Discourse (via API):
- Discord bot listens for message events
- POST to
/chat/api/channels/{channel_id}/messages - Payload:
{“message”: “text content”}
Implementation Considerations
- Message ID mapping: Track Discourse ↔ Discord message IDs to prevent echo loops
- User mapping: Map Discourse usernames to Discord user representations
- Avatar sync: Use extracted avatar URLs for Discord webhooks
- Category filtering: Configure webhooks for specific categories/channels
- Loop prevention: Maintain state to avoid infinite message echoes between platforms
API Response Examples
Chat Messages Response
{
"messages": [
{
"id": 77045,
"message": ":eyes:",
"created_at": "2025-12-23T00:30:23Z",
"chat_channel_id": 1,
"user": {
"id": 2,
"username": "Chelsea",
"name": "Chelsea Lee ᶘ ᵒᴥᵒᶅ",
"avatar_template": "/user_avatar/baseurl.tld/chelsea/{size}/3_2.png",
"moderator": true,
"admin": true,
"staff": true
}
}
],
"meta": {
"can_load_more_future": false,
"can_load_more_past": false
}
}
References
- Discourse Meta - Official community
