Note: This post is part of a series. Each post builds on the previous ones. If you are just trying to add one thing to an existing system that was not built following this series, then I cannot promise that these instructions will work for you, although they probably will. If you’ve started from something other than a non-NOOBS Raspbian image, then you’ll probably need to adjust for that.
Please refer to the series Introduction for a list of all the different posts in the series.
Self-Promotion: I have recorded this series as a screencast for Pluralsight:
If you have a Pluralsight subscription, please consider watching it. Reading the instructions is one thing, but watching it done demystifies the whole process.
Running the Raspberry Pi off of an SD card is simple, affordable, and very convenient. SD cards are also very small compared to hard drives, though, and they can only be written to a finite number of times. That number is ridiculously large, but it can still be used up faster than you think by things like virtual memory swap files which are written and re-written constantly.
If you plan to run this server 24/7 and actually use it, you’re probably going to want something a little more robust. My current setup is using a 120GB SSD for the system drive. I’m using an SSD because it can be powered entirely from the Pi’s USB port. I could have gone with something smaller, both storage-wise and physically by buying a small MSata drive and an adapter, but in the end it was cheaper to buy a normal 2.5″ SSD and put it in a case I already had.
This SSD has two partitions on it. The first is the “os” partition, which I left free back in the post “Adding a Hard Drive“. The second is my “data” partition, and holds information about my collection such as MiniDLNA’s database. I don’t actually put my media files or public share on the data partition anymore. My public share now lives entirely on an external RAID enclosure. In the original version of this post, I used the RAID box as the system drive, but over time I have discovered that RAIDs don’t work very well in this capacity, so I just use the RAID for my shares now. Everything that is the Raspberry Pi Home Server itself lives on the SD card and on the SSD.
Your setup doesn’t need to have as many moving parts, though. A typical Raspberry Pi Home Server setup might consist of a Raspberry Pi attached to a single, very large, hard drive. You can get 5TB external drives for under $150 these days. You only need to partition off a small space for the Pi to boot from, and the rest is yours to store most, if not all, of your stuff. This is how my CrashPi works.
Booting from a hard drive
Okay, I’ll level with you. You can’t actually boot entirely from the hard drive, at least not yet. The Raspberry Pi 3 has a USB boot mode, but it’s still considered experimental at this time and you have to do some tweaks to even make it available. You can boot mostly from the hard drive today though, and it should be easy enough to adapt this post in the future when the Pi 3’s alternate boot modes are considered finished.
When the Raspberry Pi boots up, it looks to the first partition on the SD card for instructions on what to do next. That boot partition contains just a tiny piece of the boot process. You can easily move all the stuff that comes after that first step to a different device, such as the external hard drive. All that stays behind on the SD card is a very small boot partition, everything else moves to the hard drive.
The resulting system should run faster and smoother, and you won’t be using up your SD card’s limited number of write cycles on high-frequency stuff like a virtual memory swap file.
This article would not have been possible if not for others that went before me. In particular, Ted Hale’s article at http://raspberrypihobbyist.blogspot.com/2013/07/running-from-external-hard-drive.html is pretty much the blueprint I started from. In turn, that article was based on a forum post by “Rattus” at http://www.raspberrypi.org/phpBB3/viewtopic.php?f=26&t=10914&p=129474&hilit=resizefs#p122476.
I’ve added my own bits, such as consulting the boot/cmdline.txt file rather than simply assuming the root file system’s location, using UUIDs to identify the partitions, and expanding the existing swap file rather than creating a separate swap partition.
I’ve turned these resources into a walkthrough that matches the rest of this series, but I wanted to at least point out where my source material originated.
Shut down services
We’d like as little to be going on with this partition as possible while we’re copying it. If you have another Linux system, or another Raspberry Pi handy and a USB adapter for your SD card, then your best shot is to let another system do this copy, but for this tutorial, I’m assuming you only have the one Pi available. To make sure things are as “quiet” as possible, we’re going to want to exit the desktop, shut down any of the background services we’ve installed, and do the copy from the command line.
First, let’s boot to the command line. If you’ve configured the Pi to boot to the desktop, then unfortunately there’s no option to simply exit the desktop, so we’ll need to tweak the configuration first. Use the desktop configuration tool or raspi-config to set the pi to boot to the command line.
Reboot the Pi, and connect via SSH using a tool like PuTTY, or connect the Pi to a monitor and work directly from the Pi. I’ll be using PuTTY for this demo.
Once you’re logged in, shut down any of the services you’ve installed so far. For me, that’s Samba, MiniDLNA, OpenVPN, Resilio Sync, and NUT. If you have any other services like CrashPlan or Transmission running, you’ll want to stop those too.
sudo service samba stop sudo service minidlna stop sudo service openvpn stop sudo service btsync stop sudo service nut-monitor stop sudo service nut-client stop sudo service nut-server stop sudo service nut-driver stop
We want as little activity going on as possible while we clone the OS partition.
Copy the old root partition to the new drive
The file /boot/cmdline.txt specifies the location of the root filesystem. You’ll need to edit this file later in order to boot from the hard drive, but for now, you just need to know for sure where the root filesystem currently resides. Show the cmdline file’s contents like this:
The part immediately to the right of “root=” specifies the device and partition that holds the root filesystem, and that’s the partition whose contents you need to move to the hard drive. In my case, it says “mmcblk0p2”. If you started from a raw Raspbian image, then yours probably says the same thing. I can’t guarantee that the default Raspbian image might not change in the future though, so it’s best to check.
Copy the existing root partition’s contents to the new 16GB partition (sda1) on the hard drive, changing the highlighted part to match the current root filesystem path from above.
Important: Be very careful here. Make sure that sda1 is the partition you set aside for the OS. If you configured your server differently, then double-check before continuing.
sudo dd if=/dev/mmcblk0p2 of=/dev/sda1 bs=32M conv=noerror,sync
This can run for quite a while, depending on how large your SD card is. Unfortunately, the dd command doesn’t give you any kind of feedback. In this case, no news is good news. As long as the drive appears to be busy, then keep waiting.
Here’s what that all means:
- “dd” copies things
- “if” specifies the input file, in this case an entire partitions
- “of” specifies the output file
- “bs” specifies how many bytes to copy in one chunk, here it’s 32 MB
- “conv” specifies how to convert things as they are copied
- noerror says to continue if anything goes wrong
- sync does some padding during the copy
When the copy process eventually finishes, check the target partition on the hard drive for errors.
sudo e2fsck -f /dev/sda1
Press enter at the prompts to fix any errors that it finds. You can probably expect one about the free block count, and one about the free inodes count.
Since the image that you just copied over from the SD card needs to be smaller than the partition you copied it to, you’ll want to expand it to fill up the available space.
sudo resize2fs /dev/sda1
Uniquely identifying a drive
This next section is a partial repeat from the “Adding a Hard Drive” post, but I’ll include it again for completeness, and so that you don’t have to bounce back and forth between the posts.
Warning: Because the next section involves copying around large strings of unmemorizable data, I recommend performing the steps through a remote SSH window, or from a terminal on the X desktop. The ids you’ll need to copy must be copied verbatim, letter for letter, or you’ll find yourself unable to boot, and you’ll have to start over again from your last backup.
Currently, the cmdline.txt file specifies that the root file system is on the second partition of the internal SD card (/dev/mmcblk0p2). We’d like that to say “/dev/sda1″ instead, but there’s a problem. As I mentioned earlier, we don’t have any guarantee that this particular drive will be called “sda” in the future. What we need is a way to uniquely refer to this drive no matter what letter it get assigned on any given day.
This was why we built the drive with a GUID Partition Table. Each partition on a GPT device is assigned a universally-unique identifier (UUID).
Note: The difference between GUID and UUID is not important here, they are different terms for the same thing; a very long, randomly-assigned number.
Get the partition UUID for /dev/sda1
sudo blkid /dev/sda1
The piece of information we’re interested is the “PARTUUID” value. Take a picture, write it down very carefully, or select and copy the text if you are doing this through a remote terminal connection like I am.
The new root filesystem’s partition is now uniquely identifiable, but the Raspberry Pi doesn’t know to use it. Edit the /boot/cmdline file to change where the bootloader will look for the root filesystem.
sudo nano /boot/cmdline.txt
Find the part that says “root=/dev/mmcblk0p…” and change it to “root=PARTUUID=” and whatever your Partition unique GUID was from above. Also add the string “rootdelay=5” at the end. This will give the Raspberry Pi time to discover the USB drive before it tries booting from it.
The result should look like this (sorry for the small picture):
Exit Nano, saving your changes (ctrl-x,y,enter)
Reboot (Important, do not skip this step)
Before the next set of changes will “stick”, you’ll need to reboot so that the Raspberry Pi uses the hard drive for the initial load. If you don’t reboot now, nothing you’re about to do will count, and you’ll just have to do it all again. You’ve been warned.
If everything goes well, you should find yourself back at a login, and you can continue. If something went wrong, go back to your most recent backup and try again.
Partitions and filesystems are not the same thing. Linux now knows what partition to load the OS from, and that’s great, but as things stand right now, it’s still going to mount the root filesystem from the SD card.
To complete the transition to the hard drive, you’ll need to edit the filesystem table. This file controls what gets mounted where, and in what order, and it needs to know where the root filesystem is. Take a look at the current contents.
Note: You’ll probably have an entry here for your data partition that we added earlier in the series.
That third line is the root filesystem, which you can tell by the “/” in the second column. Unfortunately, it’s still loading from the 2nd partition on the SD card (mmcblk0p2). You could change this to say “/dev/sda1”, but that would only work as long as the drive continues to get the name “sda”.
Fortunately, you can use a very similar UUID-based trick here to uniquely identify the filesystem no matter what letter the device gets. Filesystems have UUIDs too, and you can see them all with this command:
If you look carefully, you may notice a problem. both /dev/mmblk0p2 and /dev/sda1 have the same UUID. So much for being unique, right? This is because of the way we cloned the old root filesystem into a new location. It brought the whole filesystem over, including its UUID. Before you can use a unique Id to identify the drive, you’ll need to make sure it’s actually unique.
You need to give /dev/sda1 a new UUID. You can hand-assign your favorite UUID, or just let the computer pick a random one. Use the following command to assign a new UUID to the first partition on the hard drive:
sudo tune2fs /dev/sda1 -U random
Note: At the time of this edit, there seems to be an issue with the newer “Jessie” release of Raspbian, and it may prevent you from changing the UUID on the partition. If you see an error that says “The UUID may only be changed when the filesystem is unmounted.”, even though it’s NOT mounted, then you’ll need to perform one more tweak. The magic words are
sudo tune2fs -O ^uninit_bg /dev/sda1
I can’t take credit for this one at all. I just found it here. After reading the man page on tune2fs, I still don’t honestly even understand what this has to do with anything, but it does seem to do the trick, so if you get the above error, give this command a shot, then try the “-U random” command again and it should succeed.
Either way, display the device Ids again, just to be sure
Copy down the UUID value (not the PARTUUID value) for /dev/sda1. You’ll need it in a minute. Open the filesystem table in an editor.
sudo nano /etc/fstab
Change “/dev/mmcblk0p2” on the third line to “/dev/disk/by-uuid/” and the new UUID you just assigned to /dev/sda1.
The end result should look like this:
Your ids will obviously differ, but the important thing is that you have pointed the root filesystem to the hard drive. Any other mounts you’ve defined should appear here as well. Don’t worry about the columns lining up, it doesn’t matter, but I’m presenting mine to the public, so I’ve gone ahead and made it pretty
Exit Nano, saving your changes (ctrl-x,y,enter)
That’s it. you’re ready to reboot again, and this time, everything should be faster.
You’ll notice that the activity light on the Raspberry Pi will not blink much anymore. That’s because the SD card is no longer being accessed for anything other than the boot partition. The hard drive’s activity light will now blink where the Raspberry Pi’s activity light used to.
When the system has rebooted, double-check your filesystem table to make sure your changes are still there.
If you see /dev/mmcblk0p2, and no line for your data partition, it’s because you skipped that “Reboot” step above. See? I told you it was important. Redo everything in the “Mounting filesystems” section, and reboot again.
One thing you may have noticed if you’ve looked at other walkthroughs for booting from the hard drive is that they usually create a “swap” partition on the hard drive to be used as virtual memory.
If you look back at the filesystem table from the beginning of this post, though, you’ll notice that Raspbian never had a swap partition in the first place. That’s because Raspbian is set up to use swap files instead of swap partitions. Raspbian’s swap file lives at /var/swap, and since we just moved the whole root filesystem onto the hard drive, the swap file came along for the ride.
At this point, you’re already running your swap file from the hard drive, and you didn’t even have to do anything. Check it out with the “swapon” command:
sudo swapon –s
This shows the swap summary, which will tell you what swaps are in use. It should have one entry in it (/var/swap). It’s pretty small, though; only 100MB:
The partition we created for the root filesystem is 16GB (or more if you so chose). There’s plenty of space left to expand the swap file to something roomier.
Edit the swap file configuration:
sudo nano /etc/dphys-swapfile
Find the line that says “CONF_SWAPSIZE=100”. This is the size of the swap file in megabytes. Change the value to 2048, which will create a 2GB swap file, which is a more appropriate for a server running on a Raspberry Pi 2 or 3.
Close Nano, saving your work (ctrl-x,y,enter).
We’ll need to reboot one more time to get this to stick, but there’s another change to make first. If you’d like to return to booting to the desktop, now is the ideal time to set that back up using raspi-config.
Go to “Boot Options”, then “Desktop/CLI”, and set it back to “Desktop”. When you exit raspi-config, it will offer to reboot. Go ahead and let it.
Once the system has rebooted, check the swap file by typing “swapon –s” again. You should see a table similar to the first time you ran this command, only now the swap file is much larger:
You now have a Raspberry Pi that boots (mostly) from an external USB drive. It also uses this drive for its virtual memory swap file. The whole system should run more smoothly now, and you won’t have to worry about using up your SD card, if that’s the sort of thing you worry about.
Although most of the important stuff is on the hard drive, you should probably make at least one more backup of the SD card for safety.
In the next post, I’ll show you how to make backups now that the SD card isn’t being used for the root filesystem anymore.