NodeRunner
1.4.0Run your Node.js Discord bot directly alongside your Minecraft server! Features auto-setup, live web dashboard, and in-game controls
1.4.0latestNodeRunner 1.4.0May 14, 2026 What's new in 1.4.0
Java 25 & Build Modernization
- Upgraded to Java 25 — compiled with Java 25 bytecode for the latest JVM performance and language features. - Gradle 9.5.1 — build system upgraded from Gradle 8.x to 9.5.1 for native Java 25 support. - Virtual thread executor — the dashboard HTTP server now usesnewVirtualThreadPerTaskExecutor()for non-blocking I/O scaling on Java 25. - `plugin.yml` api-version updated to1.21to match the target platform.Compatibility
- Minecraft 1.21.x – 26.2.x — compiled against Paper 1.21.1 API, which is forward-compatible with all Paper versions through 26.2.x. Existing configs remain fully compatible — no config changes required.1.3.0NodeRunner 1.3.0Apr 27, 2026 What's new in 1.3.0
Dashboard — Settings & Bot Management
The web dashboard has been completely overhauled. It is no longer just a log viewer — it now gives you full control over the plugin without ever touchingconfig.ymlor SSHing into the server. New Settings tab — every config value is editable live from the browser: - Process behaviour: auto-restart toggle, restart delay, max restarts, crash window, startup delay - Node.js runtime: version pin and custom executable path - Discord webhook: URL, enable/disable, and individual toggles for each event type (start, stop, crash, log lines) - Dashboard: log history line count and password (changing the password immediately invalidates all active sessions) New Bots tab — full bot instance management with TunnelMC-style modal dialogs: - Add a new bot (name, entry file, env vars) — starts automatically - Edit an existing bot — stops it, applies the new config, and restarts - Delete a bot — stops it and removes it fromconfig.yml- All changes persist to disk immediately Download logs — a new button on the sidebar downloads the currentbot.logas a file attachment. SSE push-based streaming — log lines are now pushed directly to the browser the moment they are written, rather than polled. This eliminates latency and CPU overhead. Log counter fix — the line counter now reflects what is actually visible in the DOM (capped at 3000) rather than a stale incrementing integer that could exceed the cap. `switchBot()` race fixed — historical logs are now fully loaded before the SSE stream connects, preventing out-of-order or duplicate lines when switching bots.Bug Fixes
- `failedAttempts` map unbounded growth — the brute-force attempt tracker is now swept hourly alongside the session map, preventing unbounded accumulation of stale IP entries. - JSON injection in status/bots API — bot names and entry file paths are now serialised with Gson instead of string concatenation, preventing malformed JSON if a name contains quotes or backslashes. - `setsid` path now covers Alpine Linux / Pterodactyl — checks/usr/bin/setsid,/bin/setsid, and/usr/local/bin/setsidbefore falling back gracefully. Previously only/usr/bin/setsidwas checked, so process group killing silently did nothing on Alpine-based containers. - `FileReader` platform charset —/proc/<pid>/statusis now read with explicit UTF-8 instead of the JVM default charset. - `FileUtils.deleteDir()` now returns `boolean` — failures are logged per-file instead of silently swallowed. - GET endpoints reject non-GET requests —/api/status,/api/bots,/api/logs/recent,/api/logs/streamnow return 405 for wrong-method requests. - Log timestamps include the date — entries now useMM-dd HH:mm:ssformat so logs spanning midnight are unambiguous. - Content-Security-Policy header added to all dashboard responses. - Gradle performance flags —parallel,caching, andG1GCJVM args added togradle.propertiesfor faster local builds. - Fixed state race condition —BotStateis now managed viaAtomicReferenceinstead of a plainvolatilefield, eliminating a class of bugs where background threads could set the state back toRUNNINGafter a stop was already requested. - Fixed child process orphaning on stop — NodeRunner now launches the bot withsetsidon Linux/macOS and sendskill -TERM -<pgid>on stop, matching PyRunner's behaviour. Previously, any subprocesses spawned by the bot (workers, shell scripts, etc.) were left running after the parent was killed. - Fixed `extractTar` double-reading the archive — The extracted directory is now located by diffing the folder listing before and after extraction. The old approach rantar tfon the archive file a second time, which could fail if the archive had already been deleted. - Fixed `restart()` busy-wait — The polling loop (Thread.sleep(200)in awhileloop) has been replaced with aCountDownLatch, so restarts are both more reliable and use zero CPU while waiting. - Fixed `HttpClient` created per request — A single sharedHttpClientinstance is now reused across all Node.js version and download requests, rather than constructing a new one (with its own thread pools and connection pool) for each call. - Fixed config values not validated — All numeric config getters (restart-delay-seconds,max-restarts,crash-window-seconds,startup-delay-seconds) now clamp to valid ranges, preventingThread.sleep(-N)exceptions from negative values. - Fixed log-history cap not enforced —getDashboardLogHistory()is now capped at 500 (matchingLogManager's ring buffer size), so requesting more lines than the buffer holds no longer silently returns a truncated result without warning. - Fixed static `instance` not `volatile` — The singleton is now declaredvolatilefor safe cross-thread visibility, and is nulled out inonDisable()to prevent classloader leaks on plugin reload. - Fixed Discord embed JSON escaping — Webhook embeds now use Gson'stoJson()for string serialisation instead of a manualreplace()chain, correctly escaping all Unicode control characters (\u0000–\u001F) that could cause Discord to reject payloads. - Fixed malformed bot entries crashing plugin load — Each entry in thebots:YAML list is now parsed inside a try-catch; a bad entry logs a warning and is skipped rather than throwing aClassCastExceptionthat aborts the whole enable sequence. - Added default password warning — A prominent console warning is now printed at startup if the dashboard is enabled and the password is still set to"changeme". - Improved `getBotDir()` fallback — Whenentry-filehas no directory component (e.g.index.jsinstead ofbot/index.js), the plugin now derives the directory from the actual file path and logs a descriptive warning, rather than silently assuming"bot". - Fixed dashboard SSE exhausting the HTTP thread pool — The server now uses Java 21 virtual threads (newVirtualThreadPerTaskExecutor) instead of a fixed 8-thread pool. Previously, 8+ simultaneous SSE log-stream connections (e.g. multiple open tabs or reconnect storms) would fill the pool and cause all other API requests — status polls, button clicks — to hang indefinitely. - Fixed `DashboardServer.jsonString()` unsafe escaping — Dashboard log API responses now useGSON.toJson()instead of a manualreplace()chain. Log lines containing Unicode control characters (\u0000–\u001F) could previously produce malformed JSON that failed to parse in the browser. - Fixed memory cache non-atomic reads — The three separatevolatilefields (cachedMemKb,cachedMemPid,cachedMemTime) have been replaced with a singlevolatile MemCacherecord, swapped atomically. A thread could previously observe a new timestamp alongside stale memory values from the previous cycle. - Fixed SSE handler busy-polling — Thewhile (!heartbeatFuture.isDone()) Thread.sleep(1000)loop is replaced withheartbeatFuture.get(), eliminating the 1-second wake-up overhead while waiting for clients to disconnect. - Fixed session map growing without bound — AScheduledExecutorServicenow sweeps expired sessions every hour. Previously, repeated logins without a server restart would accumulate stale entries in the map indefinitely. - Fixed dashboard password using string equality — The login check now usesMessageDigest.isEqual()for constant-time comparison, preventing theoretical timing attacks on the password. - Fixed duplicate `deleteDir()` logic — A new sharedutil/FileUtils.deleteDir()(NIO-based) replaces the identical recursive-delete method that was duplicated acrossNodeBotCommandandDashboardServer. - Fixed `plugin.yml` `api-version` outdated — Updated from'1.13'to'1.21'to match the actual target platform and suppress Paper's de1.2.0NodeRunner 1.2.0Apr 11, 2026 What's new in 1.2.0
Per-Bot Environment Variables
Each bot in thebots:list can now have its ownenv:block that overrides the global environment variables — useful for running multiple bots with different tokens.Multi-Bot Dashboard Selector
When running multiple bots, the dashboard now shows a bot selector dropdown to switch between bots without leaving the page.Dashboard Security Hardening
- CSRF protection — All mutating API endpoints now require anX-NR-Requestheader - Brute-force login protection — Lockout after 5 failed attempts (5 minute cooldown) - Session pruning — Expired sessions are now cleaned up automatically ---Changes
- Dashboard now uses a bounded thread pool (8 threads) instead of an unbounded cached pool - SSE heartbeats use a shared scheduled executor instead of per-connection threads - NodeDownloader is now a shared singleton — multiple bots no longer trigger duplicate downloads - Webhook shutdown is now handled cleanly on plugin disable - Restart now waits for the process to fully stop before starting again (prevents race conditions)Bug Fixes
- Fixed stop command not waiting for the process to actually terminate before reporting "stopped" - Fixed auto-restart thread not being interrupted when manually stopping a bot1.1.0NodeRunner 1.1.0Mar 30, 2026 v1.1.0
- Multiple Bot Support — Run more than one Node.js bot at the same time. Define aNew Features
bots:list inconfig.yml, each with its own name and entry file. Each bot gets its own process, log file (bot-<name>.log), and independent crash recovery. Falls back to single-bot config automatically — no breaking changes. - Node.js Version Pinning — Newnode-versionconfig option (e.g."20","22") lets you pin a specific Node.js major version instead of always downloading the latest LTS. - stdin Passthrough — New input bar at the bottom of the web dashboard lets you send text directly to the bot's stdin without restarting it. - Memory Monitoring — The web dashboard now shows live RSS memory usage of the bot process (Linux only, reads from/proc/<pid>/status). - TunnelMC Integration — If TunnelMC is installed and has an active tunnel on the dashboard port, NodeRunner now prints the public URL to console when the bot starts. Configurable viaintegrations.tunnelmc-suggestioninconfig.yml.
-Changes
/nodebotcommands now accept an optional[name]argument to target a specific bot when running multiple bots - Bot log files are now namedbot-<name>.logper bot when using multi-bot mode; single-bot mode still usesbot.log- Webhook messages now include the bot name in brackets (e.g.Bot [modbot] started) when running multiple bots - Thread names now include the bot name for easier debugging
- Fixed npm install failing on Pterodactyl panels due to PATH not being set correctly — node binary directory is now explicitly injected into PATH before running npmBug Fixes
1.0.0NodeRunner 1.0.0Mar 28, 2026 NodeRunner
Run your Node.js Discord bot directly inside your Minecraft server — no VPS, no extra hosting, no SSH.
NodeRunner is a Paper plugin that manages a Node.js process as a child of your Minecraft server. It handles everything automatically: downloading Node.js, installing dependencies, restarting on crash, live log streaming via a web dashboard, and Discord notifications — all from oneconfig.yml. ---Features
No Node.js on your server? No problem. NodeRunner auto-detects your OS and CPU architecture and downloads the correct Node.js LTS binary on first launch. Works on Linux (x64 & ARM64), macOS, and Windows. Downloaded once (~30MB), cached forever.Zero-Touch Node.js Setup
Got aAutomatic Dependency Installation
package.json? NodeRunner runsnpm installautomatically before launch whenevernode_modulesis missing. No manual intervention needed.
Your bot is monitored constantly. On crash, NodeRunner waits a configurable delay and restarts it. If it crashes too many times within a short window, crash-loop protection halts retries and alerts you — no infinite restart spam.Crash Recovery & Loop Protection
A built-in, password-protected control panel accessible from any browser: - Live log streaming via Server-Sent Events (no page refresh) - Start / Stop / Restart / npm install buttons - Real-time status, uptime, PID, and restart count - Color-coded log output with auto-scroll toggleLive Web Dashboard
Get pinged in Discord when your bot starts, stops, crashes, or when npm install runs — via a standard webhook URL. No bot token required. Every event type is individually toggleable.Discord Webhook Notifications
Full control from the Minecraft console or in-game viaIn-Game Commands
/nodebot. No SSH session needed just to restart your bot.
Fully tested on Pterodactyl panels. UsesPterodactyl Compatible
.tar.gzfor Node.js extraction (noxzdependency), and correctly injects the node binary into the process PATH for npm compatibility inside containers. ---
1. DropInstallation
NodeRunner.jarinto yourplugins/folder 2. Start the server once to generate config files 3. Place your bot code inplugins/NodeRunner/bot/(needsindex.js+package.json) 4. Set your bot token and any other secrets underenv:inconfig.yml5. Restart, or run/nodebot startNodeRunner handles the rest — Node.js download,npm install, and bot launch all happen automatically. ---File Structure
---plugins/ NodeRunner/ config.yml ← All plugin settings bot/ ← Your bot code goes here index.js package.json logs/ bot.log ← Live output (rotates at 5MB, keeps 3 backups) nodejs/ ← Auto-downloaded Node.js binary (don't touch)
Permission:Commands & Permissions
noderunner.admin(default: OP) | Command | Description | | :--- | :--- | |/nodebot start| Start the bot process | |/nodebot stop| Stop the bot process | |/nodebot restart| Restart the bot process | |/nodebot status| Show state, uptime, PID, restart count | |/nodebot install| Force re-runnpm install| |/nodebot reload| Reloadconfig.yml| ---
Access atWeb Dashboard
http://<your-server-ip>:8080(port is configurable). Password protection is enabled by default — change the default password in `config.yml` before going public.Pterodactyl users: The dashboard needs a second allocated port. Ask your host to add one, or use a Cloudflare Tunnel to expose it without one.
---
1. Go to your Discord channel → Edit Channel → Integrations → Webhooks → New Webhook 2. Copy the Webhook URL 3. Paste it intoDiscord Webhook Setup
config.ymlunderdiscord.webhook.url4. Setdiscord.webhook.enabled: true5. Restart or run/nodebot reload---
| | | | :--- | :--- | | Primary Target | Paper 1.21.1 | | Backwards Compatible | Paper / Spigot 1.13+ | | Java | 11+ (21 recommended) | | OS | Linux, macOS, Windows | | Architectures | x64, ARM64 | | Hosting | Self-hosted, Pterodactyl, any VPS |Compatibility
⚠️ Java Version Note: Although the plugin API is compatible with 1.13+, the JAR is compiled with Java 21 which means your server must be running Java 21 or higher regardless of Minecraft version. Older Minecraft versions (1.13–1.16) typically run on Java 8 or 11 and will fail to load this plugin with an
---UnsupportedClassVersionError. In practice, Java 21 + Paper 1.21.x is the recommended and fully tested setup.
[TunnelMC](https://modrinth.com/plugin/tunnelmc) — Expose any server port to the internet via ngrok tunnels. If your host doesn't provide multiple port allocations (common on Pterodactyl shared hosting), TunnelMC pairs perfectly with NodeRunner to make the web dashboard publicly accessible without needing an extra port. ---Related
All Rights Reserved — This plugin and its source code are proprietary. You may not copy, redistribute, modify, or decompile this software without explicit written permission from the author. --- Developed by [Spider](https://github.com/Spider2742)License
Similar projects
Config →VeinMiner
Mine the whole vein on mining a single ore/block. Make the tedious mining experience to something satisfying and fun! FTB Ultimine alternative
99.3MSueta
Simple Voice Chat
A working voice chat in Minecraft!
76.8MSueta
Chunky
Pre-generates chunks, quickly and efficiently
18.4MSueta
VeinMiner Enchantment
Veinminer Addon - Adds vineminer enchantment to enchanting tables. Only tools with the enchantment can veinmine
18MSueta
JourneyMap
Real-time map used for mapping in-game or your browser as you explore.
14.7MSueta
WorldEdit
A Minecraft Map Editor... that runs in-game! With selections, schematics, copy and paste, brushes, and scripting. Use it in creative, or use it temporarily in survival.
11.6MSueta