Skip to content
VORQUL
Documentation / Installation

Install VorqulBOT, the self-hosted Discord bot

Two deployment paths, one configuration. The Discord side is the same either way, and it is the part people get wrong, so it comes first.

Installing VorqulBOT takes about 10 to 15 minutes. Create an application in the Discord Developer Portal, enable the presence, server members and message content intents, and copy the token and the application ID. Then either install Node.js 22 LTS on a clean Debian or Ubuntu server, copy .env.example to .env, run npm install and npm start and keep it alive with systemd, or import the included egg into Pterodactyl, upload the files and fill in the Startup variables. Storage is JSON files by default or MySQL/MariaDB, chosen with one line in .env.

Time
About 10 to 15 minutes
Runtime
Node.js 18+, 22 LTS advised
Paths
Debian or Ubuntu server, Pterodactyl
Storage
JSON files or MySQL/MariaDB

Requirements

Minimum requirements before installing
RAM 512 MB minimum, 1 GB advised, 2 GB if you run the dashboard
CPU 1 core minimum, 2 advised
Disk 500 MB minimum, 1 GB advised
Node.js 18 minimum, 22 LTS advised (18 and 20 are past upstream end of life)
npm Ships with Node.js, nothing separate to install
Database Optional, JSON files need nothing installed
Requirements for Install VorqulBOT: RAM 512 MB minimum, 1 GB advised, 2 GB if you run the dashboard, CPU 1 core minimum, 2 advised, Disk 500 MB minimum, 1 GB advised, Node.js 18 minimum, 22 LTS advised (18 and 20 are past upstream end of life), npm Ships with Node.js, nothing separate to install, Database Optional, JSON files need nothing installed.

This half is identical for every deployment path, and skipping the intents is the single most common reason a freshly installed bot does nothing.

  1. Step 01

    In the Discord Developer Portal, create a new application, then add a bot to it and reset the token to reveal it.

    The token is shown once. Copy it somewhere safe and never commit it, paste it into a support channel or put it in a screenshot. If it leaks, reset it. Discord invalidates the old one immediately.

    Also copy the Application ID from General Information. The bot needs it as DISCORD_CLIENT_ID to register its slash commands, so it is not optional even if you never open the dashboard.

  2. Step 02

    Under Bot, in Privileged Gateway Intents, switch on Presence Intent, Server Members Intent and Message Content Intent.

    Without Message Content the bot cannot see message text, so AutoMod, leveling, prefix commands and sticky messages all silently do nothing. Without Server Members it cannot react to joins, which takes out welcome cards, join roles, invite tracking and anti-raid.

    If the bot connects but ignores everything, come back to this step first.

  3. Step 03

    Under OAuth2, in URL Generator, select the bot and applications.commands scopes, choose the permissions, then open the generated URL and authorise it on your server.

    The applications.commands scope is what lets the 83 slash commands appear. Leave it out and the bot joins the server without a single command.

  4. Step 04

    Under OAuth2, in General, reset and copy the Client Secret, then add your callback URL to Redirects.

    Only needed if you want the web dashboard. The bot itself runs without the secret and without any redirect.

    Redirect URLs
    http://localhost:3000/api/auth/callback/discord
    https://your-domain.tld/api/auth/callback/discord

Two real backends, one line in .env, no separate edition. Pick by how big the server is and ignore how the names sound.

  1. Step 05

    One file per table under data/json. This is the default, it needs no service and no setup, and it is what most self-hosted instances run on.

    .env
    DATABASE_TYPE=json
    DATA_PATH=./data

    The bot appends /json to DATA_PATH by itself, so DATA_PATH=./data ends up in ./data/json. Do not write ./data/json here or the files land one level too deep.

  2. Step 06

    Install the server, create a database and a user with utf8mb4, then point the bot at it. Worth it once the server is big or you want the data alongside other services.

    Then set DATABASE_TYPE=mysql together with DB_HOST, DB_PORT, DB_USER, DB_PASSWORD and DB_NAME in .env.

    DATABASE_TYPE=sqlite is not a third option. This version has no SQLite driver, so the value logs a warning and quietly uses the JSON files instead. Your data is still saved, but write json so the configuration says what actually happens.

    MariaDB on Debian
    sudo apt install -y mariadb-server
    sudo mysql
    
    CREATE DATABASE vorqul CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
    CREATE USER 'vorqul'@'localhost' IDENTIFIED BY 'your_password';
    GRANT ALL PRIVILEGES ON vorqul.* TO 'vorqul'@'localhost';
    FLUSH PRIVILEGES;

    utf8mb4 is not optional. Anything less mangles emoji and diacritics in stored messages.

The path to take if you have your own machine. Everything below runs as an ordinary user with sudo, and the bot itself runs as a dedicated user with no sudo of its own.

  1. Step 07

    Update the system, install the basics, add the NodeSource repository for Node 22 and install it. npm comes with it.

    bash
    sudo apt update && sudo apt upgrade -y
    sudo apt install -y curl ca-certificates git unzip
    curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
    sudo apt install -y nodejs
    node --version
    npm --version

    node --version should print v22.x. Anything from v18 up runs the bot, but 18 and 20 no longer get upstream security fixes.

  2. Step 08

    A separate user keeps the token and the data out of reach of everything else on the machine.

    The second command opens a shell as that user. Every command in the next steps runs there, until the systemd step.

    bash
    sudo adduser --disabled-password --gecos "" --home /opt/vorqul vorqul
    sudo -iu vorqul
  3. Step 09

    Clone the repository into a directory of its own, or unpack a release archive there. package.json has to sit directly in the working directory.

    Prefer a release archive? Upload it, run unzip, and move the contents up so that package.json is not one level down.

    bash
    git clone https://github.com/Yamiru/Vorqul-DS-BOT.git app
    cd app
  4. Step 10

    Copy .env.example to .env and fill in the token, the application ID and the storage type. The file holds secrets, so restrict who can read it.

    • DISCORD_TOKEN: required, from the Developer Portal
    • DISCORD_CLIENT_ID: required, the Application ID, and without it no slash command is registered
    • DATABASE_TYPE: json or mysql (see Part 2)
    • BOT_OWNER_ID: your Discord user ID, unlocks the owner-only commands
    • DASHBOARD_ENABLED: leave false unless you want the dashboard (see below)
    bash
    cp .env.example .env
    chmod 600 .env
    nano .env
  5. Step 11

    npm install pulls the dependencies, npm start checks the token, compiles the TypeScript and runs the bot in the foreground so you can watch the first boot.

    The repository ships an .npmrc with legacy-peer-deps, which stops npm refusing to install because of peer dependency conflicts in the development tools. Do not delete it.

    The first start compiles the code and takes a minute or two. Later starts skip the compile until the source changes. Stop it with Ctrl+C once you see it online and the slash commands registered.

    bash
    npm install
    npm start

    Watch this first run. Errors about the token, the application ID or the intents show up here and nowhere else. Do not install with --omit=dev or NODE_ENV=production: the compiler is a development dependency and the build would fail.

  6. Step 12

    A service restarts the process when it dies and brings it back after a reboot. This is the step people skip and then wonder why the bot went offline overnight.

    Do not add Environment=NODE_ENV=production to the unit. The launcher runs npm install itself and would then skip the development dependencies it needs to compile. It sets production mode for the dashboard on its own.

    The launcher also writes its own rotated log to logs/current.log inside the bot directory, next to the journal.

    Prefer PM2? sudo npm install -g pm2, then pm2 start start.js --name vorqul, pm2 startup and pm2 save do the same job.

    bash (as your normal user, not vorqul)
    sudo tee /etc/systemd/system/vorqul.service >/dev/null <<'EOF'
    [Unit]
    Description=Vorqul DS BOT
    After=network-online.target
    Wants=network-online.target
    
    [Service]
    Type=simple
    User=vorqul
    WorkingDirectory=/opt/vorqul/app
    ExecStart=/usr/bin/node start.js
    Restart=on-failure
    RestartSec=10
    
    [Install]
    WantedBy=multi-user.target
    EOF
    
    sudo systemctl daemon-reload
    sudo systemctl enable --now vorqul
    sudo systemctl status vorqul
    journalctl -u vorqul -f
  7. Step 13

    The dashboard is not a second thing to install. Switch it on in .env and the same launcher builds it and starts it as a second process on port 3000.

    Restart the service and give the first boot a few extra minutes, because the dashboard is built then. That build wants around 2 GB of RAM.

    Do not expose port 3000 directly. Put nginx in front of it with a certificate. Leave NEXTAUTH_URL empty and the address is detected automatically. If NEXTAUTH_SECRET is empty the launcher generates one and keeps it in data/secrets, so it survives restarts.

    Set ALLOWED_EMAILS to the e-mail of your Discord account, otherwise anyone who can sign in with Discord can reach the dashboard.

    .env
    DASHBOARD_ENABLED=true
    DASHBOARD_PORT=3000
    DISCORD_CLIENT_SECRET=your_client_secret
    NEXTAUTH_SECRET=output_of_openssl_rand_-hex_32
    DASHBOARD_PASSWORD=

The path to take if you already run a game hosting panel. No shell needed at any point.

  1. Step 14

    In the admin area, open Nests, choose Import Egg and upload egg-vorqul-d-s-bot.json from the project.

    The egg sets the startup command to npm start and creates the Startup variables for you, including the two required ones: DISCORD_TOKEN and DISCORD_CLIENT_ID. It offers Node.js 20 and Node.js 18 images. Both work, but both are past upstream end of life, so if your panel provides ghcr.io/pterodactyl/yolks:nodejs_22, add it under the egg Docker Images and select it for the server.

  2. Step 15

    Create a new server on that egg with 1 GB of RAM (2 GB if you will run the dashboard), 1 GB of disk and no special CPU limit.

    A network allocation only matters for the dashboard. The bot itself connects out to Discord and listens on nothing.

  3. Step 16

    Upload the release archive with the File Manager or over SFTP, extract it, and move the contents so that package.json sits in the container root.

    Expected layout
    /home/container/
      bot.py
      start.js
      package.json
      package-lock.json
      .npmrc
      tsconfig.bot.json
      src/
      plugins/

    If package.json ended up inside a subfolder, the launcher will not find the source. Move everything up one level.

  4. Step 17

    The token and the storage type go in as environment variables rather than in a .env file.

    • DISCORD_TOKEN: your bot token
    • DISCORD_CLIENT_ID: the Application ID, required
    • DATABASE_TYPE: json (the default) or mysql
    • BOT_OWNER_ID: your Discord user ID, recommended
    • DASHBOARD_ENABLED, DASHBOARD_PORT, DISCORD_CLIENT_SECRET, NEXTAUTH_URL and NEXTAUTH_SECRET are only needed for the dashboard

    Using the Pterodactyl database host: set DB_HOST to the host the panel shows on the Databases tab, and use the prefixed user and database names it generated. For the dashboard, leave DASHBOARD_PORT empty to reuse the allocated port, and make NEXTAUTH_URL http or https exactly as it is really reachable.

  5. Step 18

    npm start on the Node.js egg. python3 bot.py exists as an alternative launcher, but it only works in a container that has Node.js and npm installed as well, which a plain Python image does not.

    The Python launcher starts the bot only, without the dashboard. Use the Node.js egg unless you have a reason not to.

    Startup command
    npm start
  6. Step 19

    These run over SSH on the machine that hosts Pterodactyl, not inside the bot. If the panel acts strange, restart pteroq and nginx first. If servers will not start, restart wings.

    bash
    sudo systemctl restart pteroq
    sudo systemctl restart nginx
    sudo systemctl restart wings
    sudo systemctl restart mariadb
    sudo systemctl restart redis-server
    sudo systemctl status wings
    sudo journalctl -u wings -f
  7. Step 20

    The first boot installs dependencies and compiles the code by itself, which takes one to three minutes. After that the bot comes online.

    If it exits immediately, read the console output. A missing token or application ID fails here with a clear message. The console also shows if the compile fell back to the slower tsx runtime.

The bot is online. Three settings decide whether it is useful or annoying.

  1. Step 21

    Nine of the 44 modules are on out of the box: moderation, leveling, afk, polls, economy, games, giveaways, reminders and prefix commands. Everything else starts off, on purpose.

    Enabling AutoMod on a live server before you have configured it is how you punish people for behaviour that was fine yesterday. Turn filters on one at a time.

  2. Step 22

    Do this before anything else. Without a log channel, moderation actions happen with no visible record, and the first dispute is unanswerable.

    Every automated and manual action writes a numbered case regardless, but a log channel is what makes it visible while it is happening.

  3. Step 23

    Run /info ping and /help. If the bot answers and the commands are listed, the token, the intents and the command registration are all working.

    Missing slash commands mean the applications.commands scope was left out of the invite, or DISCORD_CLIENT_ID is empty or wrong. Discord can take a few minutes to show newly registered commands.

Five minutes here decide whether strangers can use your bot.

  1. Step 24

    In the Developer Portal open your application, go to Bot and switch off PUBLIC BOT.

    With it on, anyone who knows your client ID can add the bot to their server. This switch is the only real protection against that.

  2. Step 25

    Set ALLOWED_EMAILS or ALLOWED_IDS in .env. Only those accounts, plus people who manage a server the bot is already on, can sign in. Everyone else does not see the Add bot button.

    DASHBOARD_PASSWORD adds one more gate in front of the sign-in. Generate NEXTAUTH_SECRET once with openssl rand -hex 32 and keep it, because changing it signs everybody out.

    Set BOT_OWNER_ID to your own Discord ID. Owner commands and plugin management belong to that ID only, and several IDs are separated by commas.

  3. Step 26

    Never send it to anyone and never upload .env to GitHub (it is in .gitignore). If it leaks, press Reset Token in the Developer Portal and paste the new one.

Every variable, what it does and whether you need it.

  1. Step 27

    The first two are required. Everything else has a sensible default.

    • DISCORD_TOKEN (required): bot token, Developer Portal, Bot, Reset Token
    • DISCORD_CLIENT_ID (required): Application ID, OAuth2, General
    • DISCORD_CLIENT_SECRET (dashboard only): OAuth2, Reset Secret
    • BOT_OWNER_ID (recommended): your Discord ID
    • DATABASE_TYPE (required): json, which needs nothing installed, or mysql
    • DB_HOST, DB_PORT, DB_USER, DB_PASSWORD, DB_NAME (mysql only): MySQL connection details
    • DATA_PATH: data folder, leave at ./data
    • DASHBOARD_ENABLED: true switches the dashboard on
    • DASHBOARD_PORT: dashboard port, leave empty on Pterodactyl
    • NEXTAUTH_URL: leave empty, the address is detected automatically
    • NEXTAUTH_SECRET (dashboard): random string that signs the login
    • DASHBOARD_PASSWORD: extra password in front of the dashboard
    • ALLOWED_EMAILS, ALLOWED_IDS (recommended): who may sign in, comma separated
    • BOT_PREFIX: prefix for text commands, ! by default
    • BOT_LANGUAGE: en, sk, cs, de, pl, ru, es, ja or tr
    • TWITCH_CLIENT_ID, TWITCH_CLIENT_SECRET: for Twitch notifications
    • DISABLED_COMMANDS: commands to hide from the / menu, because Discord allows 100
    • LOG_TO_FILE: true writes logs to data/logs

    DATABASE_TYPE only covers data such as economy, warnings and tickets. Server settings (modules, AutoMod, welcome) always live in data/json/guild_settings.json. Switching from json to mysql does not move old data by itself.

Six steps, and the third one is where most people get stuck.

  1. Step 28

    DASHBOARD_ENABLED=true, DISCORD_CLIENT_ID, DISCORD_CLIENT_SECRET and NEXTAUTH_SECRET.

  2. Step 29

    In the Developer Portal, OAuth2, General, Redirects, add every address you sign in from, domain and IP:port alike.

    Redirect URLs
    http://YOUR-IP:PORT/api/auth/callback/discord
    https://your-domain.tld/api/auth/callback/discord
  3. Step 30

    Restart the bot, open http://YOUR-IP:PORT (the port is DASHBOARD_PORT, or the port allocated to the server on Pterodactyl), press Sign in with Discord, approve access and pick your server.

    If you see an authentication processing error, check DISCORD_CLIENT_ID and DISCORD_CLIENT_SECRET, or clear the cookies for the site.

A short tour so you know what to look for.

  1. Step 31

    Everything below works per server, from one login.

    • Server overview, Server Health Score and analytics for messages, joins and leaves
    • 44 modules to switch on and off: moderation, AutoMod, anti-nuke, anti-raid, levels, economy, tickets, welcome, reaction roles, giveaways and more
    • Settings in tabs, plus limiting modules to chosen channels
    • Embed builder and a library of saved messages
    • Backup and restore in three modes: complete, server only, bot settings only
    • Plugins: install from a ZIP, enable, disable and configure
    • Role Manager: a chosen role may manage the server in the dashboard, but not plugins
    • Bot version with a notice when a newer one is out
    • 9 languages, and refresh and clear-cache buttons in every tab
  2. Step 32

    Five screens, in the order you meet them.

    • Gate: an optional password screen, switched on with DASHBOARD_PASSWORD
    • Login: sign in with Discord
    • Server picker: lists the servers you manage where the bot is present
    • Server home: bot status with online state, ping, uptime, memory and CPU, server stats and a notice when an update is out
    • Settings: one page with tabs, listed in the next step
  3. Step 33

    Each tab groups related modules, so a feature is never more than one click from its switch.

    • General: language, prefix and manager role
    • Channels and Channel scopes: restrict where the bot works
    • Modules: turn each feature on or off
    • Moderation and security: AutoMod, Warnings, Appeals, Verification and Logging
    • Members: Welcome and Goodbye, Reaction roles and Birthday
    • Engagement: Leveling, Starboard, Suggestions, Reputation, Counting, Confessions and Giveaways
    • Economy and games: Economy and Games
    • Content: Tickets, Applications, Autoresponse, Sticky messages, Tags, Feeds (RSS, YouTube, Twitch, Reddit), Socials and Reminders
    • Tools: Temp channels, Crypto, Backup and Restore, Webhooks, Embed builder, Links and Help
    • Admin: Analytics, Audit log, API keys, Plugins (upload a ZIP, owner only) and Premium
  4. Step 34

    The slash commands, and the same commands with the ! prefix, grouped by area.

    • Moderation: ban, tempban, softban, unban, kick, mute, unmute, timeout, voicemute, warn, notes, case, jail, shadowmute, clear, cleanup, lock, lockdown, slowmode, nick, role, timedrole, filter, altcheck and permcheck
    • Security: AutoMod, anti-nuke, anti-raid lockdown, verification, appeals, message and server logs, role snapshots and Persist, which restores roles and nickname when a member rejoins
    • Members: welcome and goodbye messages, join roles, reaction roles, role menus, invite tracking, name history and referrals
    • Engagement: XP and levels with level roles, rank and leaderboards for messages and voice, starboard, suggestions, reputation, a counting channel, birthdays, confessions, AFK, polls, streaks, quests and giveaways
    • Economy: balance, daily, work, pay, bank, shop, market, lootbox, spin and heist
    • Games: 8ball, coinflip, dice, rock-paper-scissors, tictactoe, connect4, trivia, matchmaking and game stats
    • Content tools: tickets, applications, autoresponses, sticky messages, tags, feeds and socials, reminders, a digest, and commands that answer what did I miss and why was I banned
    • Utility: temp voice channels, crypto prices, Minecraft status, info, help, links, backup, webhook events, API keys with a read-only v1 stats API, and Premium
    • Plugins: drop-in plugins such as sample_plugin, minecraft_status and fear_greed, each with settings per server

The launcher notices what changed, so an update is short.

  1. Step 35

    Get the new files while keeping .env and the data directory, then restart. The launcher reinstalls dependencies when package.json changes and recompiles when the source changes.

    On Pterodactyl, upload the new files over the old ones without deleting .env, data or plugins, then restart the server. Settings carry over through the schema migrations.

    bash
    sudo -iu vorqul
    cd app
    git pull
    exit
    sudo systemctl restart vorqul

Troubleshooting

The failures that actually happen, and what causes them.

Questions about installing

Summary

Installing VorqulBOT takes about 10 to 15 minutes. Create an application in the Discord Developer Portal, enable the presence, server members and message content intents, and copy the token and the application ID. Then either install Node.js 22 LTS on a clean Debian or Ubuntu server, copy .env.example to .env, run npm install and npm start and keep it alive with systemd, or import the included egg into Pterodactyl, upload the files and fill in the Startup variables. Storage is JSON files by default or MySQL/MariaDB, chosen with one line in .env.

  • Time: About 10 to 15 minutes
  • Runtime: Node.js 18+, 22 LTS advised
  • Paths: Debian or Ubuntu server, Pterodactyl
  • Storage: JSON files or MySQL/MariaDB
Terms on this page

Pterodactyl egg Self-hosted Privileged intents Slash command OAuth2 dashboard sign-in Module registry Prefix bridge

Related

Reviewed