How I run a Discord bot, a website and an AI image scanner on 2 GB of RAM
The whole of VoidBox lives on one $5 server. Here's what fits, and what I had to change to make it fit.
VoidBox is a Discord bot I build alone. It does economy, levels, tickets, moderation and AI, and it's in about 250 servers. It also has a website with a dashboard, a blog and an MCP server so people can run their Discord server from Claude.
All of it runs on one VPS:
- 1 vCPU
- 1.9 GB of RAM, plus 1 GB of swap
- about $4.76 a month
People assume a bot like this needs a cluster. It doesn't. It needs you to know where every megabyte goes.
Where the memory goes
This is a snapshot from today:
| What | Memory |
|---|---|
| The Discord bot | ~845 MB |
| The NSFW image scanner (when loaded) | up to ~620 MB |
| The website + API | ~160 MB |
| Process manager and the rest | ~150 MB |
Add that up and you can see the problem: the bot and the image scanner together already want most of the box. Everything below is about making sure they're never both at full size at the wrong moment.
1. Load the heavy thing only when it's needed
VoidBox scans images for NSFW content on its own hardware, so no images get sent to a third party. That model is the single biggest thing on the server: about a third of all the RAM while it's loaded.
But most servers go long stretches without anyone posting a picture. So the scanner runs as a separate process that starts when an image arrives and shuts itself down after 4 minutes of quiet.
The cost: the first image after a quiet spell waits about 7 seconds while the model loads. The message is already posted, so a moderator would never notice the difference. The benefit: 620 MB goes back to the rest of the system most of the time.
I learned this one from a memory alert, not from planning.
2. Put a time limit on every cache
The Discord library I use keeps the last 200 messages of every channel in memory, and by default it never throws them away. With hundreds of channels across 250 servers, that grows forever.
I need some cached messages: it's how the bot can show what was deleted when someone deletes a message. But almost all deletes and edits happen within minutes. So messages now expire after 6 hours. Anything older gets logged with an honest "content unavailable".
A cache without a limit isn't a cache. It's a slow memory leak.
3. Use SQLite, and skip native dependencies
Both databases are SQLite files: the bot's is about 82 MB and the website's about 9 MB. There's no database server running in the background using memory.
I use node:sqlite, which is built into Node, so there's no native module to compile. That matters on a tiny box, because compiling things is exactly what eats memory and CPU.
4. Deploys can take the server down too
The scariest outage I've had wasn't caused by traffic. It was caused by a deploy.
My deploy script used to reinstall the bot's dependencies every time. On a 2 GB box, a full npm install running next to the live bot froze the whole server.
Now:
- dependencies are only reinstalled when the lockfile actually changed,
- the install runs at the lowest CPU priority,
- and it downloads only a couple of packages at a time.
The live bot and the website keep running while it happens.
5. Cap the expensive features
Voice chat with the AI is the most CPU-hungry thing VoidBox does. With one vCPU, I allow two servers in voice at once. When a bigger server comes along, that's the number I'll raise, not something I'll pretend is unlimited.
6. Swap is a safety net, not a plan
There's 1 GB of swap. Right now about 150 MB of it is in use. It's there so a short spike slows things down instead of killing a process. If swap were full all the time, the answer would be fixing the memory, not adding more swap.
Why I haven't just upgraded
I've looked. Bigger servers from other hosts cost about the same or a bit more. But moving means redoing the encrypted disk, the firewall, the deploy setup and everything else, for no change users would notice. The CPU load sits around 0.0 to 0.1 most of the day and the server has been up for 24 days straight.
When the image scanner and the bot regularly fight for memory, or voice hits its limit, I'll upgrade. Not before.
What I'd tell another solo founder
- Measure before you optimise. I only knew the scanner was a third of the box because I looked.
- Load heavy features on demand and let them exit when idle.
- Every cache gets a lifetime. "Keep forever" is a bug that just hasn't happened yet.
- Watch your deploys, not just your traffic. They spike memory too.
- A small server is a feature. It forces you to know your system. Upgrade when the numbers tell you to, not when you're nervous.
*I'm building VoidBox, one Discord bot that replaces the four or five most servers run. Free to use.*