Rocket.Chat tails the Mongo oplog for realtime delivery, and a standalone mongod has no oplog — so the database has to be a replica set even with one member. Uses the official mongo image rather than bitnami/mongodb (which upstream's own compose uses) because Bitnami moved its catalog behind a paid registry and the free tags are no longer dependable for a long-lived install. The cost is that rs.initiate() is not automatic, so the post-start hook runs it once — guarded by rs.status() so a reinstall over restored data doesn't re-initiate a live set, and followed by a wait for the member to report itself primary. Mongo runs without auth: enabling it on a replica set also requires a shared keyfile for member-to-member auth, which is a lot of moving parts for a database that is never published outside the docker network. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
80 lines
3.1 KiB
Bash
80 lines
3.1 KiB
Bash
#!/bin/bash
|
|
|
|
# Rocket.Chat install hooks.
|
|
#
|
|
# Rocket.Chat needs its Mongo to be a replica set, because realtime message
|
|
# delivery works by tailing the oplog and a standalone mongod has no oplog. The
|
|
# official mongo image does not initiate a set on its own, so we do it once here
|
|
# after the containers come up.
|
|
|
|
rocketchat_install_post_start()
|
|
{
|
|
local app_name="$1"
|
|
|
|
((menu_number++))
|
|
echo ""
|
|
echo "---- $menu_number. Initiating the MongoDB replica set for $app_name"
|
|
echo ""
|
|
|
|
# Wait for mongod to answer at all. It has to be listening before
|
|
# rs.initiate() can be sent, and on a cold volume the first boot spends a
|
|
# while building journal files.
|
|
local attempts=0
|
|
while ((attempts < 45)); do
|
|
if runFileOp docker exec rocketchat-db mongosh --quiet --eval "db.adminCommand({ping:1}).ok" 2>/dev/null | grep -q 1; then
|
|
break
|
|
fi
|
|
sleep 2
|
|
((attempts++))
|
|
done
|
|
|
|
if ((attempts >= 45)); then
|
|
isError "rocketchat-db never started accepting connections — replica set not initiated."
|
|
isNotice "Rocket.Chat will keep restarting until it is. Once the database is up, run:"
|
|
isNotice " docker exec rocketchat-db mongosh --eval 'rs.initiate({_id:\"rs0\",members:[{_id:0,host:\"rocketchat-db:27017\"}]})'"
|
|
return 0
|
|
fi
|
|
|
|
# Idempotent: a reinstall over restored data, or a rerun of this hook, finds
|
|
# the set already configured and must not re-initiate it — rs.initiate() on
|
|
# a live set errors out and would leave the step looking failed.
|
|
if runFileOp docker exec rocketchat-db mongosh --quiet --eval "rs.status().ok" 2>/dev/null | grep -q 1; then
|
|
isSuccessful "MongoDB replica set rs0 already initiated — leaving it alone."
|
|
else
|
|
local result
|
|
result=$(runFileOp docker exec rocketchat-db mongosh --quiet --eval \
|
|
'rs.initiate({_id:"rs0",members:[{_id:0,host:"rocketchat-db:27017"}]})' 2>&1)
|
|
checkSuccess "Initiating MongoDB replica set rs0"
|
|
fi
|
|
|
|
# Election of the single member takes a moment. Rocket.Chat exits rather
|
|
# than retries if it connects before a primary exists, so wait for the node
|
|
# to report itself primary before handing control back.
|
|
attempts=0
|
|
while ((attempts < 30)); do
|
|
if runFileOp docker exec rocketchat-db mongosh --quiet --eval "db.hello().isWritablePrimary" 2>/dev/null | grep -q true; then
|
|
break
|
|
fi
|
|
sleep 2
|
|
((attempts++))
|
|
done
|
|
|
|
# Restart so Rocket.Chat reconnects immediately instead of waiting out its
|
|
# own backoff after the crash-loop it was in while the set was forming.
|
|
dockerComposeRestart "$app_name"
|
|
}
|
|
|
|
rocketchat_install_post()
|
|
{
|
|
local app_name="$1"
|
|
echo ""
|
|
isNotice "Rocket.Chat first run:"
|
|
echo ""
|
|
echo " Open the web interface and complete the setup wizard — the first"
|
|
echo " account you create there becomes the workspace administrator."
|
|
echo ""
|
|
echo " For voice and video, point Rocket.Chat at your Jitsi Meet server"
|
|
echo " under Admin -> Settings -> Video Conference -> Jitsi."
|
|
echo ""
|
|
}
|