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
| 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.
-
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.
-
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.
-
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.
-
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.
-
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=./dataThe 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.
-
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.
-
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 --versionnode --version should print v22.x. Anything from v18 up runs the bot, but 18 and 20 no longer get upstream security fixes.
-
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 -
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 -
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 -
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 startWatch 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.
-
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 -
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.
-
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.
-
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.
-
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.
-
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.
-
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 -
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.
-
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.
-
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.
-
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.
-
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
VorqulBOT
What the bot is, module by module.
AutoMod
The 15 filters and what they cost you if switched on carelessly.
Direct answers
Price, requirements and limits in one place.
Reviewed