Table of contents
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.com → http://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:
- Discourse version + plugins + config — edit
containers/app.yml, then./launcher rebuild app.- Discourse pulls the latest
discourse/baseimage, rebuilds the local image with your plugins, recreates the container. - First rebuild after a major version bump can take 10–20 minutes.
- Discourse pulls the latest
launcherscript + templates —cd /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.
Navigation
Quick Links
Projects
Server (discourse)
- Host
- Cloudflare Tunnel
- Tailscale
- SFTP
- Discourse
- Forgejo
- Filedrop
- Filebrowser
- Landing Sites
- Onboarding
- Backups
- Recovery
Window Managers
Homelab
Docker
Software
Thoughts
Archived
Connect
Butterbian — my Debian 13 distro · butterrepo — community APT repo · butterknife — TUI installer
justaguylinux.com · Butterforge · Butter Lab · The Churn · YouTube · Mastodon · Links · Contact · Support on Buy Me a Coffee
"Not an expert. Just A Guy." — Licensed CC BY-SA 4.0 · © 2026 JustAGuy Linux