Table of contents
- Installation & Deployment
- Prerequisites (all paths)
- Step 1 — Create the Discord Bot (same for every path)
- Path A — Pelican Panel (Wings)
- Path B — Docker (standalone, no panel)
- Path C — Native Installation (plain Node, e.g. systemd/pm2)
- Resilience & Crash Behavior
- First Start (all paths)
- Per-Guild Slash Commands
- Invite the Bot
Installation & Deployment
GrumpyCore is a plain Node.js/TypeScript process — Pelican/Wings is how we run our own production instance, but it's not a requirement. This page covers three paths: Pelican Panel (Wings), standalone Docker, and native installation (plain Node, e.g. via systemd/pm2).
Prerequisites (all paths)
- Node.js 22+ (
"engines": {"node": ">=22.0.0"}inpackage.json) - Discord Bot Token (Developer Portal)
- Bot intents enabled: Presence Intent, Server Members Intent, Message Content Intent
- Database — default SQLite (auto-created, no setup needed). Optional: MySQL / MariaDB / PostgreSQL
Step 1 — Create the Discord Bot (same for every path)
- Go to discord.com/developers/applications → New Application
- Click Bot in the left menu → Add Bot
- Disable Public Bot
- Enable all three intents (Presence, Server Members, Message Content)
- Copy the Token
Path A — Pelican Panel (Wings)
This is how our own production instance runs. Recommended if you already operate a Pelican Panel for game/bot servers — you get console access, automatic restart on crash, resource limits, and SFTP file access for free.
Egg: pelican-egg.json (included in the GrumpyCore repo)
Docker Image: ghcr.io/parkervcp/yolks:nodejs_24
Startup: npm ci --omit=dev (only if package-lock.json is newer than node_modules)
&& npm run db:push
&& node build/index.js
RAM: 512 MB recommended
Environment variables in the Pelican Panel (all optional):
| Variable | Default | Description |
|---|---|---|
DATABASE_PROVIDER |
sqlite |
sqlite / mysql / mariadb / postgres |
DATABASE_URL |
file:./grumpy.db |
Connection URL — falls back to SQLite, no .env needed |
A
.envfile is not required. Token, guildId, channels, and roles are all configured inconfigs/config.yml.
Upload files (via SFTP or the panel's file manager):
build/ ← compiled bot code (run npm run build locally, then upload)
index.js ← root entry point (loads build/index.js)
package.json
package-lock.json
prisma/ ← database schema
scripts/ ← prisma-setup.js
configs/andgrumpy.dbare created automatically on first start.
Restart after a crash: Wings' crash handler detects a terminated process automatically and restarts it (no fixed schedule, purely event-driven). Since July 2026 the bot also has its own built-in self-recovery for transient Discord gateway errors — see Resilience & Crash Behavior below.
Path B — Docker (standalone, no panel)
There's no official Dockerfile in the repo — GrumpyCore is deliberately a plain Node process with no Docker requirement. If you want to containerize it anyway, a generic Node image works:
FROM node:22-slim
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
COPY . .
RUN npm run build
VOLUME ["/app/configs", "/app/database.sqlite", "/app/logs"]
CMD ["node", "build/index.js"]
docker build -t grumpycore .
docker run -d \
--name grumpycore \
--restart unless-stopped \
-v grumpycore-configs:/app/configs \
-v grumpycore-data:/app \
grumpycore
Important points:
--restart unless-stopped(oralways) is the equivalent of Wings' crash handler here — without it the container stays dead after a fatal error.- Volumes for
configs/(settings) and the SQLite file/database.sqliteare mandatory, otherwise config and data disappear on everydocker run. - Token/guildId/channels/roles belong in
configs/config.yml, not Docker environment variables — exceptDATABASE_PROVIDER/DATABASE_URLwhen using MySQL/MariaDB/PostgreSQL (see.env.examplein the repo). - Across multiple Docker hosts/Compose setups: SQLite isn't network-capable — use MySQL/PostgreSQL via
DATABASE_URLfor distributed setups.
Path C — Native Installation (plain Node, e.g. systemd/pm2)
For your own servers without a panel and without Docker — running Node directly.
git clone <repo-url>
cd GrumpyCore
# 1. Install dependencies
npm install
# 2. Build + DB schema
npm run build
npm run db:push
# 3. First start — creates configs/config.yml
npm start
# Fill in token, guildId, channels, roles → restart the bot
Without a supervisor (node build/index.js run directly in a terminal/nohup): since July 2026 the bot survives transient Discord gateway errors on its own (see below), but on a genuine fatal error (e.g. database connection completely gone, a real bug) it still terminates via process.exit(1) — and then stays dead unless something restarts it. For continuous operation, use a supervisor:
With systemd (/etc/systemd/system/grumpycore.service):
[Unit]
Description=GrumpyCore Discord Bot
After=network.target
[Service]
Type=simple
WorkingDirectory=/opt/grumpycore
ExecStart=/usr/bin/node build/index.js
Restart=on-failure
RestartSec=5
User=grumpycore
[Install]
WantedBy=multi-user.target
sudo systemctl enable --now grumpycore
sudo journalctl -u grumpycore -f # live logs
With pm2:
npm install -g pm2
pm2 start build/index.js --name grumpycore
pm2 save
pm2 startup # sets up autostart on boot
Console commands (stop / exit / quit / help) only work in a real TTY session — under systemd/pm2 (no TTY) they're automatically disabled; stopping then goes through systemctl stop / pm2 stop (sends SIGTERM → graceful shutdown, see below).
Resilience & Crash Behavior
Regardless of deployment path, since July 2026:
Transient gateway 5xx errors (e.g. Cloudflare 521) are no longer a crash reason. When Discord's gateway briefly hiccups behind Cloudflare, the WebSocket library throws an Unexpected server response: 5xx error before discord.js can handle it normally. The bot now recognizes this pattern itself and:
- logs it and alerts (crash webhook), but does not exit immediately
- attempts to reconnect on its own (
client.destroy()+client.login()), with backoff (5s → 15s → 30s → 60s → 120s) - only exits (
process.exit(1)) if all attempts fail after ~4 minutes
That means: even without Wings/pm2/systemd, the bot rides out this kind of hiccup on its own. For every other fatal error (real bugs, DB gone, etc.), the process still terminates, and a supervisor (Wings' crash handler, --restart unless-stopped, systemd Restart=on-failure, pm2) is needed to bring it back — without one of those, the bot stays dead after a genuine crash.
Graceful shutdown (all paths):
- SIGTERM (Pelican "Stop" button,
docker stop,systemctl stop,pm2 stop) → clean shutdown - Ctrl-C in terminal → SIGINT → same flow
- 10 s timeout fallback, a second signal forces immediate exit
- Step-by-step shutdown logging in the console
First Start (all paths)
- Start the bot — Prisma generates the schema and migrates the database
- On first start the bot shows a setup status table:
═══════════════════════════════════════════════════════
GrumpyCore — Setup Status
═══════════════════════════════════════════════════════
✅ Connected to: Minetechworld.de (13 members)
Modules: welcome 🟢 mod 🟢 tickets 🟢 news 🟢
Channels: ✅ welcome ✅ verify ✅ mod-log ✅ alert
✅ staff ✅ ticket-log ✅ news
Roles: ✅ unverified ✅ member ✅ support
Panels deployed:
✅ verify-panel msg 1234...
✅ support msg 5678...
═══════════════════════════════════════════════════════
- Fill in any missing IDs in
configs/config.yml→ Configuration-EN - Restart the bot
- The verify panel and ticket panels are automatically posted to the configured channels — no
/welcome setup-verifyor/tickets setup-panelneeded
If a saved panel is deleted, the bot detects this on the next start and re-posts it. Manual re-deploy via
/welcome setup-verify//tickets setup-panelis still available.
Upgrade note: When updating to a newer bot version you do not need to manually edit
configs/config.yml. The Config Auto-Heal adds missing addon toggles and channel fields on startup and removes deprecated sections (e.g.message-ids:). Existing values and comments are preserved — see Configuration-EN.
Per-Guild Slash Commands
All commands are registered only for the guild configured in guildId — updates are instant, no 1-hour cache. On the very first start there is a one-time migration step (~20 s) that removes any old globally registered commands.
Invite the Bot
https://discord.com/api/oauth2/authorize?client_id=BOT_ID&permissions=8&scope=applications.commands%20bot
BOT_ID = Application ID from the Developer Portal
GrumpyCore Wiki
⚙️ Setup
🔧 Core-Module
🆕 Neue Module
🔔 Benachrichtigungen & Utility
💬 Commands
👥 Staff
GrumpyCore Wiki (English)
⚙️ Setup
🔧 Core Modules
🆕 New Modules
🔔 Notifications & Utility
💬 Commands
👥 Staff