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

Part 1 - The Discord side

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

    Create the application

    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

    Enable the three privileged intents

    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

    Invite the bot to your server

    Under OAuth2 - 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 82 slash commands appear. Leave it out and the bot joins the server without a single command.

  4. Step 04

    Collect the OAuth credentials for the dashboard

    Under OAuth2 - 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

Part 2 - Choose your storage

Two real backends, one line in .env, no separate edition. Pick by how big the server is, not by how it sounds.

  1. Step 05

    JSON files - nothing to install

    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

    MySQL or MariaDB - for a large server

    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.

Part 3a - Deploy on a clean Debian or Ubuntu server

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

    Install Node.js 22 and git

    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

    Create a user for the bot

    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

    Put the files in place

    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

    Write the configuration

    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; 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

    Install dependencies and start it

    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

    Keep it running with systemd

    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

    Optionally enable the dashboard

    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, and set NEXTAUTH_URL to exactly the address the browser uses, scheme included - a mismatch makes the login cookie vanish. 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_URL=https://bot.your-domain.tld
    NEXTAUTH_SECRET=output_of_openssl_rand_-hex_32
    DASHBOARD_PASSWORD=

Part 3b - Deploy on Pterodactyl

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

  1. Step 14

    Import the egg

    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 the server

    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 files

    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

    Fill in the Startup variables

    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, NEXTAUTH_SECRET - only 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

    Check the startup command

    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 - 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

    Press Start

    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.

Part 4 - First setup inside Discord

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

  1. Step 20

    Turn on the modules you need

    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 21

    Set a staff role and a log channel

    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 22

    Verify the install

    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.

Part 5 - Updating

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

  1. Step 23

    Update to a new release

    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

Terms on this page

Pterodactyl egg Self-hosted Module registry Prefix bridge

Related

Reviewed