Persistent ramdisk with tmpfs and rsync



Minecraft reads and writes a lot from disk. In order to speed this up we can store the files in memory instead. If you google "minecraft tmpfs" you will find a lot of info on this.

The main issue is that ramdisks created using tmpfs are temporary. The content is wiped on a system reboot.

The script I created takes care of persisting the ramdisk. It is a general solution that can be used for any content. Minecraft servers included.

#!/bin/bash

# QUESTIONG: What's the purpose of this script?
# ANSWER: 
# The purpose is to mount a part of the computers RAM to a folder on the harddrive.
# This will make IO to that folder VERY fast. However the content of such a folder
# will be erased on system restart. This script uses rsync to mirror the content of
# another folder on the harddrive. This way the persistance issue is solved as well

# QUESTIONG: How do I use this script?
# ANSWER: 
# Make sure this script runs 
#  - directly after system start
#  - directly before system shutdown
#  - every once in a while
#
# You whould not care about the hdd-folder (on the hard-drive).
# Work with the files in the ram-folder. This script will take care of the save and
# load to hdd. See the hdd-folder as a persistance backup you don't need to care about :)

# QUESTION: How do I install this? 
# QUESTION: Where should I put this script?
# QUESTION: How do I make it execute at those times you mentioned above?
# ANSWER: (For Ubuntu)
# Put this script here: /etc/init.d/ramdrivetick
# Then run this: chmod +x /etc/init.d/ramdrivetick
# Then run this: update-rc.d ramdrivetick defaults
# The script will now run on startup, reboot and shutdown.
# To make it run each other create a file /etc/cron.hourly/runramdrivetick with this content
# ---------
# #!/bin/bash
# . /etc/init.d/ramdrivetick
# ---------
# Then run this: chmod +x /etc/cron.hourly/runramdrivetick
# If you get this message: /bin/bash^M: bad interpreter: No such file or directory
# This means that you have incorrect line endings in the file. Make them linux line endings.

# QUESTION: How do I uninstall this? 
# Run this: rm /etc/cron.hourly/runramdrivetick
# Run this: update-rc.d -f ramdrivetick remove

# QUESTION: How can I trigger a manual "tick"?
# Run this: service ramdrivetick

# ===== Configuration START =====
SIZE="4000m" #Size of the RAM folder. This obviously can not be more than the RAM available on your system.
FOLDER_RAM="/ramdrive" # Path to the folder where the ram will be mounted
FOLDER_HDD="/ramdrivehdd" # Path to our backup hdd folder
PERMMODE="0755" # The permission for the folders. Used when creating non-existing folders and when mounting.
MARKFILE_INITIATED=".zz_ramdrive_initiated" # This file is used to mark if the initial move of items from hdd to ramdisk yet is done
MARKFILE_RUNNING=".zz_ramdrive_sync_is_running" # Marking that an action is running at the moment.
# ===== Configuration END =====

# Calculate folderpaths for the markers
MARKFILE_INITIATED_FOLDER="${FOLDER_RAM}/${MARKFILE_INITIATED}"
MARKFILE_RUNNING_FOLDER="${FOLDER_RAM}/${MARKFILE_RUNNING}"

# ===== Function Declarations START =====
func_sayhi()
{
	echo ""
	echo "====== RamDriveTick START ======"
}

func_exit()
{
	echo "====== RamDriveTick END ======"
	exit 0
}

func_mark_open()
{
	# Stop if a sync is running already
	if [ -e "${MARKFILE_RUNNING_FOLDER}" ]; then
		echo "A sync is already in progress. Exiting."
		func_exit
	fi
	# Otherwise mark this sync in progress
	touch ${MARKFILE_RUNNING_FOLDER}
}

func_mark_close()
{
	rm ${MARKFILE_RUNNING_FOLDER}
}

func_makefolders()
{
	# Create the folders if they don't exist :)
	if [ ! -d "${FOLDER_HDD}" ]; then
		echo "Creating hdd folder \"${FOLDER_HDD}\""
		mkdir -p ${FOLDER_HDD}
		chmod ${PERMMODE} ${FOLDER_HDD}
	fi
	if [ ! -d "${FOLDER_RAM}" ]; then
		echo "Creating ram folder \"${FOLDER_RAM}\""
		mkdir -p ${FOLDER_RAM}
		chmod ${PERMMODE} ${FOLDER_RAM}
	fi
}

func_mount()
{
	# Mount the tmpfs if it isn't already mounted
	if ! mountpoint -q "${FOLDER_RAM}"; then
		echo "Mounting a tmpfs with size ${SIZE} to the ram mountpoint folder \"${FOLDER_RAM}\"."
		mount -t tmpfs -o size=${SIZE},mode=${PERMMODE} tmpfs ${FOLDER_RAM}
	fi
}

func_sync()
{
	# Syncronize
	if [ -e "${MARKFILE_INITIATED_FOLDER}" ]; then
		echo "Saving from memory to disk."
		rsync -av --delete --exclude ${MARKFILE_INITIATED} --exclude ${MARKFILE_RUNNING} "${FOLDER_RAM}/" ${FOLDER_HDD}
	else
		echo "INIT! The ram drive content will now be synchronized to look like the hdd content."
		rsync -av "${FOLDER_HDD}/" ${FOLDER_RAM}
		touch ${MARKFILE_INITIATED_FOLDER}
	fi
}
# ===== Function Declarations END =====

# ===== Execute START =====
func_sayhi
func_makefolders
func_mount
func_mark_open
func_sync
func_mark_close
func_exit
# ===== Execute END =====

OR you could just move your whole server there 🙂 That way you won't need to mess with symbolic links at all. If you are using the dynmap plugin you should configure so the rendered tiles are stored elsewhere. Because all those images can easily reach over 10 gigabytes.