Running Ghost on 1GB RAM - mySQL fix

The memory requirement is 2GB RAM for self-hosted Ghost which is very reasonable.

I have a personal blog not expecting much traffic, so I run it on a 1GB droplet on DigitalOcean. It uses 80-90% RAM after installation as expected, but Ghost can not be updated which is frustrating.

By tweaking the mySQL config I reduced he memory consumption down to 70% which is fine for now with very few visitors. I’ll upgrade the VPS and remove the tweaks when/if I see more traffic. Checking mySQL use with mytop there’s really no load on the DB.

This is obviously not a good idea for a professional Ghost deployment, but for a personal blog it should be OK.

Here’s my config in /etc/mysql/my.cnf

[mysqld]
performance_schema = off
key_buffer_size = 16M
tmp_table_size = 1M
innodb_buffer_pool_size = 1M
innodb_log_buffer_size = 1M
max_connections = 50
sort_buffer_size = 512K
read_buffer_size = 256K
read_rnd_buffer_size = 512K
join_buffer_size = 128K
thread_stack = 196K
1 Like

Hi, I have the same configuration on Digital Ocean. It was very frustrating to know that digital ocean sells a service (the droplet) with Ghost that basically doesn’t work out of the box. Luckily, many good people created guides to setup the machine and make it faster without crashes. Have a look here: Ghost on 1GB Digital Ocean droplet using 100% of memory - #8 by jazmy

You can create 2GB SWAP on the server and run Ghost without any issues. I have made more than 8 Ghost websites on Digital Ocean 1GB servers with 2GB SWAP and they are doing well.

So I made a tutorial for making swap.

Run Ghost on 1GB RAM without getting stuck :partying_face: - Developer help / Installation - Ghost Forum

Swap solves memory problems at a performance cost, but the MySQL tuning posted above is a no-compromise solution that adjusts the settings to align with a site that has less data and less traffic than the default tuning expected.

So, use both approaches for a small site on a small server with less traffic.

1 Like

The SQL-tweaking above brings memory down below 60% - would be interesting to test how many simultaneous visitors Ghost can take before falling apart with this configuration.

For now it holds. But my website it’s still not that big to try with, let’s say, 200 simultaneous user. I hope one day I will have to care about this type of problems :D What’s the maximum users you reached with your DO 1GB droplet?

1 Like

Hmmm… maybe I’ll do a run with loadview-testing.com this weekend and see what happens. It’s probably a very good idea to snapshot the Digital Ocean droplet beforehand, thinking this could corrupt mySQL tables and what not.

High load should not corrupt MySQL. To do that you’d have full up the disk with logging or exhaust the memory so processes are randomly killed. Even then, it’s likely MySQL would recover after a reboot.

If Nginx is tuned as a caching reverse proxy, the traffic load could go even higher. For example, tune blog posts to be cached for one minute. So only one request per minute to blog posts would even cause a MySQL query.

I have not enabled Nginx caching yet myself.

I made an annotated guide to the tuning settings posted above. This is not an endorsement of them, just explaining how the defaults were changed and what the impact might be.

Generally, it seems they try to trade lower memory use for increased disk I/O or higher CPU load. It’s not clear which settings are really making the most impact without testing them.

One setting that would be a “win-win” for personal blog would be turn down the max_connections. I’m running 5 blogs one 2 GB memory instance and the max connections MySQL has ever needed at once is about 25, compared to the default of 151.

This is not necessarily related to the “max visitors at one time”. For example, Nginx can be serving static files and even the Ghost blog itself can send multiple queries through a single MySQL connection. And if Nginx connections are tuned probably, when the site starts to overload, Nginx can queue the connections rather than completely overloading the server.

So for a single blog, I might try max_connections = 10 and separately read up on tuning your Nginx connection limits.


# The Performance Schema enables some monitoring tools for 1 to 3% CPU penalty.
# If you are using those monitoring tools, turn it off to improve performance.
# https://dev.mysql.com/blog-archive/performance-schema-great-power-comes-without-great-cost/
performance_schema = off

# The default value of key_buffer_size is 8M. 
# Increasing the value to 25% of RAM improves index performance
# https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_key_buffer_size
key_buffer_size = 16M

# default value for tmp_table_size is 16M
# defines the maximum size of any individual in-memory internal temporary table  
# Lowering it could save memory but reduce performance
# https://dev.mysql.com/doc/refman/8.0/en/internal-temporary-tables.html
tmp_table_size = 1M

# default value for innodb_buffer_pool_size is 128M
# Lowering it reduces memory use while increasing disk I/0
# https://dev.mysql.com/doc/refman/8.0/en/innodb-parameters.html#sysvar_innodb_buffer_pool_size 
innodb_buffer_pool_size = 1M

# The default value of innodb_log_buffer_size is 16M
# buffer size that InnoDB uses to write to the log files on disk.
# reducing it saves memory but increases Disk I/O in some casees.
# https://dev.mysql.com/doc/refman/8.0/en/innodb-parameters.html#sysvar_innodb_buffer_pool_size
innodb_log_buffer_size = 1M

# The default value of max_connections is 151
# Reducing connections reduces memory.
# For a personal blog, as few as 10 may be sufficient.
# There's no point setting the value higher than the number that the application 
can use under load.
# https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_max_connections
max_connections = 50

# The default value of sort_buffer_size is 256K
# It's the amount memory allocated when sorting
# It's not clear why increasing it would improve performance or reduce memory
# on a smaller server.
# ref: https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_sort_buffer_size
sort_buffer_size = 512K

# The default for read_buffer_size is 128K
# Increasing it may help the performance of sequential scans
# Ref: https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_read_buffer_size
read_buffer_size = 256K

# The default value for read_rnd_buffer_size is 256K
# A higher value improves disk I/O but uses more memory.
# https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_read_rnd_buffer_size
read_rnd_buffer_size = 512K

# The default size for join_buffer_size is 256K 
# Smaller values can slow down joins but use less memory
# https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_join_buffer_size
join_buffer_size = 128K

# The default size of thread_stack varies up to 1048k
# A smaller size limits the complexity of SQL the server can handle,
# but saves memory
# https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_thread_stack
thread_stack = 196K
1 Like

Edit, are the settings safe?