------------------------------------------------------------------------------------------------------------------
Part 1, unraid write amplification
------------------------------------------------------------------------------------------------------------------
To start out with, this is just a journal of my own experiences dealing with these writes, you do any and all of these commands at your own risk! Always make a backup of any data/drives you will be messing with. I would recommend getting a fresh backup of your cache drive and docker/appdata before starting any of this, just in case. Honestly it is a good excuse to update your complete backup.
Ok, so as many have started to realize unraid had a serious issue with massively inflated writes to the cache SSD for the last few years. To the point it has killed a number of SSD's in a very short amount of time and used up a hefty amount of the life of many other drives.
A lot of it was documented here:
but instead of reading all that I am going to give you the results of all the testing that went on in that thread.
My writes when starting this journey with far less dockers then I have now was around :
~200gb+/day IIRC (forgot exact numbers and lot my notes from that long ago but it was a LOT)
The first step to reducing writes is to update to upraid 6.9+ and then move all the data off your cache SSD's to the array temporarily. You will then erase the cache pool using the built in erase option and reformat it when you restart the array. This fixes the core unraid side of the excessive writes. It fixes some partition and mounting issues with the filesystem.
After that move the data back to the cache from the array.
This dropped my writes to around ~75-85gb/day using a single BTRFS formatted drive with BTRFS image.
Formatted as XFS with BTRFS image my writes dropped to ~25gb/day but you can't have redundancy then and has it's own issues.
The excessive writes still persist as you see just to a lesser extent after this, the remaining writes will be dependent on what dockers you are using and is an issue with docker.
------------------------------------------------------------------------------------------------------------------
Part 2: Docker logs causing write inflation
------------------------------------------------------------------------------------------------------------------
All the docker commands I will put below need to be entered into the
Extra Parameters:
section of the docker template in unraid (you will need to go to the advanced view in the top right corner)
To match up a long container ID with container in unraid GUI, simply use crtl+f to search the docker page in unraid for the container ID you see in the activity logs. Generally the first 3 or 4 characters are enough to find the right container.
There are a few basic places writes come from with docker and each has it's own fix.
------------------------------------------------------------------------------------------------------------------
The first step is to run the inotifywait command from mgutt:
This command will watch the internal docker image for writes and log them to /mnt/user/system/recentXXXXXX.txt
inotifywait -e create,modify,attrib,moved_from,moved_to --timefmt %c --format '%T %_e %w %f' -mr /var/lib/docker > /mnt/user/system/recent_modified_files_$(date +"%Y%m%d_%H%M%S").txt
An alternate and less effective method is to use this command to return the 100 most recently modified files in the docker image
find /mnt/user/system/docker -type f -print0 | xargs -0 stat --format '%Y :%y %n' | sort -nr | cut -d: -f2- | head -n100
I chose to make a userscript with the first command and then use the "run in background" option so I don't have to keep the terminal open.
To kill the inotifywait run this:
pkill -xc inotifywait
------------------------------------------------------------------------------------------------------------------
For me the first and most common writes came from the internal logging mechanism in docker. It basically logs all the messages that would show up in the terminal if it was run directly and not in docker among other stuff.
These writes will be to:
/var/lib/docker/containers/{containerid}/{containerid}-json.log
These are stopped by the following command while leaving the unriad GUI logs in tact:
--log-driver syslog --log-opt syslog-address=udp://127.0.0.1:541
------------------------------------------------------------------------------------------------------------------
The next type of writes are from:
/var/lib/docker/containers/{containerid}/container-cached.log
These are the logs you see when you click the log option in the unraid gui, these require a stronger version of the above command:
--log-driver none
This disables both the above type of logs.
------------------------------------------------------------------------------------------------------------------
Next up are the helthcheck logs, these are seen as writes to these files:
/var/lib/docker/containers/{containerID}/.tmp-hostconfig.json{randomnumbers}
/var/lib/docker/containers/{containerID}/.tmp-config.v2.json{randomnumbers}
These are solved by either extending the health checks or disabling them. I prefer extending them to ~1 hour.
--health-interval=60m
They can be disabled completely with:
--no-healthcheck
------------------------------------------------------------------------------------------------------------------
The next type of writes are internal logs from the program in the container to the /tmp directory of the container
/var/lib/docker/containers/{containerid}/tmp/some type of log file
Or
/var/lib/docker/containers/{containerid}/var/tmp/some type of log file
or
/var/lib/docker/subvolumes/{Randomstring}/tmp/some type of log file
This last one is hard to figure out as it can be difficult to connect the subvolume to a container,
sometimes opening the log file in question can clue you into what docker it is for.
This is a more advanced rabbit hole that was not really nesscary to chase in my case
This is from a program thinking it is writing to a ramdrive but by default docker does not map a ramdrive to the /tmp directory. You can do it yourself easily though with the following command (can be adapted to other dirs and use cases as well).
This command creates a ramdrive in /tmp with full read/write permissions and a max size of 256mb (much larger then needed in most cases but it only uses ram as needed so should not hurt anything in most cases, you can make it smaller as well):
--mount type=tmpfs,destination=/tmp,tmpfs-mode=1777,tmpfs-size=256000000
And thats pretty much it for properly created containers.
Doing these commands to the worst offending containers dropped my writes down to around ~40gb/day
I left a few containers logs in tact as I have needed them a few times.
------------------------------------------------------------------------------------------------------------------
Part 3: Dealing with appdata writes
------------------------------------------------------------------------------------------------------------------
After this things get a bit more complicated. Each container will behave differently and you will kinda have to wing it. I saw random writes to various files in containers, sometimes you could change the logging folder in the program to the /tmp folder and add a ramdisk to the container.
Others you cap map another ramdrive to some other log folder and still others can use other workarounds unique to that specific program. It takes some know-how and digging to fix writes internally in the dockers.
The alternate and universal option (and required option in many cases) is to simply copy the appdata folder to a ramdisk on unraid and sync it back to the SSD hourly. This works with any docker and vastly reduces writes from logs / constant database access.
Like above first you need to log the appdata folder to see where the writes come from:
This command will watch the appdata folder for writes and log them to /mnt/user/system/appdata_recentXXXXXX.txt
inotifywait -e create,modify,attrib,moved_from,moved_to --timefmt %c --format '%T %_e %w %f' -mr /mnt/user/appdata/*[!ramdisk] > /mnt/user/system/appdata_recent_modified_files_$(date +"%Y%m%d_%H%M%S").txt
From here it will take some detective work to find the misbehaving containers and see what and where they are writing to. In my case all the *arr's (sonarr etc) were causing a lot of writes and there was nothing that could be done internally to fix it.
After figuring out which appdata needs to move to the ramdisk is to create the ramdisk itself and then copy the appdata into it from the SSD.
First create a folder in /mnt/cache/appdata/, Very important to create the folder on the drive itself and NOT in /user.
mkdir /mnt/cache/appdata/appramdisk
chmod 777 /mnt/cache/appdata/appramdisk
after this I use a very basic user script that is set to "run at array start", adjust the max size of the disk to suite your use case, it only uses ram as needed so there is not a lot of harm in making it too big as long as it leaves enough room for everything else to run.
You will need to customize the rsync commands with the folders you want to copy naturally.
#!/bin/bash
#description=This script runs at the start of the array creating the appdata ramdisk and rsyncing the data into it
echo ---------------------------------Create ramdisk for appdata----------------------------------
mount -vt tmpfs -o size=8G appramdisk /mnt/cache/appdata/appramdisk
echo ---------------------------------rsync to ramdisk in appdata----------------------------------
rsync -ah --stats --delete /mnt/user/appdata/binhex-qbittorrentvpn /mnt/user/appdata/appramdisk
rsync -ah --stats --delete /mnt/user/appdata/binhex-nzbhydra2 /mnt/user/appdata/appramdisk
rsync -ah --stats --delete /mnt/user/appdata/*arr /mnt/user/appdata/appramdisk
I then have a separate script set to run hourly that rsync's everything in the ramdisk back to the SSD, it only copied the data that was changed to save writes:
#!/bin/bash
#description=This script syncs the ramdisk appdata back to the ssd
rsync -ahv --progress --delete /mnt/user/appdata/appramdisk/* /mnt/user/appdata/
You will also need to apply a delay to the first docker container that is set to autostart in the unraid GUI (enable advanced view, right side of the container). Preferably put a container that is not being run out of the ramdisk first and put the delay on it as the delay takes effect after the selected container has started.
The delay needs to be long enough for the ramdisk rsync to complete.
UPDATE THE DOCKER APPDATA FOLDER TO USE THE NEW "appramdisk" copy of the appdata or it will just keep writing to the cache.
Now for a clean shutdown, I created a "stop" file on the USB drive at /boot/config. It is called first thing when you click shutdown/reboot in the GUI and the rest of the shutdown will wait until it is finished.
touch /boot/config/stop
In the stop file I decided to simply redirect it to a script in user scripts called "Run at Shutdown" to make it easier to manage.
#!/bin/bash
#Runs the user script "Run at Shutdown" during shutdown or reboot.
#it is called before anything else during the shutdown process
# Invoke 'Run at Shutdown' script if present
if [ -f /boot/config/plugins/user.scripts/scripts/Run\ at\ Shutdown/script ]; then
echo "Preparing Run at Shutdown script"
cp /boot/config/plugins/user.scripts/scripts/Run\ at\ Shutdown/script /var/tmp/shutdown
chmod +x /var/tmp/shutdown
logger Starting Run at Shutdown script
/var/tmp/shutdown
fi
The run at shutdown script itself first stops all running docker containers so they can close out open files. It then rsyncs the appramdisk back to the SSD before clearing the ramdisk and unmounting it.
#!/bin/bash
#description=This script runs first thing at shutdown or reboot and handles rsyncing appramdisk and unmounting it.
logger Stopping Dockers
docker stop $(docker ps -q)
logger Dockers stopped
logger Started appramdisk rsync
rsync -ah --stats --delete /mnt/user/appdata/appramdisk/* /mnt/user/appdata/ | logger
logger rsync finished
logger clearing appramdisk data
rm -r /mnt/user/appdata/appramdisk/* | logger
logger unmounting appramdisk
umount -v appramdisk | logger
And thats it, seems to be working good, no hang-ups when rebooting and everything is working automatically.
Risks are minimal for these containers as worst case I loose an hours worth of data from sonarr, big deal. I would not use this on a container that has data you can't afford to loose an hours worth of.
The writes are finally low enough that I would be ok putting appdata and docker back onto my main SSD's with redundancy instead of the single piece of junk drive I am using now (which has gone from ~98% life to 69% in the last year doing nothing but handling docker on unraid)
I am really impressed with how well this is working.
So to recap:
unraid 6.8 > BTRFS image > BTRFS formatted cache =
~200gb++/day
unraid 6.9 > BTRFS image > separate unprotected XFS SSD everything stock =
~25GB/day
unraid 6.9 > BTRFS image > BTRFS SSD everything stock =
75-85GB/day
unraid 6.9 > Docker Folder > BTRFS SSD everything stock =
~60gb/day
unraid 6.9 > BTRFS image > BTRFS SSD > Disabled the low hanging fruit docker json logs =
~48gb/day
unraid 6.9 > BTRFS image > BTRFS SSD > Disabled all misbehaving docker json logs for running containers except those I want to see + added ramdrives to /tmp in containers that do internal logging =
~30gb/day
unraid 6.9 > BTRFS image > BTRFS SSD > Disabled all misbehaving docker json logs for running containers except those I want to see + added ramdrives to /tmp in containers that do internal logging + moved appdata for the *arr's and qbittorrent to a ramdisk with hourly rsyncs to the ssd appdata =
~10-12gb/day
Since most of the writes are large writes from the rsync, there is very little write amplification which vastly improves the total writes from the day even though that is possibly more raw data being written to the drive.
I dont use plex but it and database dockers are known for being FAR worse then what I run in writes. People were regularly seeing hundreds of GB in writes a day from these alone. They could be vastly improved with the above commands.