Docker deployment for NodeBB, PostgreSQL in local development, Discord forum sync, and Discord OAuth login.
nodebb-deploy contains deployment only. The plugin and worker source live in the separate discord-nodebb-sync repository.
The NodeBB image also installs and activates @nodebb/nodebb-plugin-reactions. It depends on the standard nodebb-plugin-emoji, which NodeBB installs by default. Discord sync treats reactions as optional at runtime: if the reactions plugin is disabled, normal forum synchronization continues and reaction events are ignored.
Keep the repositories next to each other:
nodebb-deploy/
discord-nodebb-sync/
Create the local environment:
cp .env.example .envStart PostgreSQL + NodeBB without the Discord worker:
docker compose -f docker-compose.dev.yml up -d --buildStart the full dev stack including the worker:
docker compose -f docker-compose.dev.yml --profile discord-worker up -d --buildThe dev NodeBB image packs the local sibling plugin with npm pack, so unpublished plugin changes can be tested locally. The worker image is also built from the sibling repository.
For local OAuth use:
NODEBB_URL=http://localhost:4567and register this Discord OAuth redirect:
http://localhost:4567/auth/discord/callback
discord-nodebb-sync has one version for both artifacts. A release such as v0.2.0 publishes the worker image:
ghcr.io/nu31hackerspace/discord-nodebb-sync-worker:0.2.0
The NodeBB plugin is installed directly from the matching GitHub tag in the discord-nodebb-sync repository:
https://github.com/nu31hackerspace/discord-nodebb-sync/archive/refs/tags/v0.2.0.tar.gz
nodebb-deploy uses one GitHub variable for both:
DISCORD_SYNC_VERSION=0.2.0
On every push to main, .github/workflows/publish.yml:
- builds the production NodeBB image;
- installs the Discord sync NodeBB plugin from GitHub tag
v${DISCORD_SYNC_VERSION}; - pushes the NodeBB image to GHCR as
latestand${GITHUB_SHA}; - creates the deployment env file;
- deploys
docker-stack.ymlto the Docker Swarm manager over SSH withcssnr/stack-deploy-action.
The Swarm stack then runs the NodeBB image from this repository together with discord-nodebb-sync-worker:${DISCORD_SYNC_VERSION}.
This follows the same deploy pattern as the other NU31 services: GHCR image build followed by remote Docker Stack deployment.
Repository variables:
HOST
USERNAME
DISCORD_SYNC_VERSION
NODEBB_URL
DISCORD_GUILD_ID
IMPORT_BOTS
Repository secrets:
ROOT_SSH_PRIVATE_KEY
DATABASE_HOST
DATABASE_NAME
DATABASE_USER
DATABASE_PASSWORD
DISCORD_SYNC_SECRET
DISCORD_BOT_TOKEN
DISCORD_OAUTH_CLIENT_ID
DISCORD_OAUTH_CLIENT_SECRET
Production database values are intentionally kept together in Secrets. DATABASE_HOST is a required GitHub Secret containing the Docker Swarm DNS name of the PostgreSQL service on infra_postgres-net. There is no default or hardcoded production database host.
The production database secrets are:
DATABASE_HOST
DATABASE_NAME
DATABASE_USER
DATABASE_PASSWORD
There is no production database-port setting. PostgreSQL uses its standard internal port 5432, which is fixed in docker-stack.yml. Caddy is not involved in database traffic; NodeBB connects directly to PostgreSQL over infra_postgres-net.
NODEBB_URL must be the real public forum URL, for example:
https://community.nu31.space
Register the matching Discord OAuth redirect:
https://community.nu31.space/auth/discord/callback
The production stack expects the existing infrastructure networks:
infra_reverse-proxy
infra_postgres-net
NodeBB joins both. PostgreSQL is external and is not created by this stack.
The worker is not exposed through Caddy. NodeBB and the worker communicate over the stack-private sync-net overlay network.
Before the first production deploy, create a dedicated PostgreSQL role/database, for example:
CREATE USER nodebb WITH PASSWORD 'change-me';
CREATE DATABASE nodebb OWNER nodebb;
GRANT ALL PRIVILEGES ON DATABASE nodebb TO nodebb;DATABASE_PASSWORD is the password for this dedicated DATABASE_USER. It is deliberately separate from the infrastructure PostgreSQL superuser password.
Add a Caddy route to the infra repository for the chosen NodeBB domain. For example:
community.nu31.space {
reverse_proxy * nodebb_app:4567
}Then redeploy infra. The nodebb_app service is reachable because it joins infra_reverse-proxy.
Release the sync repository first, for example v0.2.1. After the tag is pushed and the worker image is published, update one variable in the nodebb-deploy GitHub repository:
DISCORD_SYNC_VERSION=0.2.1
Then push/re-run the nodebb-deploy workflow. Plugin and worker cannot be configured to different versions.
Local NodeBB:
docker compose -f docker-compose.dev.yml logs -f --tail=100 nodebbLocal worker:
docker compose -f docker-compose.dev.yml logs -f --tail=100 discord_workerProduction logs are available through the existing infra Loki/Grafana log collection, or directly with docker service logs on the Swarm manager.
docker compose -f docker-compose.dev.yml --profile discord-worker run --rm discord_worker \
node src/cli.js reset --channel <DISCORD_CHANNEL_ID>