← All posts

Running a Discord music bot for free on Oracle Cloud's Always Free tier

A practical guide to deploying RuneBeats on a permanently-free 1 GB Oracle Cloud VM with Node 22, yt-dlp, and PM2.

Hosted Discord music bots cost money or vanish when the free trial ends. RuneBeats was built to run on infrastructure that’s permanently free — specifically Oracle Cloud’s Always Free tier. Here’s the whole deployment, start to finish.

Why Oracle’s free tier

Oracle gives you an Always Free VM.Standard.E2.1.Micro instance: 1 vCPU, 1 GB RAM, no time limit. That’s modest, but a music bot is mostly I/O — it shells out to yt-dlp and FFmpeg and shuttles bytes to Discord — so it fits comfortably. RuneBeats keeps per-server queues in memory and caps playlists at 50 tracks specifically so a 1 GB box stays happy.

1. Provision the VM

Create the E2.1.Micro instance with an Ubuntu image, add your SSH key, and make sure ingress is open for SSH. The bot makes only outbound connections (to Discord and to media sources), so you don’t need to open any inbound ports beyond SSH — one fewer thing to secure.

2. Install the runtime

# Node.js 22
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejs

# yt-dlp (the primary YouTube extractor — must be on PATH)
sudo curl -L https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp -o /usr/local/bin/yt-dlp
sudo chmod +x /usr/local/bin/yt-dlp

# PM2 to keep the bot alive
sudo npm install -g pm2

Note there’s no apt install ffmpeg here — RuneBeats bundles FFmpeg via the ffmpeg-static npm package, so the system install isn’t needed.

3. Clone, configure, validate

git clone https://github.com/dwarvenstack/runebeats
cd runebeats
npm install
cp .env.example .env
nano .env          # add DISCORD_TOKEN, CLIENT_ID, and optionally SOUNDCLOUD_CLIENT_ID

node setup_environment.js   # sanity-checks Node, yt-dlp/ffmpeg, folders, and .env

That last step is worth running before anything else — it catches the common “wait, is yt-dlp actually on PATH?” class of problem up front instead of at the first /play.

4. Register commands and launch under PM2

npm run deploy                       # register slash commands with Discord
pm2 start src/index.js --name runebeats
pm2 startup                          # generate the boot service
pm2 save                             # persist the process list across reboots

pm2 startup + pm2 save is the combination that makes the bot survive a VM reboot — PM2 re-launches it automatically. Without both, a restart leaves your server silent.

5. Operating it

pm2 logs runebeats      # tail logs (LOG_LEVEL in .env controls verbosity)
pm2 restart runebeats   # after pulling changes
pm2 monit               # live CPU / memory

When you ship an update:

git pull && npm install && pm2 restart runebeats

A note on the 1 GB ceiling

The two things that eat memory on a music bot are concurrent FFmpeg processes and unbounded queues. RuneBeats keeps each guild’s queue isolated and in memory, and caps playlist imports at 50 tracks — both deliberate choices so the free tier doesn’t OOM. If you run a very busy bot across many servers at once, that’s when you’d graduate to a bigger box. For a personal or small-community bot, the free tier is genuinely enough.

That’s the whole thing: a real, always-on Discord music bot for the cost of nothing. Grab RuneBeats on GitHub and the setup lives on the product page.