How to Self-Host Discourse on a VPS: Community Forum with SSO, Email, and Plugins
Discourse is the most modern open-source community forum platform — with trust levels, gamification, real-time notifications, full-text search, mobile-optimized design, and hundreds of plugins. Discourse hosting starts at $50/month per forum on their cloud. Self-hosting on a VPS provides the full platform at infrastructure cost, with complete control over plugins, customization, and data.
Server Requirements
- Minimum 2 GB RAM (4 GB recommended for active communities)
- Ubuntu 22.04 or 24.04
- Docker (Discourse uses its own Docker-based installer)
- A domain and transactional email service (Mailgun required — Discourse is email-heavy)
- SMTP credentials before starting (Discourse won’t install without valid email config)
Step 1: Prerequisites
<code"># Install Docker curl -fsSL https://get.docker.com | sh sudo usermod -aG docker $USER # Install Discourse's launcher sudo -s git clone https://github.com/discourse/discourse_docker.git /var/discourse cd /var/discourse
Step 2: Configure Discourse
<code">cp samples/standalone.yml containers/app.yml nano containers/app.yml
<code">## Essential configuration (edit these sections):
## Networking
expose:
- "80:80" # HTTP
- "443:443" # HTTPS
## Environment — core settings
env:
LANG: en_US.UTF-8
DISCOURSE_HOSTNAME: forum.yourdomain.com
DISCOURSE_DEVELOPER_EMAILS: admin@yourdomain.com
## Transactional email (required — get from Mailgun)
DISCOURSE_SMTP_ADDRESS: smtp.mailgun.org
DISCOURSE_SMTP_PORT: 587
DISCOURSE_SMTP_USER_NAME: postmaster@mg.yourdomain.com
DISCOURSE_SMTP_PASSWORD: your_mailgun_smtp_password
DISCOURSE_SMTP_ENABLE_START_TLS: true
DISCOURSE_SMTP_AUTHENTICATION: plain
## Notification email (From address for all emails)
DISCOURSE_NOTIFICATION_EMAIL: forum@yourdomain.com
## Let's Encrypt SSL (Discourse handles this itself)
LETSENCRYPT_ACCOUNT_EMAIL: admin@yourdomain.com
## Memory settings (adjust for your VPS RAM)
db_shared_buffers: "256MB" # 25% of RAM for 1 GB VPS, 50% for 2+ GB
UNICORN_WORKERS: 3 # Number of worker processes (2 per CPU core max)
## Plugins to install (optional — uncomment to add)
hooks:
after_code:
- exec:
cd: $home/plugins
cmd:
## Official plugins
- git clone https://github.com/discourse/discourse-solved.git
- git clone https://github.com/discourse/discourse-assign.git
## Community plugins
# - git clone https://github.com/discourse/discourse-oauth2-basic.git
Step 3: Build and Start Discourse
<code"># Build the Docker image (takes 5–15 minutes) cd /var/discourse ./launcher bootstrap app # Start Discourse ./launcher start app # Check status ./launcher status app # View logs ./launcher logs app
Step 4: Initial Admin Setup
- Visit
https://forum.yourdomain.com - Click “Register” — use the email in
DISCOURSE_DEVELOPER_EMAILS - Check your email for verification link
- After verifying, you’ll be prompted to complete the admin setup wizard
- Complete the setup wizard: forum name, category creation, invite users
Step 5: Configure SSO (Single Sign-On)
If you have an existing user authentication system (custom app, Keycloak, Zitadel), integrate it via Discourse SSO:
<code"># Discourse Admin → Settings → Login
# Enable: "discourse connect provider"
# Set: "discourse connect url" to your app's SSO endpoint
# Set: "discourse connect secret" to a shared secret key
# Your SSO endpoint must handle the Discourse SSO protocol:
# 1. Receive: GET /sso?sso=ENCODED_PAYLOAD&sig=HMAC_SHA256
# 2. Verify HMAC signature with shared secret
# 3. Decode payload, authenticate user
# 4. Return: GET https://forum.yourdomain.com/session/sso_login?sso=RESPONSE&sig=RESPONSE_SIG
import hashlib
import hmac
import base64
import urllib.parse
def discourse_sso_login(sso: str, sig: str, secret: str, user: dict) -> str:
"""Generate Discourse SSO response URL."""
# Verify incoming signature
expected = hmac.new(secret.encode(), sso.encode(), hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, sig):
raise ValueError("Invalid SSO signature")
# Decode nonce from payload
decoded = base64.b64decode(sso).decode()
nonce = urllib.parse.parse_qs(decoded)['nonce'][0]
# Build response
params = {
'nonce': nonce,
'email': user['email'],
'external_id': str(user['id']),
'username': user['username'],
'name': user['name'],
'admin': str(user.get('is_admin', False)).lower(),
}
payload = urllib.parse.urlencode(params)
encoded = base64.b64encode(payload.encode()).decode()
response_sig = hmac.new(secret.encode(), encoded.encode(), hashlib.sha256).hexdigest()
return f"https://forum.yourdomain.com/session/sso_login?sso={encoded}&sig={response_sig}"
Step 6: Install Plugins
<code"># Edit containers/app.yml — add to hooks.after_code.cmd: - git clone https://github.com/discourse/discourse-solved.git # Mark posts as solved - git clone https://github.com/discourse/discourse-assign.git # Assign topics to users - git clone https://github.com/discourse/discourse-reactions.git # Emoji reactions - git clone https://github.com/discourse/discourse-calendar.git # Events calendar - git clone https://github.com/discourse/discourse-chat-integration.git # Slack/Discord # After editing, rebuild the container: ./launcher rebuild app
Discourse Management Commands
<code"># All commands run as root in /var/discourse ./launcher rebuild app # Rebuild after config/plugin changes ./launcher restart app # Quick restart (no rebuild) ./launcher stop app # Stop Discourse ./launcher start app # Start Discourse ./launcher logs app # View logs ./launcher enter app # Shell into container # Rails console (run admin tasks) ./launcher enter app discourse exec bin/rails console # Backup via admin UI: # Admin → Backups → Backup Now # Or configure automatic daily backups to S3
Performance Tuning
<code"># In app.yml environment section: UNICORN_WORKERS: 4 # Match CPU cores (4 vCPU VPS) db_shared_buffers: "512MB" # For 2+ GB RAM VPS # Enable Redis for sidekiq job caching # (Discourse includes Redis — no extra config needed) # CDN for assets (improves global load times): DISCOURSE_CDN_URL: https://cdn.yourdomain.com # S3 for uploads (keeps VPS disk usage low): DISCOURSE_S3_BUCKET: discourse-uploads DISCOURSE_S3_ACCESS_KEY_ID: your_key DISCOURSE_S3_SECRET_ACCESS_KEY: your_secret DISCOURSE_S3_REGION: us-east-1 DISCOURSE_USE_S3: true
Getting Started
Discourse requires 2 GB RAM — a 2 GB Ubuntu VPS at VPS.DO is the minimum; 4 GB provides comfortable headroom for active communities. The Docker-based installer handles everything — PostgreSQL, Redis, Nginx, SSL — in a single container. Configure Mailgun before starting, as Discourse requires working email for registration and notifications from the very first setup step.
Conclusion
Self-hosted Discourse provides a complete community forum platform with trust levels, SSO integration, plugin support, and full data ownership — at VPS cost instead of $50+/month per forum. The Docker-based installer makes initial setup straightforward, and plugin installation requires only adding git clone lines to the config file and rebuilding. For developer communities, customer support forums, or any group needing structured discussion, Discourse remains the gold standard of open-source forum software.