====== 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 The ''8080:80'' mapping is not well-documented. The reverse proxy (nginx) handles TLS termination and forwards plaintext HTTP to port 8080. ==== 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 | The build process takes 5-15 minutes depending on hardware. ==== 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/ 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.**// **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-For'' and ''X-Forwarded-Host'' - Discourse uses these for client IP detection * WebSocket headers - Real-time updates require ''Upgrade'' and ''Connection'' headers * ''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 @:'' ====== API ====== **AUTO-GENERATED DOCUMENTATION** This section was automatically generated and is subject to change upon human review. Last updated: 2026-01-19 ==== 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", "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 ===== * [[https://meta.discourse.org/|Discourse Meta]] - Official community * [[https://github.com/discourse/discourse|GitHub Repository]] * [[https://github.com/discourse/discourse_docker|Docker Repository]] ====== See Also ======= * [[SyncBot|SyncBot]]