2 Installation EN
darksoon edited this page 2026-07-11 11:44:07 +02:00

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"} in package.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)

  1. Go to discord.com/developers/applicationsNew Application
  2. Click Bot in the left menu → Add Bot
  3. Disable Public Bot
  4. Enable all three intents (Presence, Server Members, Message Content)
  5. 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 .env file is not required. Token, guildId, channels, and roles are all configured in configs/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/ and grumpy.db are 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 (or always) 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.sqlite are mandatory, otherwise config and data disappear on every docker run.
  • Token/guildId/channels/roles belong in configs/config.yml, not Docker environment variables — except DATABASE_PROVIDER/DATABASE_URL when using MySQL/MariaDB/PostgreSQL (see .env.example in the repo).
  • Across multiple Docker hosts/Compose setups: SQLite isn't network-capable — use MySQL/PostgreSQL via DATABASE_URL for 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:

  1. logs it and alerts (crash webhook), but does not exit immediately
  2. attempts to reconnect on its own (client.destroy() + client.login()), with backoff (5s → 15s → 30s → 60s → 120s)
  3. 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)

  1. Start the bot — Prisma generates the schema and migrates the database
  2. 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...
═══════════════════════════════════════════════════════
  1. Fill in any missing IDs in configs/config.ymlConfiguration-EN
  2. Restart the bot
  3. The verify panel and ticket panels are automatically posted to the configured channels — no /welcome setup-verify or /tickets setup-panel needed

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-panel is 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 guildIdupdates 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