How a Forgetful MySQL Config on a Cheap VPS Turned My NestJS App 10× Slower—and I Fixed It in 30 Minutes
Imagine spending hours polishing a new NestJS feature, only to watch your API lag like a snail when you finally push to production. The culprit? A tiny MySQL setting you missed on a $5 VPS. In this story I’ll show you exactly how I tracked down the problem, fixed it in half an hour, and got my app back to lightning‑fast speed.
Why This Matters
If you’re building SaaS tools, internal dashboards, or any data‑driven service, performance is a make‑or‑break factor. Slow queries cost you:
- Lost customers who can’t wait for a response.
- Higher cloud bills because you keep scaling CPU to compensate.
- Developer frustration and wasted time.
And the worst part? The root cause is often a single mis‑configured MySQL variable that hides behind “it works on my laptop.” Let’s dig into the exact steps to catch that bug before it hurts your users.
Step‑by‑Step Tutorial: Find & Fix the MySQL Bottleneck
-
Reproduce the slowdown locally
First, confirm the issue isn’t just network latency. Run a simple benchmark against both your local MySQL instance and the production VPS.
ab -n 1000 -c 50 http://your‑api.com/usersIf the VPS version is 10× slower, you know the problem lies in the database layer.
-
Check MySQL version and default config
Log into the VPS and dump the current settings:
mysql -u root -p -e "SHOW VARIABLES;" > /tmp/mysql_vars.txtLook especially for
innodb_buffer_pool_size,query_cache_type, andmax_connections. -
Spot the missing
innodb_buffer_pool_sizeWarning: On a cheap VPS the default buffer pool is often set to 128 MB or even 64 MB, which is nowhere near enough for a production‑grade table set.
If the value is too low (e.g.,
64M), MySQL will constantly flush pages to disk, causing massive latency spikes. -
Resize the buffer pool safely
Allocate 50‑70 % of your RAM to the buffer pool. For a 2 GB VPS, 1 GB is a good starting point.
# Edit /etc/mysql/my.cnf or /etc/my.cnf [mysqld] innodb_buffer_pool_size=1GAfter saving, restart MySQL:
sudo systemctl restart mysqlVerify the new setting:
mysql -u root -p -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size';" -
Enable the Query Cache (optional but helpful)
If your workload is read‑heavy, turn on the cache:
[mysqld] query_cache_type=ON query_cache_size=64MRestart once more and watch the cache fill up.
-
Test again with the same
abcommandYou should now see response times drop from ~500 ms per request to under 50 ms — a genuine 10× improvement.
Code Example: NestJS Service Using TypeORM
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User)
private readonly userRepo: Repository<User>,
) {}
async findAll(): Promise<User[]> {
// Optimized query – selects only needed columns
return this.userRepo.find({
select: ['id', 'email', 'createdAt'],
order: { createdAt: 'DESC' },
});
}
}
Tip: Pair the larger buffer pool with selective field queries (as shown) to keep the dataset in memory and avoid full‑table scans.
Real‑World Use Case: SaaS Dashboard
My client runs a multi‑tenant analytics dashboard built on NestJS + TypeORM. After moving to a $5 DigitalOcean droplet, the dashboard’s “Load All Users” page went from 2 seconds to 20 seconds. The culprit was the default 128 MB buffer pool. After the quick fix above:
- Page load time: 1.8 seconds
- CPU usage dropped by 45 %
- Monthly VPS cost stayed at $5 — no need to upgrade.
Results / Outcome
In under 30 minutes I turned a 10× slowdown into a speed‑up that felt like a brand‑new server. The key takeaways:
- Never trust default MySQL configs on low‑cost servers.
- Allocate 50‑70 % of RAM to
innodb_buffer_pool_size. - Run a quick benchmark before and after each change.
Bonus Tips for Ongoing Performance
- Enable slow query logging. Add
slow_query_log=ONandlong_query_time=0.5to catch rogue queries. - Use connection pooling. NestJS’s
TypeOrmModule.forRoot({extra: {connectionLimit: 20}})prevents socket thrashing. - Monitor with Percona Monitoring. Visual graphs make it easy to spot memory pressure.
- Consider a managed DB for growth. When you outgrow a cheap VPS, a managed Aurora or CloudSQL instance saves time and headaches.
Monetization (Optional)
If you help clients migrate from cheap VPS setups to scalable, high‑performance stacks, you can charge a one‑time audit fee ($150–$300) plus a monthly retainer for monitoring. The ROI for a $5 server turned $50‑plus per month is a compelling sales pitch.
Performance isn’t a mysterious art; it’s a series of small, verifiable tweaks. The next time your NestJS API feels sluggish, remember the buffer pool—and you’ll save time, money, and a lot of angry users.
No comments:
Post a Comment