I am using my Raspberry Pi for a backup and torrent server, however even high quality SD cards do not survive for more than a month. Maybe I should have moved the swap file first before attempting what is described here, but in any case, a hard drive is a more reliable medium than an SD card.

So I decided to move the OS to one of the attached hard drives.

In the case of a single usb storage attached to the Pi this would require two steps:

Step 1: The boot loader

You will have to keep the bootloader on the SD card as this is the only device that the Pi recognises and checks early in the boot.

Just prepare an SD card as usual. Then open the boot partition and modify the cmdline.txt file in order to boot Linux from the hard drive instead of from the second SD card partition. The bootloader used here is sufficiently smart to enumerate the USB devices, so you just need to point to the appropriate USB device by changing the root parameter to root=/dev/sdaN where N is the appropriate partition number.

Step 2: Place a Pi distribution on the hard drive

You can just use your SD card and type on your laptop dd if=/dev/mmcblk0p2 of=/dev/sdLN where L is the letter for the appropriate external hard drive and N the appropriate partition. After that just use gparted or parted to expand the partition. And you are done.

You have to use dd and not cp, otherwise your system will boot but it would be unusable because all the user permissions will be lost.

If you wish you can skip the SD card and dd directly from the image file. The issue is that the image file contains two partitions so you will have to specify and offset in order to copy only the second partition (the Linux partition, not the bootloader partition). Use parted to find the appropriate offset:

root@debian-laptop:/home/stefan# parted 2013-12-20-wheezy-raspbian.img
GNU Parted 2.3
Using /home/stefan/2013-12-20-wheezy-raspbian.img

Welcome to GNU Parted! Type 'help' to view a list of commands.

(parted) unit
Unit?  [compact]? B

(parted) print
Model  (file)
Disk /home/stefan/2013-12-20-wheezy-raspbian.img: 2962227200B
Sector size (logical/physical): 512B/512B
Partition Table: msdos

Number  Start      End          Size         Type     File system  Flags
 1      4194304B   62914559B    58720256B    primary  fat16        lba
 2      62914560B  2962227199B  2899312640B  primary  ext4

(parted) quit

And then use dd to copy from the offset on (and keep the appropriate size):

root@debian-laptop:/home/stefan# dcfldd if=2013-12-20-wheezy-raspbian.img of=/dev/sdLN skip=62914560 count=2899312640 bs=1
2899312640 blocks (2765Mb) written.
2899312640+0 records in
2899312640+0 records out

If you do not care about the few megabytes dedicated to the boot partition you can just dd the whole image instead of cutting out only the second partition. However this will impose the use of the old-style MBR partition table and as you will see in the next section, this might be undesirable.

Step 3: Make it work with more than one USBs attached

I have more than one hard drive attached, so I need a way to distinguish them. The bootloader enumerates the drives asynchronously so the same drive will have different letter designation each time.

The solution is to address the drives by GUID (a unique identification number). However this is not supported by the old style MBR partition table that is set up by default with most partition and formating utilities. You will need to set up your hard drive to use the GUID partition table (GPT) in gparted first.

After that you repeat step 2 and copy the Linux partition to your hard drive. You have to copy only the Linux partition, because copying the whole image will overwrite the GPT table and reinstate the MBR table.

After you are done with that you need to find the GUID of your Linux partition. You can do this with gdisk:

root@raspberrypi:/home/pi# gdisk /dev/sda
GPT fdisk (gdisk) version 0.8.5

Partition table scan:
  MBR: hybrid
  BSD: not present
  APM: not present
  GPT: present

Found valid GPT with hybrid MBR; using GPT.

Command (? for help): i
Partition number (1-3): 2
Partition GUID code: 0FC63DAF-8483-4772-8E79-3D69D8477DE4 (Linux filesystem)
Partition unique GUID: 176ADF0D-357D-4C4B-ADC0-371342F444AB
First sector: 2099200 (at 1.0 GiB)
Last sector: 23070719 (at 11.0 GiB)
Partition size: 20971520 sectors (10.0 GiB)
Attribute flags: 0000000000000000
Partition name: ''

Command (? for help): q

And you use this in cmdline.txt as:

root=PARTUUID=176ADF0D-357D-4C4B-ADC0-371342F444AB

Keep in mind that the GUID is not the same number as the UUID that you would use in fstab in order to assign the same mount points for your drives. This is a separate (and quite useful, so do it) setup process.

Sources

And now the bootloader will always know which hard drive to use in order to boot Linux.