Raspberry Pi Photo Backup

May 3, 2025

I have an old Western Digital My Passport backup disk. It has a built in SD card reader, a 1TB disk, and is powered by a battery. When on holiday, I can turn it on, put in an SD card and it'll backup all the files to the internal old fashioned spinny disk drive.

There are two problems with it - it sometimes decides not to copy the files and requires a "turn it off an on again" to fix it, and it's really slow to copy things.

As a little project, I thought I'd have a go at building a replacement with a Raspberry Pi.

My requirements are:

  • Automatically copies all files on an SD card when it's powered up, onto internal storage
  • Lots of space to back things up, I don't want to be running out
  • No need for a battery, I'm happy to plug this thing into the mains -
  • It'd be nice if it sorted the images into folders based on the date the image was taken as they were copied
  • Small as possible, it's designed for use when away on holiday

Hardware

In terms of hardware, I went with a Raspberry Pi 5, and decided on the 4GB RAM version. That's probably more than is needed, but it leaves room for running other stuff on this in the future. The choice of a Pi 5 was because of it's support for NVME. I wanted this as small as possible and with as few things as possible dangling off it, so internal storage was a must. The Argon Neo 5 case (https://thepihut.com/products/argon-neo-5-m-2-nvme-case-for-raspberry-pi-5) looked good, and it took an NVME drive in the base, including the required hardware. In terms of storage, I went for a 4TB Crucial P3 Plus drive.

I'd recommend using an SD card reader that has an activity light in it, so that you can see it's still copying. The previous Western Digital device had a light that would flash while it was still copying. It might be nice to install an LED into the pi case in the future to show when it's complete, but that's something for a future day.

Assembly

Assembly went well, though the instructions leave a little to be desired. Even though you can put two screws into the Pi 5 to hold it in place, don't - these holes are used to fasten the case together later. The case came with two ribbon cables of two different colours for the NVME drive, I'm not sure why. I chose the orange one and it seemed to work.

Operating System

I used my Mac to write Raspberry Pi OS 64 Bit Version onto a micro SD card and booted the PI from that. I then used parted to create a partition on the nvme drive and installed ext4 on it.

/etc/fstab was comfigured to add the NVME drive and the SD card reader:

proc               /proc           proc   defaults         0      0
PARTUUID=ddXXX-01  /boot/firmware  vfat   defaults         0      2
PARTUUID=ddXXX-02  /               ext4   defaults,noatime 0      1

PARTUUID=d75b8dc5-xxxx-xxxx-xxxx-xxxxxxxxxxxx /backup ext4 defaults,noexec,sync 0 2

/dev/sda1 /mount auto ro,users,noauto,noexec 0 0

# a swapfile is not a swap partition, no line here 
#   use  dphys-swapfile swap[on|off]  for that

The /backup mount has the sync option so files are not cached before writing. We'll be just powering this thing down when it's done, so don't want any half written files hanging around.

The /mount mount, where the sd card will be found, is mounted read only so we can't change it. It's also got the noauto option to make sure the pi can start without it, so we'll need to mount this from a bash script before starting the backup.

Backup Script

Next we need to create a backup script that looks for files in /mount and copies them to a structure of folders based on the date and filenames in /backup. I came up with this python script:

INPUT = '/mount' 
OUTPUT = '/backup'

EXCLUDES = (
    '.DS_Store',
    'owner.txt'
)

import os, datetime, shutil, filecmp

def copy(input_folder, output_folder):
    for (dirPath, dirNames, fileNames) in os.walk(input_folder):
        for filename in fileNames:
            if filename.endswith(EXCLUDES):
                continue
            if filename.startswith("."):
                continue
            filetime = datetime.datetime.fromtimestamp(os.path.getmtime(os.path.join(dirPath, filename)))
            if filename[0] == '_':
                prefix = filename[1:4]
            else:
                prefix = filename[0:3]
            from_file = os.path.join(dirPath, filename)
            destination_path = '%d/%02d%02d/%s/%s' % (filetime.year, filetime.month, filetime.day, prefix, filename)
            to_file = os.path.join(output_folder, destination_path)

            if os.path.exists(to_file):
                if filecmp.cmp(from_file, to_file, shallow=False):
                    continue

            print ("%s %d %02d %02d %s" % (filename, filetime.year, filetime.month, filetime.day, prefix))
            os.makedirs(os.path.dirname(to_file), exist_ok=True)
            shutil.copy2(from_file, to_file)

copy(INPUT, OUTPUT)

This file was saved to /root/copy-files.py

Run Script on Startup

When the pi is booted, we want to mount the sd card, and then run this script to copy files.

We can do this by editing /etc/rc.local

#!/bin/bash
mount /mount
python /root/copy-files.py

And then making it executable chmod a+x /etc/rc.local