When a server has plenty of free CPU and RAM but specific requests are still slow, the bottleneck is usually in the application code or database queries, not the infrastructure. This guide covers finding and fixing it.
Step 1 — Confirm It's Not an Infrastructure Issue First
Rule out CPU, memory, and disk I/O bottlenecks before assuming it's application-level — see How to Diagnose a Slow VPS: Complete Performance Checklist.
Step 2 — Measure Where Time Is Actually Spent
curl -w "@-" -o /dev/null -s https://yourdomain.com/slow-endpoint <<'EOF'
DNS lookup: %{time_namelookup}s
Connect: %{time_connect}s
TLS handshake: %{time_appconnect}s
Time to first byte: %{time_starttransfer}s
Total: %{time_total}s
EOF
A large gap between connect time and "time to first byte" points to slow server-side processing, not network issues.
Step 3 — Enable the Database Slow Query Log
For MySQL/MariaDB:
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1
See How to Tune MySQL/MariaDB Performance for a VPS for full setup detail.
Step 4 — Use EXPLAIN to Analyze Slow Queries
EXPLAIN SELECT * FROM orders WHERE customer_id = 123;
Look for type: ALL (full table scan) in the output — a strong signal that an index is missing on the columns used in your WHERE clause.
Step 5 — Add Missing Indexes
CREATE INDEX idx_customer_id ON orders(customer_id);
Be deliberate with indexes — each one speeds up reads but adds overhead to writes; don't index every column reflexively.
Step 6 — Check for N+1 Query Problems
A very common application-level performance issue: fetching a list of items, then issuing a separate database query for each item's related data in a loop, resulting in dozens or hundreds of queries for one page load. Most ORMs offer "eager loading" to fetch related data in a single query instead.
Step 7 — Add Caching for Expensive, Repeatable Operations
See How to Install and Secure Redis on Ubuntu & Debian for caching frequently-accessed, expensive-to-compute data instead of recalculating it on every request.
Step 8 — Profile Application Code Directly
Most languages have profiling tools available:
- PHP: Xdebug's profiler, or a lighter-weight tool like Blackfire
- Node.js:
node --prof, or the built-inconsole.time()/console.timeEnd()for quick manual profiling - Python:
cProfile
Step 9 — Check for Synchronous External API Calls
A request that waits synchronously on a slow third-party API call will be slow regardless of how fast your own server is — consider caching external API responses, or moving non-critical calls to a background job/queue.
Step 10 — Re-Measure After Each Fix
Apply one optimization at a time and re-measure, rather than changing several things simultaneously and losing track of what actually helped.
Common Culprits Summary
| Symptom | Likely Cause |
|---|---|
| Specific database-heavy pages slow | Missing index, or N+1 query pattern |
| All pages consistently slow | Undersized VPS, or a global bottleneck like a slow external dependency |
| Slow only under concurrent load | Database connection pool exhaustion, or lock contention |
Related Articles
- How to Tune MySQL/MariaDB Performance for a VPS
- How to Install and Secure Redis on Ubuntu & Debian
- How to Diagnose a Slow VPS: Complete Performance Checklist
