Storing Files in Hetzner Volumes with Laravel and Statamic
Storing large amounts of data online has become incredibly cheap and easy in the last few years. Just hook your app up to Amazon S3 or one of the other cloud providers and enjoy virtually endless storage capacity.
But there are also other alternatives: if you're using the Hetzner Cloud for your hosting, you can easily add storage volumes for the low price of 0,04€ per GB per month.
I had the opportunity to use this for a recent project where the client didn't want to use any American cloud provider but needed to store around 50GB of data with another 20GB added every year, without breaking the bank.
So here's a quick tutorial on how you can use Hetzner Volumes for your Laravel app or your Statamic site.
Creating Your Volume
Setting up the storage volume is easily done using the Hetzner Cloud Console. Navigate to the server you want to attach it to, select volumes and create a new one. Next you get to chose the size (which can easily be increased later), as well as the file system type (ext4 vs xfs).
There are some minor differences between ext4 and xfs, but in most cases (including mine), you can just go with ext4. Feel free to do your own research if you think your case might be different.
After creating it you will get a screen with four commands you need to run on your server. Note that all of these require admin privileges (sudo) and they will obviously be different depending on the name of your volume.
mkfs.ext4 -F /dev/disk/by-id/scsi-0HC_Volume_20906626
mount -o discard,defaults /dev/disk/by-id/scsi-0HC_Volume_20906626 /mnt/volume-nbg1-1
echo "/dev/disk/by-id/scsi-0HC_Volume_20906626 /mnt/volume-nbg1-1 ext4 discard,nofail,defaults 0 0" >> /etc/fstab
The last line wouldn't work for me but gave me a 'permission denied' error instead. I was however able to open the file with
nano /etc/fstab and add the line
/dev/disk/by-id/scsi-0HCVolume20906626 /mnt/volume-nbg1-1 ext4 discard,nofail,defaults 0 0 manually, which should do the same.
After this your volume is mounted on the server and should always be activated again automatically if you have to restart it.
In order to use the new volume your app needs to be able to read from and write to it, something that currently requires administrator privileges. So first we need to figure out the correct user - which in my case is called ploi, but in most setups is www-data.
You can check who "owns" your project by running
ls -ld . in your app's main folder which in my case returned
drwxr-xr-x 17 ploi ploi 4096 Jul 1 10:21 .. Running the same command (
ls -ld /mnt/volume-nbg1-1) on the newly created mount returns
-rw-r--r-- 1 root root 0 Jul 1 10:46 /mnt/volume-nbg1-1, meaning it is owned by root and therefor not accessible to ploi.
So let's change that by making ploi the new owner of the mount including all subfolders:
sudo chown -R ploi:ploi /mnt/volume-nbg1-1
ls command on the mount again to make sure it worked, and you should get the correct output:
drwxr-xr-x 3 ploi ploi 4096 Jul 1 10:46 /mnt/volume-nbg1-1
Create a Symlink and Filesystem Disk
We could just use the mount path to store our files to, but let's make it a little prettier by adding a symlink from our projects storage folder.
ln -s /mnt/volume-nbg1-1 storage/volume in your project creates a new symlink volume inside your storage folder, which is linked to the mount. As far as Laravel is concerned it's only writing to this new folder inside storage, so we don't need to worry about the actual location of the mount.
Now that we have a storage path, we can simply add a new filesystem to the Laravel config and give it a descriptive name - I decided to stick with volume here.
You can now use this filesystem disk in your Laravel app just like you would use local or public. As always you can check out the documentation for details.
The above definition is best for internal files that you don't want anyone to access directly. If you're storing public assets (images, videos, etc.) in your volume that you want to publicly display on your website, you should create your symlink in the public folder (
ln -s /mnt/volume-nbg1-1 public/volume) and use a definition like this:
Using Your Filesystem in Statamic
If you're building your site using the Statamic CMS, there is one additional step before you can store assets in your new volume. Simply navigate to Assets in your Control Panel and either create a new container or edit an existing (empty) one.
The list of storage disks will now have an additional entry with the name you chose earlier. Once you select it and save your container, your assets will be saved to the new storage volume.
Resizing Your Volume
It's important to keep in mind that unlike cloud storage such as Amazon's S3, your volume doesn't automatically grow depending on the amount of data in it. So if you're about to exceed its capacity, you will need to manually resize the volume to keep adding data.
In your Hetzner Cloud Console you can simply navigate to the volume in question, edit it and increase the capacity however you like. Note that it is not possible to shrink a volume - in that case you will have to create a new one instead and manually move your files over.
After increasing the size of the volume, the server's filesystem isn't yet aware of the change. You can see this by running the command
df on your server and looking for the mounted volume. In my case I had increased the disk size to 50GB, but my filesystem still only knew about the old 30GB:
/dev/sdb 30832548 808868 28434432 3% /mnt/volume-nbg1-1
This can easily be fixed however, by running the command
sudo resize2fs /dev/sdb for ext4 formatted volumes or
sudo xfs_growpart /dev/sdb for xfs ones. Afterward
df should return the right size, in my case roughly 50GB:
/dev/sdb 51474912 816904 48208948 2% /mnt/volume-nbg1-1
And that's all of it. Hetzner Cloud allows you to connect up to 16 volumes to a server which can hold up to 10TB of data each, so you should never run out of space for your projects.