How to Self-Host Discourse on a VPS: Community Forum with SSO, Email, and Plugins

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

  1. Visit https://forum.yourdomain.com
  2. Click “Register” — use the email in DISCOURSE_DEVELOPER_EMAILS
  3. Check your email for verification link
  4. After verifying, you’ll be prompted to complete the admin setup wizard
  5. 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.

Fast • Reliable • Affordable VPS - DO It Now!

Get top VPS hosting with VPS.DO’s fast, low-cost plans. Try risk-free with our 7-day no-questions-asked refund and start today!