3 Discourse
Drew edited this page 2026-05-22 18:55:45 -04:00
This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Discourse Forum — lab.justaguylinux.com ("The Butter Lab")

About The Butter Lab

The Butter Lab is the forum half of JustAGuyLinux — a slower, threaded version of the YouTube comments section, where a video sparks a thread instead of an argument that scrolls off the page in a day.

Who it's for

  • Viewers of the JustAGuyLinux YouTube channel who want somewhere to talk that isn't a comment box.
  • Linux folks in general, especially beginners. The unofficial rule of the place is "if you have to ask, you're in the right room" — no gatekeeping, no "RTFM" energy.

Vibe

  • Small and slow by design. Not chasing growth, engagement metrics, or trending threads. A handful of real conversations beats a fast-moving feed.
  • Civil first, technical second. Reads like a community where people actually know each other, not a Stack Overflow drive-by.
  • An extension of the channel. A video drops, a thread shows up. Sometimes a thread surfaces something interesting and becomes a video.

Why a forum (and not Discord / Matrix / Reddit)

  • Threaded conversation that doesn't vanish — old answers stay findable a year later.
  • Searchable by Google and by the forum's own search; nothing locked behind an invite.
  • No real-time pressure. People reply when they have something to say, not because the chat is moving.

How it runs

The original service on this box. Single all-in-one container managed by Discourse's own launcher script — not docker compose. Listens on :80 and :443 directly.

Layout on disk

/var/discourse/
├── launcher                # Discourse's wrapper around docker (the only command you run)
├── containers/
│   └── app.yml             # the one config file that matters (root:600 — sudo to view/edit)
├── shared/standalone/      # persistent data (db, uploads, logs) — primary backup target
├── templates/              # base building blocks — see below
├── samples/                # example app.yml setups (standalone, web_only, data, redis)
├── image/                  # Dockerfiles for the Discourse images
├── bin/                    # helper scripts
└── discourse-doctor        # diagnostic tool

Container name: app (image local_discourse/app). The image is built locally — docker images shows it as local_discourse/app.

Live container facts

Pulled from docker inspect app (root config is gated behind sudo; these are derivable from the running container):

Mounts:

/var/discourse/shared/standalone        →  /shared
/var/discourse/shared/standalone/log/var-log  →  /var/log

Environment (sanitised):

DISCOURSE_HOSTNAME            = lab.justaguylinux.com
DISCOURSE_DEVELOPER_EMAILS    = <REDACTED>
DISCOURSE_NOTIFICATION_EMAIL  = <REDACTED>

DISCOURSE_SMTP_ADDRESS        = smtp.protonmail.ch
DISCOURSE_SMTP_PORT           = 587
DISCOURSE_SMTP_USER_NAME      = <REDACTED>
DISCOURSE_SMTP_DOMAIN         = justaguylinux.com
DISCOURSE_SMTP_AUTHENTICATION = login
DISCOURSE_SMTP_PASSWORD       = <REDACTED in app.yml>

DISCOURSE_DB_SOCKET           = /var/run/postgresql       # in-container Postgres
DISCOURSE_DB_HOST             =                           # empty → unix socket
DISCOURSE_DB_PORT             =

UNICORN_WORKERS               = 8
UNICORN_SIDEKIQS              = 1
RAILS_ENV                     = production
RUBY_VERSION                  = 3.4.7
PG_MAJOR                      = 15

Notes:

  • It's a standalone install (Postgres + Redis + Unicorn web all in the same container) — see the matching samples/standalone.yml.
  • SMTP via Protonmail with <REDACTED> as the From address.
  • 8 Unicorn workers, 1 Sidekiq for background jobs — modest sizing, fine for a small forum.
  • Postgres 15 + Ruby 3.4.7 is the current pinning of the upstream image at last rebuild.

Templates in templates/

These are reusable YAML blocks that app.yml includes. Worth knowing what's available:

Template Purpose
web.letsencrypt.ssl.template.yml Let's Encrypt for the in-container :443 listener
cloudflare.template.yml Trust Cloudflare's IP ranges for X-Forwarded-For
cron.template.yml Run scheduled jobs inside the container
sshd.template.yml SSH into the running container (debugging)
redis.template.yml In-container Redis (default for standalone)
postgres.15.template.yml In-container Postgres 15 (default)
cache-dns.template.yml Local DNS cache
offline-page.template.yml Static "we'll be back" page during rebuilds

app.yml references whichever templates this site uses — that's the part still gated behind sudo for full visibility.

Public ingress

Cloudflare Tunnel routes lab.justaguylinux.comhttp://localhost:80. The Discourse container also has a :443 listener (Let's Encrypt cert if web.letsencrypt.ssl.template.yml is included), but Cloudflare fronts everything via tunnel-to-:80, so the in-container TLS is effectively unused from the public path. The :443 listener is still present because Discourse's default expects it.

Operating

cd /var/discourse

sudo ./launcher rebuild app      # apply config changes / upgrade — recreates the container, keeps data
sudo ./launcher restart app      # restart only
sudo ./launcher logs app         # tail logs
sudo ./launcher enter app        # shell inside the container
sudo ./launcher cleanup          # prune old image layers (frees several GB)
sudo ./launcher --help

# Diagnostics
sudo ./discourse-doctor app      # health check; output goes to /tmp/discourse-debug.txt

rebuild is destructive in the sense that the container is recreated — but shared/standalone/ persists across rebuilds, so data survives.

Inside the container

sudo ./launcher enter app

discourse                        # CLI: backup, enable_restore, restore, etc.
rails c                          # Rails console
sv status                        # all in-container services (postgres, redis, unicorn, sidekiq, nginx)
tail -f /shared/log/rails/production.log

Upgrades

Two layers, upgraded independently:

  1. Discourse version + plugins + config — edit containers/app.yml, then ./launcher rebuild app.
    • Discourse pulls the latest discourse/base image, rebuilds the local image with your plugins, recreates the container.
    • First rebuild after a major version bump can take 1020 minutes.
  2. launcher script + templatescd /var/discourse && git pull.
    • These are the orchestration tools, separate from the Discourse app.

Always take a backup before rebuild (Admin UI → Backups → Create Backup) — restores from a Discourse backup tarball are by far the easiest recovery path.

Backup

See Backups. Short version:

# Use Discourse's own backup CLI — it produces one tarball with everything inside
sudo /var/discourse/launcher enter app
discourse backup
exit
# Output: /var/discourse/shared/standalone/backups/default/<timestamp>.tar.gz

# Sync that tarball off-host:
rsync -av /var/discourse/shared/standalone/backups/default/ <remote>:/backups/discourse/

Don't raw-tar /var/discourse/shared/standalone/ while the container is running — Postgres mid-write produces a broken snapshot. Use the Discourse backup CLI instead.

Restore

# Drop the .tar.gz into the backups dir on a freshly rebuilt instance
sudo cp <backup>.tar.gz /var/discourse/shared/standalone/backups/default/

sudo /var/discourse/launcher enter app
discourse enable_restore
discourse restore <backup>.tar.gz
exit

Or via the Admin UI → Backups → Upload → Restore.

Hostname trivia

The host is named discourse because this was the first service on the box. The hostname stuck even after the box absorbed everything else.