How to Optimize MySQL my.cnf on a Small VPS (1-2 GB RAM) Without Crashing

Learn to tune MySQL my.cnf on a small VPS: size the InnoDB buffer pool, disable performance_schema, and cut RAM without losing throughput.

A 1 or 2 GB RAM VPS is where MySQL on defaults starts to hurt. The mysqld process takes ~450 MB at boot, your PHP/Node app gets squeezed, the kernel starts killing processes via OOM, and swap kicks in destroying I/O. The problem is rarely MySQL itself — it is the default my.cnf, calibrated for a generic 8 GB server.

This tutorial covers the my.cnf adjustments that actually matter on a small VPS: sizing innodb_buffer_pool_size to the real machine, turning off performance_schema when it makes sense, controlling connections and the binary log, and validating everything with commands you run before and after. The persona is a developer who spun up MySQL 8 or MariaDB on a 1-2 GB droplet, saw mysqld consume 40% of RAM, and wants to shrink it without losing performance.

Execution time: 20-30 minutes including a backup of the original my.cnf, editing, restart, and measurement. The adjustments apply to MySQL 8.0+ and MariaDB 10.6+ on Ubuntu 22.04/24.04 LTS and Debian 12.

Prerequisites

What you need before starting

A Linux VPS (Ubuntu 22.04/24.04 LTS or Debian 12) with 1 or 2 GB of RAM, MySQL 8.0+ or MariaDB 10.6+ installed and running, sudo access, and the MySQL root credentials. Back up your database before anything — mysqldump --single-transaction --all-databases > backup.sql — because a restart with a broken my.cnf can prevent the service from starting.

Confirm what you have today before touching a single line:

Total RAM free -m
mysqld RAM ps -o rss= -p $(pidof mysqld)
MySQL version mysql --version
Config path /etc/mysql/mysql.conf.d/mysqld.cnf

On MariaDB, the main file is /etc/mysql/mariadb.conf.d/50-server.cnf. In both, the rule is the same: do not edit /etc/mysql/my.cnf directly — it only performs !includedir of the files in the mysql.conf.d/ or mariadb.conf.d/ directories.

Diagnosing current consumption

Before cutting, measure. Without a baseline, you have no idea whether the tuning helped.

01

Check the current mysqld RSS and how much of total RAM that is:

ps -o pid,rss,cmd -p $(pidof mysqld)
free -m

The rss value is in KB. Divide by 1024 to get MB. On a 1 GB VPS with default MySQL 8, expect something between 380 and 500 MB — well above what is needed for the real load of a small site.

02

Look at real InnoDB buffer pool usage — whether the default 128 MB pool has headroom or is undersized:

mysql -u root -p -e "SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_pages%';"

How to read it:

  • pages_total = pool size in 16 KB pages
  • pages_free near zero = pool is full, MySQL is recycling pages
  • pages_data / pages_total = how much is in productive use

If pages_free is high (>30% of total) and the server has days of uptime, you can shrink the pool without harm. If it is near zero and Innodb_buffer_pool_reads (not read_requests) grows fast, the pool is undersized.

03

Check performance_schema — alone it eats 70-100 MB:

mysql -u root -p -e "SELECT SUM(MEMORY_USED)/1024/1024 AS mb FROM sys.x\$memory_global_by_current_bytes;"

On a 1 GB VPS with no external profiling tool reading from it, this is the first candidate to switch off.

Editing my.cnf

Create your own override file instead of editing the default mysqld.cnf — that way, package updates do not overwrite your tuning.

01

Create the override file:

sudo nano /etc/mysql/mysql.conf.d/99-hostini-tuning.cnf

On MariaDB: /etc/mysql/mariadb.conf.d/99-hostini-tuning.cnf. The 99 ensures this file is read last — overriding any default.

02

Paste this base configuration for a 1 GB RAM VPS:

[mysqld]
# InnoDB — the heart of MySQL
innodb_buffer_pool_size = 256M
innodb_buffer_pool_instances = 1
innodb_log_buffer_size = 8M
innodb_flush_method = O_DIRECT
innodb_flush_log_at_trx_commit = 2

# Connections — a small VPS cannot handle 150
max_connections = 50
thread_cache_size = 8
table_open_cache = 200
table_definition_cache = 400

# Performance schema — off on 1 GB
performance_schema = OFF

# Binary log — off if you do NOT use replication
disable_log_bin
# On MariaDB use: skip-log-bin

# Slow query log for diagnosis
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2

# Temp tables — in RAM up to this limit
tmp_table_size = 16M
max_heap_table_size = 16M

# Sort/join buffers — per connection, watch max_connections
sort_buffer_size = 256K
join_buffer_size = 256K
read_buffer_size = 128K
read_rnd_buffer_size = 256K

For a 2 GB VPS, tweak only these lines:

innodb_buffer_pool_size = 512M
max_connections = 100
performance_schema = ON
performance-schema-instrument='memory/%=COUNTED'
Watch out for per-connection buffers

sort_buffer_size and join_buffer_size are allocated PER ACTIVE CONNECTION, not globally. Bumping each to 4 MB with 100 connections = 400 MB potentially in those buffers alone. The low values in the snippet (256K) are intentional — a slow query on a small VPS is solved with an index, not a bigger buffer.

03

Save (Ctrl+O, Enter, Ctrl+X) and validate the syntax before the restart:

sudo mysqld --validate-config

If any error appears, the service will not come up after the restart. Fix it before continuing. Expected output: no lines (silence is success).

04

Restart MySQL and check the status:

sudo systemctl restart mysql
sudo systemctl status mysql --no-pager

On MariaDB: sudo systemctl restart mariadb. If the status shows active (running), move on. If it failed, read the reason in sudo journalctl -u mysql -n 50 — it is almost always a typo in the .cnf.

Verification

Confirm the tuning took effect and the RAM savings showed up.

01

Confirm the values that are now in effect:

mysql -u root -p -e "SHOW VARIABLES WHERE Variable_name IN ('innodb_buffer_pool_size', 'max_connections', 'performance_schema', 'log_bin', 'tmp_table_size');"

The values should match what you put in 99-hostini-tuning.cnf. If any still shows the old default, the file is not being read — check the path and permissions (it must be 644 root:root).

02

Measure RAM consumption again:

ps -o pid,rss,cmd -p $(pidof mysqld)
free -m

Expectation post-tuning on a 1 GB VPS: mysqld at ~280-340 MB (a ~30% drop). On 2 GB with performance_schema on: ~500-580 MB. Wait a few minutes with real traffic before the final measurement — the pool warms up and stabilizes.

03

Run SHOW STATUS after 1-2 hours of real use and confirm the buffer pool is not choking:

mysql -u root -p -e "SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_%';"

Signs of health: pages_free > 5% of pages_total, reads growing slowly compared to read_requests (hit ratio > 99%). If reads spikes, bump the buffer pool by 64 MB and re-test.

Troubleshooting

MySQL fails to start after the restart

Check the journal log and find the exact line that broke:

sudo journalctl -u mysql -n 80 --no-pager

Common causes: innodb_buffer_pool_size larger than the total available RAM, O_DIRECT on a filesystem that does not support it (rare on ext4/xfs), a deprecated option in MySQL 8 (query_cache_size was removed). Revert your 99-hostini-tuning.cnf to a minimum base (only innodb_buffer_pool_size) and add lines back one at a time.

”Out of memory” in the application after tuning

You reduced MySQL’s RAM but the app grew to fill the void. Run free -m during real load and watch whether available drops below 100 MB. If it does, consider lowering swappiness (sudo sysctl vm.swappiness=10) and enable a 1 GB swap file as a safety net — not a production solution, but it prevents OOM kill while you size things better.

Queries that used to be fast are now slow

Classic symptom of a buffer pool that became too small — the table used to fit in RAM, now it does not. Check Innodb_buffer_pool_reads (not read_requests) on SHOW GLOBAL STATUS before and after. If it climbed sharply, raise innodb_buffer_pool_size by 64 MB and measure again. Structural fix: add an index to the query, do not inflate the pool.

Next steps

With my.cnf tuned, it is worth consolidating the operation:

  • Configure rotation for the slow log (slow.log grows fast) via logrotate in /etc/logrotate.d/mysql-server
  • Schedule a daily mysqldump --single-transaction with cron and ship it to external storage (rclone + S3 or Backblaze B2)
  • In production with replication or point-in-time recovery, keep log_bin on and set expire_logs_days = 7 to control disk
  • Consider moving to a VPS with more RAM if the 256 MB pool cannot keep up — generally, jumping to 4 GB eliminates fine tuning and frees you to focus on the product

A Hostini VPS ships with NVMe SSD and a kernel tuned for database workloads — O_DIRECT works without silent fallback and predictable I/O reduces the need for an oversized buffer pool to compensate for slow disk latency.

Frequently asked questions

How much RAM does MySQL 8 use by default without tuning?

On a stock MySQL 8.0 install on Ubuntu/Debian, the mysqld process sits between 380 and 500 MB of RSS at boot, with no active connections. That comes mostly from innodb_buffer_pool_size (128 MB), performance_schema (~80 MB), and the mysql.* system tables. On a 1 GB VPS, that alone eats ~40% of RAM before a single query runs.

Can I turn off performance_schema without breaking anything?

Yes, with caveats. performance_schema powers the sys.* views and tools like percona-toolkit's pt-query-digest. Turning it off saves 70-100 MB of RAM but you lose fine-grained visibility into slow queries. Recommended on a 1 GB VPS; on 2 GB or more, keep it on and tune just the consumers via setup_consumers.

What is the ideal innodb_buffer_pool_size on a small VPS?

The 70-80% of physical RAM rule only applies to servers dedicated to MySQL. On a 1 GB VPS running MySQL plus a web app plus nginx, keep it between 256 and 384 MB. On a 2 GB VPS, 512 to 768 MB. The rest goes to the app, OS cache, and a margin against the OOM killer.

Is it worth disabling the binary log (log_bin) on a small VPS?

Only disable it if you do NOT use replication or point-in-time recovery. The binary log eats IOPS (sequential writes) and disk (~10-20% of write traffic). Without it, restore only rewinds to the last mysqldump. For a single instance with acceptable daily backups, disabling is fine; for any setup that may become a replica later, keep it on.

How do I know if my tuning actually worked?

Run SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_pages%' after 24h of real traffic. If Innodb_buffer_pool_pages_free stays near zero and Innodb_buffer_pool_reads grows fast, the pool is too small. Combine that with free -m to confirm the system is not swapping. The mysqld RSS in ps aux should stabilize after warm-up.

Can I use MariaDB instead of MySQL to save RAM?

MariaDB 10.6+ has a footprint similar to MySQL 8.0 — there is no magic saving. The real difference comes from my.cnf tuning, not from the fork. MariaDB ships some more conservative defaults (innodb_buffer_pool_size=128M as well), so the same adjustments in this tutorial apply with `[mariadb]` in place of `[mysqld]` where applicable.

Topics:
Next steps Ryzen cloud with NVMe storage and always-on DDoS protection.Go live on a Hostini VPS →
Was this tutorial helpful?
Chat on WhatsApp