SSD Nodes Learn 🎉 VPS vanaf $5.50/mnd
Gidsen Matt ConnorDoor Matt Connor · Bijgewerkt 2026-08-13

Discourse installeren op een VPS met Docker

Leer hoe u Discourse succesvol installeert op een VPS met het officiële Docker-script. Wij behandelen de app.yml configuratie, SMTP-instellingen, swap en TLS-certificaten.

Discourse installeren op een VPS: één container, één configuratiebestand

Om Discourse op een VPS te installeren, voert u het eigen installatieprogramma van het project uit, beantwoordt u een korte wizard en wacht u op de build. Discourse wordt geleverd als één Docker-container die de Rails-applicatie, PostgreSQL, Redis en nginx bevat. Alles wat u later wijzigt, bevindt zich in één bestand, /var/discourse/containers/app.yml, en elke wijziging wordt op de site doorgevoerd via een rebuild.

De officiële installatie is discourse_docker: een launcher shell-script plus een set YAML-templates. Discourse ondersteunt geen Compose-bestand dat u zelf schrijft, en het is niet de bedoeling dat de container handmatig wordt opgesplitst. Als u gewend bent aan het draaien van services op een VPS met Docker Compose, houd dan rekening met een andere opzet. Er is hier geen docker compose up -d, en ./launcher rebuild app is de manier van deployen.

Wat Discourse vereist voordat u begint

Vier vereisten zorgen vaak voor problemen, en elk daarvan speelt op voordat u de inlogpagina bereikt.

  • Geheugen. Eén container draait PostgreSQL, Redis, Sidekiq en een Ruby-webserver. De build-stap compileert assets en vereist meer geheugen dan de draaiende site.
  • Een echte domeinnaam. De meegeleverde voorbeeldconfiguratie stelt het duidelijk: "Discourse werkt niet met een kaal IP-adres."
  • Een uitgaand mailpad. Accountactivatie, wachtwoordresets, beheerdersuitnodigingen en digest-mails verlopen allemaal via SMTP (Simple Mail Transfer Protocol).
  • Poorten 80 en 443 vrij op de host, tenzij u Discourse bewust achter een proxy plaatst die u al gebruikt.
ChartDiscourse published hardware requirements (official install docs, August 2026)
The data behind this chart
[
  {
    "label": "Documented minimum",
    "ram_gb": 1,
    "storage_gb": 10
  },
  {
    "label": "Documented recommended",
    "ram_gb": 2,
    "storage_gb": 20
  }
]

Het officiële installatiedocument stelt de ondergrens op 1 GB RAM met swap en 10 GB schijfruimte, en adviseert 2 GB RAM met 20 GB schijfruimte. Lees de eerste rij als het getal waarmee de installer kan voltooien, niet als het getal dat u wilt voor het draaien van een community. Het verschil is van belang omdat de piek in geheugengebruik optreedt tijdens de build, niet door het netwerkverkeer.

Verwijs het domein naar de server voordat u de installatie start

Maak een A-record aan voor de hostnaam die u gaat gebruiken en controleer dit vervolgens vanaf de server zelf.

dig +short forum.example.com
curl -4 -s https://ifconfig.co

Beide commando's moeten hetzelfde adres retourneren. Ze moeten overeenstemmen omdat de installatiewizard een verbindingstest uitvoert op uw hostnaam; een record dat nog naar een andere locatie wijst, zal deze test niet doorstaan. Een record dat u twee minuten geleden heeft aangemaakt, kan bovendien nog in de cache staan. Wacht daarom tot de oude TTL (time to live) is verlopen in plaats van de wizard te forceren.

Bepaal nu of het record via een CDN wordt geproxied. Een geproxied record verbergt uw serveradres, waardoor de certificaataanvraag van de container mislukt; de ACME-challenge (automatic certificate management environment) wordt namelijk beantwoord door de proxy in plaats van door Discourse. Houd het record voor de eerste installatie ongeproxied.

De officiële installer uitvoeren

Eén commando installeert git, installeert Docker met het eigen installatiescript van Docker, kloont discourse_docker naar /var/discourse en start de installatiewizard.

wget -qO- https://raw.githubusercontent.com/discourse/discourse_docker/main/install-discourse | sudo bash

Als Docker al op de server aanwezig is en u liever elke stap afzonderlijk ziet, voer de werkzaamheden dan handmatig uit.

sudo -s
git clone https://github.com/discourse/discourse_docker.git /var/discourse
cd /var/discourse
./discourse-setup

Voer dit uit als root. Wanneer gestart als een gewone gebruiker, stopt discourse-setup direct met This script must be run as root. Please sudo or log in as root first.. Zonder Docker op de server stopt het met Docker is not installed. Please install Docker first., omdat de handmatige kloon niets voor u installeert.

Wat de installatiewizard vraagt en wat deze wegschrijft

Sinds augustus 2026 is discourse-setup een dunne wrapper. Deze voert discourse/setup-wizard:release uit als een container met het hostnetwerk en de Docker-socket gemount, zodat de wizard de machine die hij configureert kan inspecteren. De wizard vraagt om de hostnaam en de e-mailadressen van de beheerder, en vervolgens om uw SMTP-gegevens. Hij schrijft containers/app.yml weg en voert daarna een rebuild uit.

Twee gedragingen zijn het vermelden waard voordat u begint. Als de machine over te weinig geheugen beschikt en geen swap heeft, stopt de wizard en biedt hij aan deze aan te maken: de wrapper maakt vervolgens een /swapfile van 2 GB aan, voegt deze toe aan /etc/fstab, stelt vm.swappiness = 10 in /etc/sysctl.d/30-discourse-swap.conf in en start de wizard opnieuw. Wanneer de wizard klaar is, print hij Rebuilding app in 5 seconds (Ctrl+C to cancel)... en voert hij ./launcher rebuild app uit op de host. Deze build duurt enkele minuten op een kleine VPS, en de eerste keer is het traagst omdat elk asset vanaf nul wordt gecompileerd.

./discourse-setup --help somt de vlaggen op die van belang zijn wanneer er iets misgaat. --skip-rebuild schrijft de configuratie weg zonder te bouwen, en --skip-connection-test slaat de DNS- en poortcontroles over. Gebruik --skip-connection-test alleen wanneer u al weet waarom de test faalt, bijvoorbeeld wanneer de host zich achter een netwerkfirewall bevindt die u zelf beheert.

Lees app.yml vóór de eerste rebuild

De wizard maakt een bestand aan dat u vanaf nu zelf beheert. Open het met sudo nano /var/discourse/containers/app.yml. De onderstaande onderdelen bepalen vrijwel alles.

templates:
  - "templates/postgres.template.yml"
  - "templates/redis.template.yml"
  - "templates/web.template.yml"
  - "templates/web.ratelimited.template.yml"
  ## Uncomment these two lines if you wish to add Lets Encrypt (https)
  #- "templates/web.ssl.template.yml"
  #- "templates/web.letsencrypt.ssl.template.yml"

expose:
  - "80:80"   # http
  - "443:443" # https

env:
  DISCOURSE_HOSTNAME: "forum.example.com"
  DISCOURSE_DEVELOPER_EMAILS: "you@example.com"
  DISCOURSE_SMTP_ADDRESS: smtp.example.com
  DISCOURSE_SMTP_PORT: 587
  DISCOURSE_SMTP_USER_NAME: user@example.com
  DISCOURSE_SMTP_PASSWORD: "your-smtp-password"

DISCOURSE_HOSTNAME is het adres waarop de site reageert. Discourse gebruikt dit voor het opbouwen van links; een onjuiste waarde zorgt ervoor dat de site één keer laadt en u vervolgens naar een andere locatie doorstuurt. DISCOURSE_DEVELOPER_EMAILS is een door komma's gescheiden lijst. De adressen in deze lijst krijgen automatisch beheerdersrechten bij de eerste registratie. Voer hier uw eigen adres in en registreer uzelf daarmee, aangezien dit de manier is waarop het eerste beheerdersaccount wordt aangemaakt.

Het bestand slaat uw SMTP-wachtwoord op in platte tekst. Beperk daarom de toegang tot de map met sudo chmod 700 /var/discourse/containers. Het bestand is opgesteld in YAML, wat betekent dat witruimte onderdeel is van de configuratie: een verkeerd uitgelijnde sleutel zorgt voor een parse-fout tijdens de build, waardoor de site niet beschikbaar is. Eén valkuil staat gedocumenteerd in het voorbeeldbestand zelf. Een # binnen een niet-gequoteerd wachtwoord wordt geïnterpreteerd als het begin van een commentaar. Plaats daarom elk wachtwoord dat dit teken bevat tussen aanhalingstekens.

E-mail is de stap waar de meeste installaties op vastlopen

Sinds augustus 2026 staat de wizard toe dat u SMTP overslaat en in plaats daarvan Discourse ID-logins gebruikt, en app.yml bevat een bijbehorende DISCOURSE_SKIP_EMAIL_SETUP-schakelaar, die daar wordt beschreven als het overslaan van de validatie van de e-mailconfiguratie. Overslaan is redelijk voor een eerste blik op de software. Het is een slechte keuze voor een community, omdat zonder uitgaande e-mail niemand een account kan activeren of een wachtwoord kan resetten.

Het praktische probleem is dat de meeste VPS-providers uitgaande poort 25 blokkeren, waardoor een standaard mailserver op de server niets zal afleveren. Gebruik een geauthenticeerde relay op poort 587, of op 465 met impliciete TLS (transport layer security). Voor 465 stelt u DISCOURSE_SMTP_FORCE_TLS: true in, wat de voorbeeldconfiguratie aanbeveelt voor die poort. Test de bereikbaarheid vanaf de host voordat u herbouwt.

nc -vz smtp.example.com 587

Een gezond resultaat is een enkele regel die eindigt op succeeded!. Een commando dat blijft hangen en vervolgens een time-out geeft, betekent dat de poort wordt geblokkeerd op het pad uit uw VPS, en geen enkele Discourse-instelling lost dat op. Stap over naar een poort die uw provider toestaat, of vraag de provider om deze te openen.

Zodra de site actief is, verstuurt u een testbericht vanaf de pagina Email in Admin, en leest u vervolgens de tabbladen Skipped en Bounced op diezelfde pagina. Op die tabbladen registreert Discourse e-mail die het weigerde te verzenden en e-mail die de relay afwees, en daar wordt de reden vermeld, wat sneller is dan het lezen van logs.

TLS: laat de container een eigen certificaat ophalen

Als Discourse poort 80 en 443 beheert, gebruik dan de ingebouwde uitgiftefunctionaliteit. Verwijder het commentaarteken voor de twee SSL-template-regels die hierboven worden getoond en voer vervolgens een rebuild uit. De template stuurt acme.sh aan, slaat certificaten op in het gedeelde volume onder /shared/ssl, vernieuwt deze volgens een schema binnen de container en configureert Discourse om HTTPS af te dwingen.

Poort 80 moet bereikbaar blijven vanaf het internet om dit te laten werken, omdat de HTTP-challenge daar wordt beantwoord. Een firewall die alleen poort 443 toestaat, resulteert in een build die wel voltooit, maar een certificaat dat nooit wordt uitgegeven. Controleer het resultaat direct na de rebuild met ./launcher logs app.

Moet u nginx of Caddy ervoor plaatsen?

Als Discourse de enige webservice op de VPS is, doe dit dan niet. De container draait al een geoptimaliseerde nginx; een tweede proxy voegt een extra stap toe, nog een certificaat om te vernieuwen en een nieuwe bron voor header-fouten.

Plaats er een proxy voor wanneer dezelfde VPS ook andere sites bedient. Voeg templates/web.socketed.template.yml toe aan de lijst met templates, zet beide expose-regels in commentaar en laat de twee SSL-templates in commentaar staan. De container luistert dan op een unix-socket op /var/discourse/shared/standalone/nginx.http.sock en gebruikt helemaal geen poorten, waardoor 80 en 443 vrijkomen voor uw eigen proxy.

server {
  listen 443 ssl;
  server_name forum.example.com;

  location / {
    proxy_pass http://unix:/var/discourse/shared/standalone/nginx.http.sock:;
    proxy_set_header Host $http_host;
    proxy_http_version 1.1;
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Real-IP $remote_addr;
  }
}

De dubbele punt na .sock is onderdeel van de unix-socket-syntaxis van nginx en sudo nginx -t weigert de configuratie zonder dit teken. X-Forwarded-Proto is evenmin optioneel. Discourse schrijft absolute links; zonder die header genereert het http://-links op een HTTPS-pagina, die door browsers worden geblokkeerd als mixed content. Nu de container via een socket verbonden is, is TLS uw verantwoordelijkheid. Vraag het certificaat daarom aan op de host via Certbot op Ubuntu 24.04 en nginx. Als u nog geen keuze heeft gemaakt voor een proxy, behandelt de vergelijking tussen nginx, Caddy en Traefik de afwegingen die u maakt.

Rebuilds, upgrades en de commando's die u daadwerkelijk zult gebruiken

cd /var/discourse
./launcher rebuild app

rebuild vernietigt de draaiende container, bootstrap een nieuwe vanaf app.yml en start deze op. De site is gedurende de gehele build offline; beschouw elke configuratiewijziging daarom als gepland onderhoud van enkele minuten.

Het wijzigen van waarden onder env: vereist dit niet. ./launcher destroy app && ./launcher start app maakt de container opnieuw aan op basis van de image die u al heeft gebouwd, wat slechts seconden duurt. Alles onder templates: of hooks: wijzigt de image zelf, waardoor een volledige rebuild noodzakelijk is.

Upgrades komen op twee manieren binnen. Point-releases worden toegepast via de webinterface op /admin/upgrade, geleverd door de docker_manager-plugin die app.yml tijdens de build kloont. Wijzigingen aan de basis-image of de templates komen via git.

cd /var/discourse
git pull
./launcher rebuild app

Rebuilds zijn het punt waarop kleine servers falen, omdat het compileren van assets de piekbelasting van het geheugen voor het hele systeem vormt. Een build die halverwege stopt, waarbij dmesg een regel toont zoals Out of memory: Killed process met de naam van een ruby-proces, kwam tijdens de build geheugen tekort, ook al draaide de site zelf daarvoor prima. Voeg swap toe en voer de rebuild opnieuw uit.

./launcher logs app
./launcher enter app
./launcher cleanup

logs toont de output van de container, enter opent een shell in de container en cleanup verwijdert containers die langer dan 24 uur zijn gestopt. Voer cleanup regelmatig uit, omdat elke rebuild een oude container achterlaat en de schijfruimte op een kleine VPS stilletjes volloopt.

Backups en het bestand dat de backup niet bevat

Maak backups via de pagina Backups in het Admin-paneel. Het archief wordt op de host opgeslagen op /var/discourse/shared/standalone/backups/default/. Dezelfde taak kan vanuit een shell worden uitgevoerd.

cd /var/discourse
./launcher enter app
discourse backup

discourse restore <filename> draait dit proces terug; restores worden geweigerd totdat u discourse enable_restore uitvoert. Deze beveiliging is aanwezig om te voorkomen dat een per ongeluk uitgevoerd commando een live forum overschrijft.

Er zijn twee hiaten die u zelf moet dichten. Het archief bevat de database en bevat alleen geüploade bestanden als de backup-instelling die uploads meeneemt is ingeschakeld; controleer deze instelling dus voordat u op de backup vertrouwt. Het archief bevat nooit app.yml, dus een restore op een nieuwe VPS vereist nog steeds uw hostname en SMTP-blok. Dit betekent dat u dit bestand ook buiten de server moet opslaan.

Het archief bevindt zich bovendien op dezelfde schijf als de site die het beschermt; dat is geen backup. Verplaats het archief volgens een vast schema naar een andere locatie.

rsync -avz root@forum.example.com:/var/discourse/shared/standalone/backups/default/ ~/discourse-backups/

Wat een druk forum kost aan RAM

De bootstrap stelt UNICORN_WORKERS en db_shared_buffers in op basis van het gedetecteerde geheugen en CPU, en de voorbeeldconfiguratie begrenst shared buffers op een kwart van het totale geheugen. Elke unicorn worker is een volledig Ruby-proces en Sidekiq voert achtergrondtaken uit naast deze processen, waardoor het geheugengebruik correleert met het aantal gelijktijdige verzoeken in plaats van het aantal geregistreerde leden. Een rustig forum met een paar honderd leden vormt geen zware belasting.

Bepaal de servergrootte niet op basis van een getal uit een artikel, inclusief dit artikel. Meet uw eigen verbruik.

free -m
docker stats --no-stream

Swap die constant in gebruik is in combinatie met trage pagina's betekent dat u een tekort aan RAM heeft. Stabiel geheugen met trage pagina's wijst meestal op iets anders, dus lees ./launcher logs app voordat u een groter abonnement aanschaft. Voeg ook een controle toe vanaf een externe locatie, want een forum dat 's nachts om 3 uur zonder geheugen komt te zitten, faalt geruisloos: een zelfgehoste Uptime Kuma statusmonitor op een aparte host waarschuwt u voordat uw leden dat doen.

Wanneer Discourse de verkeerde keuze is

Discourse is een grote applicatie met een zware installatie en een rebuild-cyclus voor elke instelling die in app.yml staat. Die investering levert volwaardige moderatietools op en een zoekfunctie die ook bij grote archieven blijft werken. Voor dertig mensen die een plek zoeken om te praten, is het meer machine dan het gesprek vereist. Lees eerst de vergelijking van zelfgehoste forumsoftware en kies voor Discourse omdat u de functionaliteit nodig heeft, niet omdat het de naam is die u al kende.

FAQ

Kan ik Discourse installeren op een VPS zonder domeinnaam?

Nee. De meegeleverde configuratie stelt dat Discourse niet werkt met een kaal IP-adres en DISCOURSE_HOSTNAME is vereist. Discourse bouwt absolute links op basis van die hostnaam; een IP-adres op die plek maakt links onbruikbaar en blokkeert de uitgifte van certificaten. Maak een A-record aan voordat u begint en bevestig met dig +short forum.example.com dat dit naar het adres van uw server verwijst.

Moet ik SMTP configureren om de installatie te voltooien?

Sinds augustus 2026 kunt u dit overslaan. De installatiewizard biedt in plaats daarvan Discourse ID-aanmeldingen aan en app.yml bevat een schakelaar die de validatie van de e-mailinstellingen overslaat. Configureer SMTP voor elk gebruik na de eerste kennismaking, omdat accountactivatie en wachtwoordherstel via e-mail verlopen. Gebruik een geauthenticeerde relay op poort 587 of 465, aangezien de meeste VPS-providers uitgaand verkeer op poort 25 blokkeren.

Waarom is mijn Discourse-rebuild halverwege mislukt?

Geheugentekort is de gebruikelijke oorzaak. Het compileren van assets tijdens de build vereist meer geheugen dan de draaiende site zelf; een server die het forum prima kan hosten, kan dus alsnog falen tijdens een rebuild. Als dmesg de melding Out of memory: Killed process geeft bij een ruby-proces, voeg dan swap toe (de swapfile van de wizard is 2 GB) en voer ./launcher rebuild app opnieuw uit. Een build die stopt met een YAML-fout wijst op een inspringfout in app.yml.

Moet Discourse achter mijn eigen Nginx of Caddy draaien?

Alleen als de VPS ook andere sites host. Als Discourse alleen op een server staat, laat de container dan poorten 80 en 443 beheren en zijn eigen certificaat uitgeven; dit zorgt voor minder complexe configuraties. Om de machine te delen, voegt u templates/web.socketed.template.yml toe, becommentarieert u de expose-regels en proxyt u naar de unix-socket op /var/discourse/shared/standalone/nginx.http.sock. Geef X-Forwarded-Proto door, anders genereert Discourse http://-links op een HTTPS-pagina.

Hoe maak ik een back-up van een zelfgehoste Discourse?

Gebruik de pagina Backups in het beheerderspaneel of voer discourse backup uit na ./launcher enter app. Archieven worden op de host opgeslagen op /var/discourse/shared/standalone/backups/default/. Controleer of de instelling voor het opnemen van uploads is ingeschakeld, kopieer /var/discourse/containers/app.yml samen met het archief en verplaats beide naar een andere machine. Een back-up op dezelfde schijf als de site overleeft immers niet het defect waarvoor de back-up dient.