Pi 4, Buster and PiServer – Part II

This post is an update to my earlier post “Pi 4, Buster and PiServer” where I talked about the tweaks and adjustments I had to make to get Pi 4s working with the PiServer application. Since that post, the Buster version of the “Raspberry Pi Desktop for PC and Mac” has been released and all of the tweaks I had to make have been added to the standard version of PiServer.

This post is based heavily on our internal documentation for setting up PiServer so some parts may not apply in your setting!

I will be writing a follow-up post soon about the process we use to customise our Raspberry Pi client operating systems using PiServer.

Prepare the Virtual Server

  1. Download the latest version of Raspberry Pi Desktop
  2. Create a new virtual machine with 2 CPUs, 4 Gb RAM, 3 HDD (25 Gb – Root Disk, 30Gb – Pi OS Disk and 100 Gb – Home Disk), Network adapter connected to a separate VLAN just for your Raspberry Pi devices (We will configure PiServer to be the DHCP server for the RPi devices).
  3. Mount the ISO file Downloaded in step 1 and power on the virtual machine

Install the OS

  1. When the VM boots from the ISO, select “Graphical install” from the menu
  2. Configure the keyboard
  3. On the “Partition disks” screen select “Manual
  4. Select the 25Gb disk and click “Continue
  5. Create a new partition table on the drive
  6. Select the new partition and click “Continue
  7. Select “Create a new partition” and click “Continue
  8. Subtract 2Gb from the maximum size (we will use this 2Gb as swap) and click “Continue
  9. Select “Primary” for the partition type
  10. Select “Beginning” for the partition location
  11. Check your partition settings match this and click “Done setting up the partition
  12. Now select the 2GB free space at the end of your 25Gb disk
  13. Select “Create a new partition
  14. Accept the “2.0 GB” size on the next screen and select “Primary” for the partition type. On the partition settings screen, change the “Use as:” to “swap area” and then select “Done setting up the partition
  15. Now double click the 100 Gb drive, create a new empty partition table on the drive. Then select the new partition
  16. Select “Create a new partition”, leave the size to the default of the maximum space (or type max), set the partition type as “Primary” and on the “Partition settings” screen change the “Reserved blocks” to “0%”, ensuring the mount point is “/home”. Click “Done setting up the partition” when it matches the screenshot below
  17. Your “Partition overview” should match the screenshot below. Click “Finish partitioning and write changes to disk” when it does
  18. Select “Yes” to write the changes to disk and click “Continue
  19. The system will now be installed…
  20. When prompted if you should install the GRUB boot loader to MBR, select “Yes
  21. Select “/dev/sda” as the disk to install GRUB to
  22. OS installation is now complete. Select “Continue” to restart the VM
  23. You now need to complete the Raspberry Pi First Run. Click “Next
  24. Set the locale settings and click “Next
  25. Skip the password change by clicking “Next” – We will do this later!
  26. Press “Skip” to continue without checking for updates – We will do this later!
  27. Click “Done” to complete the “Welcome to Raspberry Pi” set up

Set a Static IP Address

As the VLAN that the server is installed in has no DHCP running (it will be the DHCP server), we need to configure a static IP Address. Open the terminal…

sudo cp /etc/dhcpcd.conf /etc/dhcpcd.conf.orig
sudo nano /etc/dhcpcd.conf

Add this to the end of the file:

interface eth0
static ip_address=<IP Address for your PiServer>
static routers=<Gateway of your VLAN subnet>
static domain_name_servers=<Space separated IP addresses of DNS servers>
static domain_search=<Internal Domain Name>

Save and close the file in Nano.
Flush the old IP settings:

sudo ip addr flush dev eth0

Restart dhcpcd service:

sudo systemctl restart dhcpcd.service

Check you can ping your DNS servers and a named server on the network:

ping -c 3 <IP address of DNS Server>
ping -c 3 <Short domain name of DNS server>

Update APT Sources

We run an Apt Cache server to speed up Apt downloads for our Linux servers. I recommend you do the same! There’s a post here that talks about this

Open the terminal and edit the Apt sources files replacing “<FQDN of your Apt Cache server>” with the fully qualified domain name of your Apt cache server.

sudo sed -i 's+http://+http://<FQDN of your Apt Cache server>:3142/+g' /etc/apt/sources.list
sudo sed -i 's+http://+http://<FQDN of your Apt Cache server>:3142/+g' /etc/apt/sources.list.d/raspi.list

Update and upgrade through Apt:

sudo apt update && sudo apt dist-upgrade -y

Enable SSH

Still in the terminal, enable SSH:

sudo raspi-config
  • Select “3 Interface Options”
  • Select “P2 SSH Enable/disable …”
  • Select “Yes” to enable SSH
  • Select “OK” to exit the SSH configuration

Change the Hostname

  • Select “1 System Options”
  • Select “S4 Hostname”
  • Select “OK” to the message about hostname rules, and enter your hostname and then select “OK”
  • Select “<Finish>” to exit raspi-config
  • Select “<No>” to reboot later

Install Common packages

Still in the terminal install commonly used packages, and reboot.

sudo apt install molly-guard ntpdate mlocate open-vm-tools -y
sudo apt autoremove -y
sudo reboot now

After the server reboots, click “OK” to the warning about SSH being enabled and default passwords; we are about to fix that!

Change password

Generate and save a new password somewhere safe, then change the password on the server through a SSH session:

Changing password for pi.
Current password:raspberry
New password:<The new password>
Retype new password:<The new password>
passwd: password updated successfully

Open another SSH session and check you can login with the saved password. Exit out of the first SSH session.

Enable BASH Tab Completion

sudo nano /etc/bash.bashrc

Uncomment the “# enable bash completion in interactive shells” section:


# enable bash completion in interactive shells
if ! shopt -oq posix; then
   if [ -f /usr/share/bash-completion/bash_completion ]; then
      . /usr/share/bash-completion/bash_completion
   elif [ -f /etc/bash_completion ]; then
      . /etc/bash_completion

# if the command-not-found package is installed, use it

“Ctrl + o” followed by “Enter” to write the file, “Ctrl + x” to exit

Aptitude installs

We run an Apache webserver on our PiServer using the UserDir module to allow students to save HTML files to their home drive on their Pi and have them served by the PiServer. We also have PHP and MariaDB installed for teaching SQL to the students.

If you don’t want / need this then jump straight on to the “Configure PiServer” section.

sudo apt update
sudo apt install apache2 php php-mysql mariadb-server -y

Setup Maria DB Server

First we need to secure the Maria DB server:

sudo mysql_secure_installation
  1. Generate a password for the root user of MariaDB and save it somewhere safe!
  2. At the prompt to enter the current root password, just press Enter
  3. Enter “Y” to set a new root password
  4. Paste the new password saved in step 1 at the prompt
  5. And paste the password again
  6. Enter “Y” to remove anonymous users
  7. Enter “Y” to disallow root login remotely
  8. Enter “Y” to remove test database
  9. Enter “Y” to reload privilege tables

Next we will create an “admin” user with password based access. First create another password and save it somewhere safe!.

sudo mysql

At the MariaDB prompt enter the following:

MariaDB [(none)]> GRANT ALL ON *.* TO 'admin'@'localhost' IDENTIFIED BY '' WITH GRANT OPTION;
Query OK, 0 rows affected (0.001 sec)

Query OK, 0 rows affected (0.001 sec)

MariaDB [(none)]> exit

Now check that MariaDB is up and running:

mysqladmin -u admin -p version

You should see something like this after entering the admin password:

Enter password:
mysqladmin Ver 9.1 Distrib 10.3.27-MariaDB, for debian-linux-gnu on i686
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Server version 10.3.27-MariaDB-0+deb10u1
Protocol version 10
Connection Localhost via UNIX socket
UNIX socket /var/run/mysqld/mysqld.sock
Uptime: 18 min 57 sec

Threads: 7 Questions: 479 Slow queries: 0 Opens: 177 Flush tables: 1 Open tables: 31 Queries per second avg: 0.421

Now we need to allow connections to the database server across the network:

sudo nano /etc/mysql/mariadb.conf.d/50-server.cnf

Find the “bind-address” line and comment it out:


# Instead of skip-networking the default is now to listen only on
# localhost which is more compatible and is not less secure.
#bind-address =

# * Fine Tuning

Save and exit Nano and then restart the MariaDB service:

sudo systemctl restart mariadb

You should then be able to create databases and users on MariaDB and users should be able to remotely connect to the server using the command line and PHP.

Apache2 and PHP Config

Enable the Apache UserDir module

sudo a2enmod userdir
sudo nano /etc/apache2/mods-available/userdir.conf

Comment out the “AllowOverride” directive and add a new one below:

<IfModule mod_userdir.c>
        UserDir public_html
        UserDir disabled root

        <Directory /home/*/public_html>
                #AllowOverride FileInfo AuthConfig Limit Indexes
                AllowOverride All
                Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
                Require method GET POST OPTIONS

# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

Allow PHP to run from UserDir

sudo nano /etc/apache2/mods-available/php7.X.conf

Comment out the “<IfModule mod_userdir.c>” section:

<FilesMatch ".+\.ph(ar|p|tml)$">
    SetHandler application/x-httpd-php
<FilesMatch ".+\.phps$">
    SetHandler application/x-httpd-php-source
    # Deny access to raw php sources by default
    # To re-enable it's recommended to enable access to the files
    # only in specific virtual host or directory
    Require all denied
# Deny access to files without filename (e.g. '.php')
<FilesMatch "^\.ph(ar|p|ps|tml)$">
    Require all denied

# Running PHP scripts in user directories is disabled by default
# To re-enable PHP in user directories comment the following lines
# (from <IfModule ...> to </IfModule>.) Do NOT set it to On as it
# prevents .htaccess files from disabling it.
#<IfModule mod_userdir.c>
#    <Directory /home/*/public_html>
#        php_admin_flag engine Off
#    </Directory>

Set the global Apache2 ServerName directive

sudo nano /etc/apache2/apache2.conf

Add the ServerName directive just below the “# Global configuration” section:

# Global configuration
ServerName <FQDN of your PiServer>
# ServerRoot: The top of the directory tree under which the server's
# configuration, error, and log files are kept.

Restart Apache

sudo apache2ctl configtest

If this returns “Syntax OK” then restart Apache

sudo systemctl restart apache2

Configure PiServer

We will now run and configure the PiServer software. This needs to be completed from the desktop of the server and you will need to ensure that the server has direct Internet access through the proxy / filter.

  1. Open PiServer by clicking on the entry under the “Preferences” menu
  2. The PiServer Introduction screen will show. In short you are going to:
    1. Add at least one Raspberry Pi to the server (that is set up to network boot already)
    2. Set up user accounts
    3. Installing an Operating System
  3. You will need at least Raspberry Pi 3, 3+ or 4 set to network boot and connected to a network in the same VLAN as the PiServer for these next steps. Click “Next” to begin the PiServer setup
  4. Switch on your Raspberry Pi and then when you see its MAC address appear, click “Next”
  5. You now need to set up the “Authentication method”. Select “Authenticate users against an existing server” and enter the details as below. We have a service user created in Active Directory to perform the LDAP bind between PiServer and AD. Click “Next” to select the AD Group that can log into the RPis
  6. Scroll down the “Users must be member of group:” section until you find the “DL PiServer Users” group and then click “OK”
  7. For speed of install at this point, just select the “Raspberry Pi OS Lite” and click “Next”
  8. The PiServer setup will now download and configure all of the relevant software and files
  9. Click “Close” when the PiServer installation completes.
  10. The PiServer application will open and should list all of the users that are allowed to log into the RPis.
  11. Click the “Settings” tab in PiServer and set it to “Act as standalone DHCP server” and set the “DHCP Server settings” as shown below then click “Save”
  12. Click the “Folders” tab in PiServer and then click “Add” to create a shared folder. I tend to create one folder and then create sub-folders for each year group.
  13. Open the terminal and create a symlink to the “Shared Folders” directory on the pi user’s Desktop and then create your sub-folders
cd Desktop
ln /var/lib/piserver/os/shared/Shared\ Folders/ "Shared Folders" -s
cd Shared\ Folders
mkdir Year\ 7
mkdir Year\ 8
mkdir Year\ 9
mkdir GCSE

Add OS disk and set up mount point

We are now going to mount the additional hard drive and move the directory for client OSs to it, then set this drive to mount directly in the correct location. This will probably be easiest over SSH!

First check what your disk is called; we want the device that is not listed in the first command but is in the second!

$ df
Filesystem     1K-blocks    Used Available Use% Mounted on
udev             1003192       0   1003192   0% /dev
tmpfs             206512    5820    200692   3% /run
/dev/sda1       23705992 6908952  15569776  31% /
tmpfs            1032552       0   1032552   0% /dev/shm
tmpfs               5120       4      5116   1% /run/lock
tmpfs            1032552       0   1032552   0% /sys/fs/cgroup
/dev/sdb1      102685624  145748 102523492   1% /home
tmpfs             206508       8    206500   1% /run/user/1000

$ ls /dev/sd*
/dev/sda  /dev/sda1  /dev/sda2 /dev/sdb /dev/sdb1 /dev/sdc

Create the partition

Now we will create a primary partition in /dev/sdc, for the PiServer client OSs to be stored in.

First create a new partition and mark it as a physical volume.

sudo fdisk /dev/sdc

Welcome to fdisk (util-linux 2.33.1).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.

Device does not contain a recognized partition table.
Created a new DOS disklabel with disk identifier 0x891ae842.

Command (m for help): g
Created a new GPT disklabel (GUID: AA0D7E01-7D21-3F49-9254-D3CE4DB5B123).

Command (m for help): n
Partition number (1-128, default 1): 1
First sector (2048-209715166, default 2048): 2048
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-62914526, default 62914526): 62914526

Created a new partition 1 of type 'Linux filesystem' and of size 30 GiB.

Command (m for help): p
Disk /dev/sdc: 30 GiB, 32212254720 bytes, 62914560 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: AA0D7E01-7D21-3F49-9254-D3CE4DB5B123

Device     Start       End   Sectors  Size Type
/dev/sdc1   2048 62914526 62912479  30G Linux filesystem

Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.

We can now list our disks again and see the new partition:

ls /dev/sd*
/dev/sda  /dev/sda1  /dev/sda2  /dev/sda5  /dev/sdb  /dev/sdb1  /dev/sdc /dev/sdc1

Now we can format the volume as ext4 with 0% system reserved space:

sudo mkfs.ext4 -m 0 /dev/sdc1 -L "PiServer OS"

Mount the volume and transfer data

First we will create a directory to temporarily mount the new volume to:

sudo mkdir /mnt/temp-os

Now we will mount the new volume to its temporary directory so we can copy the existing data across:

sudo mount /dev/sdc1 /mnt/temp-os

We can now remove the default “lost+found” directory from this drive:

sudo rm -rf /mnt/temp-os/lost+found

Now we will copy all of the data from the existing client OS directory into our temp-os directory:

sudo  rsync -aH --info=progress2 /var/lib/piserver/os/ /mnt/temp-os/

When the rsync command completes, check that it has exited with a zero status:

sudo echo $?

Now we will move the directory we just copied to a backup directory (in case it all goes wrong!) and then create a new empty directory to mount our disk into:

sudo mv /var/lib/piserver/os /var/lib/piserver/os.orig
sudo mkdir /var/lib/piserver/os

Now we will unmount from our temp directory and remount in the newly made one:

cd /
sudo umount /mnt/temp-os/
sudo mount /dev/sdc1 /var/lib/piserver/os

Now we can check the local volume is mounted to the correct place:

df /dev/sdc1
Filesystem     1K-blocks    Used Available Use% Mounted on
/dev/sdc1       30831504 1242556  29572564   5% /var/lib/piserver/os

Edit fstab

Now we have copied the files over and mounted them successfully, we can edit fstab so that they mount at reboot.

First backup fstab:

sudo cp /etc/fstab /etc/fstab.orig

Now we can edit the fstab file to mount our two new drives automatically:

sudo nano /etc/fstab

We are going to edit the file to add the new mount point at the end of the file. Use tabs between items:

# /etc/fstab: static file system information.
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
# <file system> <mount point>   <type>  <options>       <dump>  <pass>
# / was on /dev/sda1 during installation
UUID=1a041e47-c807-4476-aa30-4d161dff6e82 /               ext4    errors=remount-ro 0       1
# /home was on /dev/sdb1 during installation
UUID=d4b6f347-f84f-4b8e-9e49-15a796c45af4 /home           ext4    defaults        0       2
# swap was on /dev/sda2 during installation
UUID=acba99b7-d3b8-4f45-9312-877fdc6c537d none            swap    sw              0       0
/dev/sr0        /media/cdrom0   udf,iso9660 user,noauto     0       0
# a swapfile is not a swap partition, no line here
#   use  dphys-swapfile swap[on|off]  for that

# Client OS
/dev/sdc1       /var/lib/piserver/os    ext4    defaults        0       0

Now we will check fstab works by unmounting the drive and then mounting all drives in fstab:

sudo umount /dev/sdc1
sudo mount -a

Finally we check the disks are mounted as expected:

Filesystem     1K-blocks    Used Available Use% Mounted on
udev             1003192       0   1003192   0% /dev
tmpfs             206512    8412    198100   5% /run
/dev/sda1       23705992 6909360  15569368  31% /
tmpfs            1032552       0   1032552   0% /dev/shm
tmpfs               5120       4      5116   1% /run/lock
tmpfs            1032552       0   1032552   0% /sys/fs/cgroup
/dev/sdb1      102685624  145748 102523492   1% /home
tmpfs             206508       8    206500   1% /run/user/1000
/dev/sdc1       30831504 1242556  29572564   5% /var/lib/piserver/os

Now reboot:

sudo reboot now

When the server reboots, check that you can login and run PiServer.

Add a new client OS and delete the temporary one

We are now going to download a new client OS and remove the temporary one we copied over to make sure that we are using complete copies of the client OS and we are ready to begin cloning and editing them.

Download a new client OS

In the PiServer application, click on the “Software tab” and then click the “Add” button. Select the “Raspberry Pi OS Full” and click “Next

Remove the temporary OS

When the download completes click “Close” and then switch to the “Clients” tab and highlight your Raspberry Pi that you added earlier. Click “Edit” and change the “Operating system” drop-down to “Raspberry Pi OS Full” and click “OK

You can now return to the “Software” tab, highlight the “Raspberry PI OS Lite”, click “Remove” and confirm with “Yes” to delete it from your system.

Check everything works and finish

Now reboot the Raspberry Pi you added to PiServer earlier and check you can login using your Windows network credentials.

You are now ready to customise your Client OS and add more Raspberry Pis to your PiServer install!

1 thought on “Pi 4, Buster and PiServer – Part II

Leave a Reply

Your email address will not be published. Required fields are marked *