A MySQL or MariaDB server that swaps, gets OOM-killed, or refuses new connections is almost always misconfigured for the memory it has — not short of memory. On a 1 GB or 2 GB VPS you cannot give the database “enough” RAM, so you have to give it the right RAM. This guide walks through exactly where MySQL’s memory goes, which settings to cut, and how to verify the result.
Everything here targets MySQL 8.0 and MariaDB 10.6+ on Debian or Ubuntu. The numbers assume a small VPS also running Nginx and PHP-FPM, so the database gets roughly half the machine.
Step 1: Calculate What MySQL Is Actually Using
Do not guess. Two queries give you the real picture — global buffers (allocated once at startup) and per-connection buffers (multiplied by every open connection and potentially by every thread that runs a query):
mysql -e "
SELECT
@@innodb_buffer_pool_size/1024/1024 AS buffer_pool_mb,
@@key_buffer_size/1024/1024 AS key_buffer_mb,
@@tmp_table_size/1024/1024 AS tmp_table_mb,
@@max_heap_table_size/1024/1024 AS max_heap_mb,
@@max_connections,
@@sort_buffer_size/1024/1024 AS sort_mb,
@@read_buffer_size/1024/1024 AS read_mb,
@@join_buffer_size/1024/1024 AS join_mb;"
Estimated worst-case memory is:
total = buffer_pool + key_buffer + tmp_table
+ (max_connections * (sort + read + join + binlog_cache + net + thread_stack))
On a default install, max_connections of 151 multiplied by even 1 MB of per-connection buffers is 151 MB of potential overhead — before the buffer pool. That is often what pushes a small VPS into swap.
Step 2: Set the InnoDB Buffer Pool to Fit Your Machine
For a VPS where the database shares RAM with the web tier, 256 MB is the right starting point on a 1 GB machine and 512–768 MB on a 2 GB machine:
# /etc/mysql/mysql.conf.d/90-small.cnf (MySQL)
# /etc/mysql/mariadb.conf.d/90-small.cnf (MariaDB)
[mysqld]
innodb_buffer_pool_size = 256M
innodb_buffer_pool_instances = 1
innodb_log_buffer_size = 16M
innodb_flush_method = O_DIRECT
innodb_buffer_pool_instances = 1 is deliberate: splitting a small pool creates contention without benefit. O_DIRECT avoids double-caching the same pages in the OS page cache and the buffer pool, which on a small machine effectively doubles your usable database cache.
For a 1 GB VPS, also shrink the redo log so it does not dominate disk and memory during checkpoints:
innodb_log_file_size = 64M
Step 3: Cut the Per-Connection Buffers Aggressively
This is where the biggest wins are on small servers, and where most guides go wrong by leaving defaults. Every connection can allocate these buffers, so keep them tiny and let the buffer pool absorb the load:
max_connections = 50
thread_cache_size = 16
sort_buffer_size = 256K
read_buffer_size = 128K
read_rnd_buffer_size = 128K
join_buffer_size = 128K
binlog_cache_size = 32K
thread_stack = 192K
net_buffer_length = 8K
Then eliminate the connection churn that makes max_connections matter at all:
wait_timeout = 120
interactive_timeout = 120
If your application opens a connection per request, install a pooler rather than raising limits — a single PHP-FPM pool holding 10 persistent connections will serve far more traffic than 100 connections opened and closed continuously.
Step 4: Stop Temporary Tables From Eating Disk and RAM
Small servers usually have both slow disk and little RAM, so disk-based temporary tables are doubly painful. Keep temp tables in memory, but cap them hard:
tmp_table_size = 32M
max_heap_table_size = 32M
internal_tmp_mem_storage_engine = MEMORY
Set the two size values equal — MySQL uses the smaller of the two, and mismatched values are a common source of confusion. Then watch whether you are still spilling:
mysql -e "SHOW GLOBAL STATUS LIKE 'Created_tmp%';"
A high Created_tmp_disk_tables relative to Created_tmp_tables means queries are sorting or grouping too much data. The fix is an index, not a bigger limit — raising tmp_table_size further just delays the same spill while consuming RAM you do not have.
Step 5: Disable What You Are Not Using
Every feature you are not using costs memory. On a small web server:
performance_schema = OFF
# MariaDB only:
# aria_pagecache_buffer_size = 16M
# if you use only InnoDB:
# skip-innodb does not exist in 8.0; instead reduce stats:
innodb_stats_persistent_sample_pages = 20
performance_schema = OFF alone reclaims 100–200 MB on MySQL 8.0, which is a huge fraction of a 1 GB VPS. Re-enable it temporarily if you need to diagnose a lock issue, then turn it back off. If you use MariaDB, shrink the Aria page cache unless you rely on Aria tables.
Step 6: Verify the Server Is Actually Healthy Under Load
Restart and confirm the settings took effect:
sudo systemctl restart mysql
mysql -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size';"
free -m
mysqladmin status
Then generate load and watch for the failure you were trying to prevent — swapping and OOM kills:
# In one terminal
sysbench /usr/share/sysbench/oltp_read_write.lua --mysql-user=root \
--mysql-db=bench --table-size=100000 --threads=4 --time=120 run
# In another
watch -n2 'free -m; echo ---; grep -i "out of memory\|oom" /var/log/syslog | tail -5'
Success looks like: available memory never drops near zero, swap usage stays flat, and no OOM messages appear. If MySQL still swaps, cut the buffer pool by another 25% — a smaller cache that fits in RAM always beats a larger one that forces pages out to disk.
Step 7: Know When to Stop Tuning and Buy More RAM
There is a floor below which tuning cannot go. If your working set genuinely exceeds what the buffer pool can hold, you will see persistent disk reads and unresponsive queries no matter how tight the config is. Check the cache hit ratio:
mysql -e "
SELECT
ROUND(100 * (1 - (SELECT VARIABLE_VALUE FROM performance_schema.global_status
WHERE VARIABLE_NAME='Innodb_buffer_pool_reads') /
(SELECT VARIABLE_VALUE FROM performance_schema.global_status
WHERE VARIABLE_NAME='Innodb_buffer_pool_read_requests')), 2) AS hit_pct;"
Above 99% is healthy. Below 97% with a working set that cannot shrink, it is time for a larger plan — or a plan whose storage delivers consistent IOPS so the reads you cannot cache are at least fast. If that is where you have landed, our VPS comparison table lists plans with their measured IOPS and memory ceilings so you can size the upgrade against real numbers instead of spec sheets.
Pair this with correct swap sizing — a small, well-placed swapfile is a safety net, not a substitute for RAM. See our guide to swap space on a VPS for the numbers that work on small servers.

Leave a Reply
You must be logged in to post a comment.