Thanks for the feedback, really appreciate the time. Not to be that smartass, but:
[2/5] Preparing configuration
✓ Ports 80 and 443 are free for use
It does initially say “free for use”. That said, I have my instance up and running now, with the help of Gemini (mostly about docker usage of all things lol). I would like to offer my “runbook” for any other Virtualmin users out there, because: “free the 443 port” is not the solution here. The rest of my post will be that, if I should rather post it somewhere else, like a new thread, please tell me where to go; I think I’m not the only one with this setup, and it might be useful for others. Thanks again!
So, after the setup has been completed with
cd /var/discourse
./discourse-setup --skip-connection-test
(c/o @darkpixlz
)
… you would do the following steps when on a VPS managed via Webmin and Virtualmin, and using a subdomain.
The Definitive Virtualmin + Discourse Runbook *
-
(1) Cleaning up remnants (If retrying, like I was):
rm -rf /var/discourse/shared/standalone/ssl/*rm -rf /var/discourse/shared/standalone/letsencryptrm -rf /var/discourse/shared/standalone/state -
(2) Template Deletion:
You must completely delete the
templates/web.ssl.template.ymlandtemplates/web.letsencrypt.ssl.template.ymllines fromapp.yml. The custom launcher parser will evaluate them even if prefixed with#. -
(3) Email Setup & Variables:
Change
DISCOURSE_SKIP_EMAIL_SETUPfrom'1'to'0', because your Discourse won’t be able to connect and check DiscordID;Add
DISCOURSE_FORCE_HTTPS: trueso the backend generates secure URLs.Friendly reminder: Ensure your
DISCOURSE_SMTP_USER_NAMEis set to your raw mailbox account name (e.g.,'logophilia'), not the full email address (e.g.,'logophilia@logophilia.eu'), and wrap the credentials in single quotes (') to bypass potential YAML character parsing bugs. -
(4) Expose Block Configuration:
Ensure your
expose:block in app.yml contains a HTTP mapping; mapping443=>8443is optional / redundant, since Virtualmin terminates the SSL logic before passing it along:expose: - 8080:80you can start rebuilding now:
cd /var/discourse ./launcher rebuild app -
(5) Subdomain and Proxy Path Setup:
- Create your subdomain in Virtualmin as usual, and secure it with a Let’s Encrypt SSL certificate (done automatically, just saying: make sure it doesn’t error out for whatever unrelated reason).
- Navigate to Proxy Paths (Virtualmin → your subdomain → Web Configuration → Proxy Paths), create a new mapping
/tohttp://localhost:8080/, keep “serve locally” unchecked, but toggle Proxy WebSocket to Yes to allow real-time updates and notification streams.
-
(6) CSRF Header Directives:
- In Webmin ➔ Servers ➔ Apache Webserver ➔ [find your subdomain config here and click on the config for 443] ➔ Edit Directives, place the following lines right above Virtualmin’s own proxy block for Let’s Encrypt (usually “ProxyPass /.well-known !”) to facilitate CSRF token forwarding:
ProxyPreserveHost On RequestHeader set X-Forwarded-Proto "https" RequestHeader set X-Forwarded-For %{REMOTE_ADDR}sProxyPreserveHost On: Tells Discourse your actual domain name instead of “localhost”.
RequestHeader set X-Forwarded-Proto "https": Explicitly tells Discourse the user is using a secure connection, matching up with yourDISCOURSE_FORCE_HTTPS: truesetting.
RequestHeader set X-Forwarded-For: Passes the visitor’s real IP address to the container so security logs work. -
(7) Clean Container Handshake:
While the long (sorry … it’s true though;-) rebuild process finishes, ensure any potentially stuck container blueprint is wiped with
docker rm -f appso that running./launcher start appspins up a completely fresh instance bound to port8080. Check that docker ps shows something under “ports” similar to:# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES d21772a21e36 local_discourse/app "/sbin/boot" 45 minutes ago Up 45 minutes 0.0.0.0:8080->80/tcp, [::]:8080->80/tcp, 0.0.0.0:8443->443/tcp, [::]:8443->443/tcp app(As you can see, I left the 443=>8443 directive in my app.yml, works either way.)
-
(8) Monitoring & Launch:
Tail the boot stream with
docker logs -f appuntil the database migrations finish and the workers start processing requests. A bunch of “INFO” lines in quick succession, basically. -
(9) Finalization:
Load your subdomain in a browser, click Register, and let the system send a validation email to your mailbox.
*) until proven wrong ![]()