I'm in the process of creating around 20-40 rPi's with the same software running on them.

Everything about the software running on the different Pi's are identical, except for two things:

  • The different Pi's will get different static IP's
  • They will also get different hostnames

Is there an ideal way to create a base image, and then write that base image to the SD card but with the ability to edit /etc/network/interfaces, /etc/hosts and /etc/hostname in the write process?

So far I'm using ApplePi Baker for the clone-procedure, but then I have to manually boot up the different Pi's with a screen and keyboard attached, and edit these values. This is the step I'm trying to prevent.

Ideally, I'd like a shell script that lets me do something like ./burn_image --hostname=customhost15 --ip=

…but I am not sure where I should start looking to edit these files from a preexisiting IMG. Any ideas?

  • 2
    Create the image. Mount the image and change what you need. Dismount and save the image. Or you can create a post boot script, that does stuff and deletes itself when its done. – Piotr Kula Nov 23 '16 at 12:29
  • WRT mounting and modifying images directly: raspberrypi.stackexchange.com/q/13137/5538 You could script that easily enough in order to, e.g., change a value in one particular file. – goldilocks Nov 23 '16 at 14:47
  • You could probably ssh into each one, and change it. Probably even script it. Set the default to say, and if you can connect to that you know it is not configured yet. – cybernard Feb 4 '17 at 3:48

In the same situation, I made a bash script that is automatically launched at boot, prompting for the relevant informations, doing the work and self-desactivating.

The script is launched via init.d, see /etc/init.d and /etc/init.d/skeleton for example. This way I can configure my brand new Pis via keyboard or via ssh.

Implementation may vary, in my case i only desactivate the setup script, because i may run it later for reconfiguration, but you can also suppress it, as ppumkin said.

| improve this answer | |

Assuming that you know MAC addresses of the interfaces which you intend to use on your devices.

Create a text file having the following tuple on each line:


Then edit your bash script which will be executed on startup as it reads the text file and updates the mentioned config files according to its MAC address.

For now, I don't have time to create an example bash script. I hope to share it soon.

Update #1 By the way at the very first step to easily gather the MAC addresses, just create a bash script which will be executed at startup and boot each device once:

ifconfig -a | grep eth0 | grep HWaddr >> macs.txt
shutdown -h now
| improve this answer | |

SETUP - Here is what I have done on the Pi. Download the Raspbian.zip:

wget -O Raspbian.zip https://downloads.raspberrypi.org/raspbian_latest

and unzip it:

unzip Raspbian.zip.

I renamed the image file, to save on typing:

mv 2017-04-10-raspbian-jessie.img jessie.img

Run the command sudo fdisk -l jessie.img to see the sector size 512 and start sector for each image file, 8192 and 92160.

Make directories mkdir /mnt/d1 /mnt/d2 to mount the image files in loopback mode:

sudo mount -o loop,offset="$((512 * 8192))" jessie.img /mnt/d1 and

sudo mount -o loop,offset="$((512 * 92160))" jessie.img /mnt/d2.

With these two image file mounted, there is access to the boot and root filesystems. If you wish ssh to be enabled (in Raspbian), sudo touch /mnt/d1/ssh and that will make ssh available (https://www.raspberrypi.org/documentation/remote-access/ssh/).

EDIT: It appears now that mounting two loop objects from the same image does not work. Therefore, the script will not work as written. A work around would be to loop mount the first (boot) image, create the ssh file, copy to temp file, and then umount the image file. Delete the first loop mount instruction and replace the tmp file name inplace of /mnt/d1. Also change /dev/loop1 to loop0.

Here is a draft of bash script.

# Shell script to modify image file and write the new image to /dev/sda.
# If /dev/sda is not the target, change the script, or /dev/sda will be
# overwritten, all current contents of /dev/sda will be lost.

many="$(($num + 40))"

info=$(sudo fdisk -l *.img | grep -E '.img|Sector size' || exit 1)
details=$(echo "$info" | awk '{
    if ( NR == 1 ) one_line = $2              # jessie.img
    if ( NR == 2 ) one_line = one_line " " $7 # 512
    if ( NR == 3 || NR == 4 ) one_line = one_line " " $2
    } END{ print one_line }' | tr -d ':')
image=$(echo "$details" | cut -d ' ' -f1)     # jessie.img
size=$(echo "$details" | cut -d ' ' -f2)      # 512
img1=$(echo "$details" | cut -d ' ' -f3)      # 8192
img2=$(echo "$details" | cut -d ' ' -f4)      # 92160
sudo mkdir /mnt/d1 /mnt/d2 2> /dev/null
sudo umount /mnt/d[12] 2> /dev/null
sudo mount -o loop,offset="$(( $size * $img1 ))" "$image" /mnt/d1
sudo mount -o loop,offset="$(( $size * $img2 ))" "$image" /mnt/d2

sudo tune2fs -L root /dev/loop1               # write root label
sudo touch /mnt/d1/ssh                        # enable ssh

for x in $(seq "$num" "$many");
    echo -n "Insert sd card #$x, press Enter "
    read input
    lsblk "$disk"
    echo -n "Clone $disk? (y/n) "
    read input
    [[ "$input" == "y" ]] || exit
    ls "$disk" > /dev/null || exit 1
    sudo umount "$disk"?* 2> /dev/null

    new_hostname=$(awk -v x="$x" '{
        if ( $1 == "" ) $2 = "      customhost" x
        print $0
        }' /mnt/d2/etc/hosts)
    sudo echo "$new_hostname" | sudo tee /mnt/d2/etc/hosts > /dev/null
    sudo echo "customhost$x" | sudo tee /mnt/d2/etc/hostname > /dev/null

    cat << EOF | sudo tee /mnt/d2/etc/dhcpcd.conf > /dev/null
        interface eth0
        static ip_address=10.0.0.$x/24
        static routers=
        static domain_name_servers=

    echo "It takes about 30 minutes to write images."
    sudo dd bs=4M if="$image" of="$disk"
    sudo sync; sync
    echo "Remove sd card #$x."
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.