Bali, Indonesia
Insights

Moved MySQL to a Bigger VPS but innodb_buffer_pool_size Is Still 128M

November 3, 2026
0:00
You move a MySQL database from a small VPS to one with four times the RAM and expect queries to get faster. They barely change. Disk I/O stays high, and free -h shows most of the new memory sitting unused. The cause is very often a single setting: innodb_buffer_pool_size is still 128M, the default value on a fresh install. Adding RAM to the server does not give InnoDB any more of it. Someone has to configure it. This article walks through the problem as a post-mortem: what the impact looks like, how the investigation usually goes, the point where the cause becomes clear, a temporary fix you can apply without a restart, and the permanent change.
InnoDB keeps table data and indexes in a memory area called the buffer pool. When a query needs a page that is already in the pool, it reads it from RAM. When the page is not there, InnoDB reads it from disk and may evict another page to make room. If the pool is much smaller than the data that gets read regularly, InnoDB keeps evicting pages and reading them back from disk. The default in current MySQL and MariaDB releases is 128M. That is fine for a small blog. Many WordPress, WooCommerce, or forum databases are several gigabytes once you include indexes, and a 128M pool cannot hold their regularly used pages. The symptoms are vague, which is why this problem lasts. Pages load slowly but do not fail. top shows a noticeable wa (I/O wait) value while CPU usage stays modest. The new server has plenty of free memory, which makes RAM look like the last thing to suspect. From the support side, the complaint usually sounds like "we upgraded the plan and nothing improved". That should prompt a check of the database configuration, not only the hardware.
The usual suspects get checked first: CPU load, steal time on the hypervisor, PHP workers, and the slow query log. On a box with this problem they often look ordinary. Individual queries may not be slow enough to reach the slow log threshold. Each one is just a bit slower than it should be, because it waits on disk. The high I/O wait is the useful clue. It points at storage, and the next question is why the database is reading from storage this much when so much RAM is free. Three read-only queries answer it:
SQL
-- Active buffer pool size in MB
SELECT @@innodb_buffer_pool_size / 1024 / 1024 AS buffer_pool_mb;

-- Logical read requests vs. reads that had to go to disk
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read%';

-- Total InnoDB data + index size in MB
SELECT ROUND(SUM(data_length + index_length) / 1024 / 1024) AS innodb_mb
FROM information_schema.tables
WHERE engine = 'InnoDB';
In the second result, compare two counters. Innodb_buffer_pool_read_requests counts page reads InnoDB served, from memory or disk. Innodb_buffer_pool_reads counts the reads that missed the pool and went to disk. Both are cumulative since the server started, so one snapshot tells you little. Take two snapshots a few minutes apart during normal traffic and compare how fast each one grows. If disk reads keep rising steadily on a site that is not doing anything unusual, the pool is too small for the working set. The third query gives the upper bound. It shows how large the InnoDB data actually is, which tells you how much buffer pool could possibly help.
This is usually where the cause becomes clear. The first query returns 128, on a server with 8 or 16 GB of RAM. The reason is simple once you see it. innodb_buffer_pool_size is a server setting stored in an option file such as my.cnf. It is not stored in the database. A mysqldump and restore, an rsync of the data directory, or a panel account transfer moves the data and leaves the configuration behind. A fresh MySQL install on the new VPS starts with no value set, so it uses the default. The new server's RAM has no effect on that. There are a few variations of the same problem:
  • The old my.cnf was copied over, including a value sized for the old, smaller server.
  • The line was added under [client] or [mysql] instead of [mysqld], so the server never reads it.
  • The value is set correctly in one file, but a file read later sets it again. When a setting appears more than once, the last value read takes effect.
  • Someone edited a file that this server does not read at all, which is common when distro packages and vendor packages use different paths.
You can check what the server actually reads instead of guessing:
BASH
# Which option files mysqld reads, and in what order
mysqld --verbose --help 2>/dev/null | grep -A1 'Default options'

# Options the [mysqld] group resolves to from those files
my_print_defaults mysqld | grep -i buffer_pool

# Every place the setting is defined on disk
grep -rn 'innodb_buffer_pool_size' /etc/my.cnf /etc/mysql/ 2>/dev/null
Paths and even the binary name vary. Newer MariaDB packages may use mariadbd and read a [mariadb] group, and control panels sometimes keep their own copy of the config. Treat the commands above as a starting point and adjust them to the server.
Restarting MySQL on a busy site is not always possible right away. In MySQL 5.7.5 and later, and in MariaDB 10.2.2 and later, the buffer pool can be resized while the server is running:
SQL
-- Illustrative example: set 4 GB (value in bytes)
SET GLOBAL innodb_buffer_pool_size = 4294967296;

-- Watch the resize progress
SHOW STATUS LIKE 'Innodb_buffer_pool_resize_status';
This requires an account with admin privileges. The resize runs in chunks, and the final value is rounded to a multiple of innodb_buffer_pool_chunk_size × innodb_buffer_pool_instances, so the result may differ slightly from the number you entered. Run the first query from the investigation again afterwards to see the real value. Resizing can briefly hold up queries while memory is reallocated, so a quieter period is a better time to do it. This is only a mitigation. A value set with SET GLOBAL is lost on the next restart, and the server returns to whatever the option files say. MySQL 8.0 offers SET PERSIST, which writes the value to a file in the data directory. That works, but it also means the setting now lives in a place the next person might not think to check. For a setting this important, keeping it in the main config file is usually clearer.
The value to choose depends on what else runs on the VPS. A common rule of thumb for a server that only runs the database is 50 to 75 percent of RAM. Most hosting VPS setups are not dedicated database servers. The same box often runs LiteSpeed, Apache, or Nginx, PHP-FPM workers, maybe Redis, a mail stack, cron jobs, and backup tasks. Each of these needs memory, and so does MySQL itself beyond the buffer pool, for per-connection buffers and temporary tables. A practical approach is to start from total RAM and subtract the rest. Leave room for the OS and page cache. Estimate PHP as the maximum worker count multiplied by typical memory per worker. Add whatever Redis and other services are configured to use. What remains is the most the buffer pool can safely have. Then compare that with the InnoDB size from the earlier query. Going well past the dataset size plus room for growth gives nothing back. As an illustrative example, take a 16 GB VPS running the web stack and the database together, with a 3 GB InnoDB dataset. A 4 GB pool fits the whole dataset with headroom and leaves plenty for PHP. If the dataset were 30 GB instead, it could not fit at all. The goal then is to hold the regularly accessed pages, which might mean 6 to 8 GB, depending on what the other services need. It is safer to undersize than to oversize. A pool that is too small makes things slow. A pool that is too large pushes the box into swap, or the kernel's OOM killer terminates mysqld. That causes an outage instead of slowness. Once you have a number, put it under [mysqld] in the file the server actually reads, as identified earlier. If a control panel manages that file, use the panel's method so the change is not overwritten. Restart MySQL during a maintenance window, then verify:
  • Run SELECT @@innodb_buffer_pool_size again and check that it matches the new value.
  • Run SHOW GLOBAL STATUS LIKE 'Uptime' to confirm the server really restarted and you are not looking at an old process.
  • After the pool has had time to warm up, compare the Innodb_buffer_pool_reads growth rate with your earlier snapshots. A restart empties the pool, so the first period will look worse until frequently used pages are loaded again.
  • Watch free -h and swap usage over a normal traffic cycle to make sure the new value fits alongside everything else.
The main lesson is to treat database configuration as part of the migration, not something that comes along with the data. Before moving a database, record the old server's innodb_buffer_pool_size and the other tuned values. After the move, choose a new value for the new server's RAM and the services running on it, and confirm it with a query instead of assuming it took effect. Upgrading the hardware only helps if the configuration changes with it.