Skip to content

feat: add Rocket.Chat LXC script - #2313

Closed
william-aqn wants to merge 7 commits into
community-scripts:mainfrom
william-aqn:feat/rocketchat
Closed

william-aqn wants to merge 7 commits into
community-scripts:mainfrom
william-aqn:feat/rocketchat

Conversation

@william-aqn

Copy link
Copy Markdown

✍️ Description

Adds ct/rocketchat.sh, install/rocketchat-install.sh and json/rocketchat.json.

Rocket.Chat installed bare-metal from the official prebuilt Meteor bundle on Debian 13, backed by a local MongoDB 8.0 single-node replica set. Defaults 2 CPU / 4096 MB / 20 GB. Config lives in /etc/rocketchat/rocketchat.env, outside the bundle directory an update replaces wholesale.

Supersedes #2250, which I opened while PRs were closed.

Where the install steps came from

Not from the docs: the docs and the shipped code disagree, so the thresholds come out of the bundle itself (programs/server/app/app.js) and from releases.rocket.chat/<version>/info.

  • Runtime: Node 22. engines pins 22.22.3, but the server validates only the major line at startup, against bundle/.node_version.txt. Any 22.x boots.
  • Database: MongoDB 8.0. /info reports compatibleMongoVersions: ["8.0"]; the code hard-exits below 7.0 and warns below 8.0.
  • Install sequence: bundle/README - (cd programs/server && npm install), then node main.js with MONGO_URL, ROOT_URL, PORT.

Findings worth a reviewer's attention

The setup wizard cannot be completed on a fresh install, so the script skips it. Its last step waits on an emailed confirmation code; a fresh install has no MAIL_URL, so the mail never leaves the box and the UI sits on step 4 with an empty address and a disabled code field. The admin created in step 1 exists, but Show_Setup_Wizard stays in_progress, so logging in only returns to the dead end. Reproduced from scratch: after the organization step it jumps past the Register/Standalone choice, cloud.registerPreIntent returns {"offline":false,"success":true}, and the server logs an unhandled rejection from Meteor's email package at that instant. The script therefore seeds the admin through Rocket.Chat's own ADMIN_* variables and forces Show_Setup_Wizard to completed - both mechanisms the bundle already implements (insertAdminUserFromEnv, OVERWRITE_SETTING_*), and the former only fires while no admin exists, so they stay inert on later boots.

No GitHub release assets. RocketChat/Rocket.Chat releases carry an empty assets array; the tarball lives only on releases.rocket.chat. fetch_and_deploy_gh_release / check_for_gh_release do not apply, so this uses fetch_and_deploy_from_url with the version from /latest/info. The GitHub API is not merely unnecessary but wrong here: backport releases on older majors are published after newer ones (7.10.15 landed after 8.7.1), so /releases/latest can return a 7.x tag. Two further traps on that host - HEAD returns 404 while GET returns 307, so a curl -I probe reports a live release as missing; and an unknown version returns 500, not 404.

npm pinned to 10 via NPM_VERSION. Under npm 12 the bundle fails twice over: npm 12 refuses its remote tarball dependency (EALLOWREMOTE on source-map-support), and the bundle's own npm-rebuild.js calls npm rebuild --update-binary, which npm 12 rejects.

build-essential is required, not optional. @sematext/gc-stats has no prebuilt binary for Node 22 - node-pre-gyp 404s and it compiles.

MongoDB must be a replica set. 8.x dropped oplog tailing and needs change streams and multi-document transactions; MONGO_OPLOG_URL was removed in 8.0 and is deliberately absent. Debian 13 works despite MongoDB shipping no trixie server packages - core's manage_tool_repository maps trixie onto bookworm.

NSAPP is overridden, as ct/netboot-xyz.sh does, because variables() strips only spaces: the dot in Rocket.Chat would otherwise resolve to install/rocket.chat-install.sh.

One note for the project, not this script: a bare read breaks unattended installs. The install script is reached over lxc-attach with no tty, so read hits EOF and returns 1 and the error trap fails the install. The prompt here needs || true. The same bare read -r -p appears in install/forgejo-runner-install.sh, install/valkey-install.sh, and the CONTRIBUTING example that documents the pattern.

Testing

Proxmox VE 9.2.10, Debian 13 unprivileged LXC, rebuilt from scratch after every correction - most findings above came from a failed run, not from review. Clean install ~4 min; no wizard; POST /api/v1/login with the generated password returns status: success. Both prompt paths rebuilt: var_admin_email=ops@example.net reaches the seeded account in MongoDB, and the unset path defaults and still completes. Update path exercised with an update available and without. Re-verified today against current main and Rocket.Chat 8.8.1. shellcheck clean at warning level, bar SC1090 on the mandated boot line.

🔗 Related PR / Issue

Link: #2250

✅ Prerequisites (X in brackets)

  • Self-review completed – Code follows project standards.
  • Tested thoroughly – Changes work as expected.
  • No breaking changes – Existing functionality remains intact.
  • No security risks – No hardcoded secrets, unnecessary privilege escalations, or permission issues.

🏗️ arm64 Support (X in brackets)

  • arm64 supported - Tested and supported on arm64.
  • arm64 not tested - Assumed to work on arm64, but testing has not been done.
  • arm64 not supported - Confirmed upstream dependencies or binaries do not support arm64.

var_arm64 is set to yes, decided per AGENTS.md rather than left commented: the shipped bundle carries aarch64 natives alongside the x86-64 ones (node-v127-linux-arm64-glibc/gcstats.node), anything without a prebuild is compiled by node-gyp (hence build-essential), MongoDB 8.0 has 428 arm64 packages in the Ubuntu noble suite that core maps Debian arm64 onto, and NodeSource 22 serves arm64. That is reasoned from the artifact and its dependencies - I have no arm64 host, so the checkbox above stays honest.


🛠️ Type of Change (X in brackets)

  • 🐞 Bug fix – Resolves an issue without breaking functionality.
  • ✨ New feature – Adds new, non-breaking functionality.
  • 💥 Breaking change – Alters existing functionality in a way that may require updates.
  • 🆕 New script – A fully functional and tested script or script set.
  • 🌍 Website update – Changes to website-related JSON files or metadata.
  • 🔧 Refactoring / Code Cleanup – Improves readability or maintainability without changing functionality.
  • 📝 Documentation update – Changes to README, AppName.md, CONTRIBUTING.md, or other docs.

🔍 Code & Security Review (X in brackets)

  • Follows CODE-AUDIT.md & CONTRIBUTING.md guidelines
  • Uses correct script structure (AppName.sh, AppName-install.sh, AppName.json)
  • No hardcoded credentials
  • No Docker / Docker Compose – The application is installed bare-metal; Docker is not used.
  • No git pull – Updates use fetch_and_deploy_from_url.

🤖 AI Assistance (X in brackets)

  • No AI used – Scripts were written without AI assistance.
  • AI was used – I confirm the scripts were built using AGENTS.md and .github/agents/pve-script-creator.agent.md as guidance, and the output has been reviewed and corrected to match those guidelines.

Claude Opus 5, high reasoning effort, used to research the upstream project, write both scripts, and drive the testing against a live Proxmox host, then reviewed and corrected against AGENTS.md and CONTRIBUTING.md.

This was not one-shot generated. Every version threshold, dependency and failure mode above was established by installing against real hardware, and the container was rebuilt from scratch after each correction. The npm pin, the build-essential requirement, the NSAPP override, the move to fetch_and_deploy_from_url, the wizard skip and the read / || true guard each came from an observed failure or a guideline check, not from a guess. Re-reviewed against the new binding AGENTS.md before opening this PR, which is what changed var_arm64 and the placeholder strings.

📋 Additional Information (optional)

var_testurl is intentionally unset - sync-testurl.yml fills it in once a testing issue is labelled.

🤖 Generated with Claude Code

william-aqn and others added 7 commits September 26, 2026 19:14
Installs Rocket.Chat 8.8.0 from the official prebuilt Meteor bundle on
Debian 13, backed by a local MongoDB 8.0 single-node replica set.

Notes on the non-obvious parts, all found while testing against a real
Proxmox host:

- The bundle is published on releases.rocket.chat, not as a GitHub release
  asset, so check_for_gh_release/fetch_and_deploy_gh_release cannot be used.
  /latest/info gives the stable tag as JSON; the GitHub API is unsuitable
  because backport releases on older majors are published after newer ones
  and win a "latest" query.
- npm is pinned to 10. Under npm 12 the install fails twice over: npm 12
  refuses the bundle's remote tarball dependency (EALLOWREMOTE on
  source-map-support) and the bundle's own npm-rebuild.js calls
  `npm rebuild --update-binary`, a flag npm 12 rejects.
- build-essential is required, not optional: @sematext/gc-stats has no
  prebuilt binary for Node 22 and falls back to compiling.
- MongoDB runs as a replica set because 8.x dropped oplog tailing and now
  needs change streams and multi-document transactions. MONGO_OPLOG_URL was
  removed in 8.0 and is deliberately absent.
- NSAPP is overridden because variables() only strips spaces, so the dot in
  "Rocket.Chat" would resolve to install/rocket.chat-install.sh.
- Config lives in /etc/rocketchat/rocketchat.env, outside the bundle
  directory that an update replaces wholesale.

Verified end to end on Proxmox VE 9.2.10: clean install in ~4 minutes, web
UI answering ~15s after service start, and the update path exercised both
when an update is available and when it is not.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
AGENTS.md and the PR checklist both require the existing helper over a
hand-rolled download, and fetch_and_deploy_from_url covers this case: it
detects the archive type with file(1) rather than the extension, which
matters because the release URL ends in /download and carries no suffix,
and it strips the archive's single top-level bundle/ directory into the
target. The update path gets CLEAN_INSTALL=1 so a stale bundle cannot
survive.

Retested end to end after the change: clean install and both update paths.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The wizard cannot be completed on a fresh bare-metal install. Its last step
("Awaiting confirmation") waits on an emailed confirmation code, and the
server has no MAIL_URL, so the mail never leaves the box: the UI sits on
step 4 with an empty address and an empty, disabled security-code field.
The admin account created in step 1 already exists at that point, but
Show_Setup_Wizard stays "in_progress", so logging in only returns to the
dead end.

Reproduced from scratch on a clean container, not inferred: after the
organization step the wizard jumps straight past the Register/Standalone
choice to "Awaiting confirmation", cloud.registerPreIntent returns
{"offline":false,"success":true}, and the server logs an unhandled
rejection from Meteor's email package at the same instant.

So the admin is seeded through Rocket.Chat's own ADMIN_* environment
variables and Show_Setup_Wizard is forced to "completed". Both are
mechanisms the shipped 8.8.0 bundle already implements
(insertAdminUserFromEnv, OVERWRITE_SETTING_*), and insertAdminUserFromEnv
only fires while no admin exists, so they are inert on later boots. The
generated password lands in ~/rocketchat.creds (0600), matching how
dreeve and unsloth surface credentials.

Verified on a clean rebuild: install completes, no wizard is shown, and
POST /api/v1/login with the generated password returns status success.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The admin address was hardcoded to admin@example.com, which every install
then had to correct by hand. It now follows the var_ pattern from
CONTRIBUTING: read the variable first, prompt only when it is unset, and
fall back to a default, so an unattended run is never blocked. The prompt
sits before the ~250 MB download rather than next to the config it feeds,
so an interactive run is not left waiting minutes for a question.
Declared in json as app_vars so the website generator can offer the field.

Two details found by testing rather than by reading:

`read` needs `|| true`. An unattended run reaches the install script over
lxc-attach with no tty, so read hits EOF and returns 1, and the error trap
turns that into a failed install before anything is downloaded. Without the
guard the default path -- the common one -- dies at line 20. The same bare
`read` appears in forgejo-runner-install.sh and valkey-install.sh, and in
the CONTRIBUTING example itself.

An invalid address is worth catching. Rocket.Chat drops an ADMIN_EMAIL it
cannot parse and leaves the admin with no address at all, which is quieter
and more confusing than a warning, so a malformed answer falls back.

Also adds `|| exit` to the two `cd` calls; shellcheck now reports both
scripts clean at warning level (bar SC1090 on the mandated boot line).

Verified on clean rebuilds of both paths: var_admin_email=ops@example.net
reaches the seeded account in MongoDB, and the unset path completes and
logs in with the generated password.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
var_arm64="no" states that arm64 was verified not to work. It was not
verified either way -- the bundle is JS and core resolves MongoDB arm64
from the Ubuntu repo, so it may well work. Unset is what the repo uses for
exactly this case ("unset = ask the user; set yes/no only when verified",
45 scripts against 5), and it lets the engine ask instead of deciding on
the user's behalf. json still declares amd64 only, matching stoatchat,
which is in the same untested state.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Found by a real run, not by review: the Proxmox web console sends a
carriage return with the line and read keeps it, so a typed d@x-crm.in
arrives as $'d@x-crm.in\r'. \r is in [:space:], so the address failed
validation and was silently replaced by the placeholder -- the check
meant to catch typos rejected a perfectly good address instead.

Stripping whitespace before validating fixes that case and the
leading/trailing space variants with it. An email address cannot contain
whitespace, so removing it is safe rather than merely convenient.

Verified against $'d@x-crm.in\r', both space variants, a genuinely
malformed address (still rejected) and the empty answer (still defaults).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
AGENTS.md became a work instruction rather than a style guide while this
sat waiting, and two of its new rules apply here.

"Write the Application's Name, Not a Placeholder": the strings this script
author wrote now say Rocket.Chat. The two that stay as ${APP} --
"No ${APP} Installation Found!" and the footer -- are verbatim from the CT
template in AGENTS.md itself, so changing them would diverge from it.

"Deciding var_arm64" forbids leaving the line commented when the answer is
knowable. It is: the shipped bundle carries aarch64 natives next to the
x86-64 ones (node-v127-linux-arm64-glibc/gcstats.node), anything without a
prebuild is compiled by node-gyp, which is why build-essential is a
dependency, and both other layers publish arm64 -- MongoDB 8.0 has 428
arm64 packages in the Ubuntu noble suite core maps Debian arm64 onto, and
NodeSource 22 serves arm64. So yes, with the caveat stated in the PR that
this is reasoned from the artifact and its dependencies, not from a run on
arm64 hardware.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@william-aqn
william-aqn requested a review from a team as a code owner September 26, 2026 16:22
@github-actions

Copy link
Copy Markdown
Contributor

❌ Pull Request Closed – PR Template Not Used

The PR description is missing the 📦 Application Requirements section, which means this PR was not opened with the ProxmoxVED PR template.

This usually happens in one of two ways:

  • The main repo's PR template was used instead of the ProxmoxVED one, or
  • The template was cleared or overwritten with a custom description.

The ProxmoxVED template contains required sections (Type of Change, Code & Security Review, Application Requirements, Source) that the maintainers rely on to review submissions. A custom description — no matter how thorough — cannot replace it.

What to do

  1. Open a new PR in this repository.
  2. When the PR form loads, the ProxmoxVED template should appear automatically in the description. If it doesn't, copy it manually from .github/pull_request_template.md.
  3. Fill out the checkboxes and relevant sections — you can keep your original write-up by pasting it into the Description section of the template.

⚠ Maintainer note

Please do not ping or repeatedly contact maintainers to reopen this PR. Open a fresh PR with the correct template instead.

@github-actions github-actions Bot closed this Sep 26, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Try this script

COMMUNITY_SCRIPTS_URL=https://raw.githubusercontent.com/william-aqn/ProxmoxVED/feat/rocketchat \
bash -c "$(curl -fsSL https://raw.githubusercontent.com/william-aqn/ProxmoxVED/feat/rocketchat/ct/rocketchat.sh)"

COMMUNITY_SCRIPTS_URL is not optional. Fetching the ct/ script from a branch
does not tell the engine where that branch is — with bash -c "$(curl …)" there
is no file on disk for the scripts root to be derived from, so it would fall back
to upstream main and look for the install script there.

Against a core branch as well

Add COMMUNITY_SCRIPTS_CORE_URL=https://raw.githubusercontent.com/OWNER/core/BRANCH
to test an engine change at the same time. The two resolve independently.

Useful flags while testing

dev_mode=net logs every fetch with status and duration, so you can confirm the
branch is really being used. dev_mode=keep stops a failed build from deleting
the container along with the evidence.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant