I think the Raspberry Pi’s are a guilty pleasure for many developers. We will use
docker-compose to begin setting the Pi up.
Conceptually, we will split the software we intend to install in two major groups:
- Applications, which are to be installed on the Pi and are meant to be a base for everything and help us run services easy;
- Services, which are the software which actually solves what we want from our Pi.
The first step is to setup the Raspberry Pi’s operating system. We intend to use this as a server, so we don’t care to have a desktop environment installed. A Debian server version from the list of default options will do just fine. And the main user of the system will be
pi, of course 🙂
At the first login, we need to update the packages and install git, as it’s one of the most used tools for a developer:
sudo apt update
sudo apt upgrade
sudo apt install git
As far as security goes, at the moment we will keep the Pi inside the local network, we will not do much at the moment. The main thing will be to avoid using the root user for local commands or in docker.
Apart from that, we will enable auto updates (we can skip this if we want to setup webmin later):
sudo apt install unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades
sudo systemctl enable unattended-upgrades
sudo systemctl start unattended-upgrades
We want a pretty interface, so the first thing we want to do is install
zsh, which is a more customizable shell, together with
More information here:
Of course I see the usefulness of the command line! However, I am not a die hard fan of doing things the complicated way, thus I prefer to have an interface for managing certain aspects of the Pi.
curl -o setup-repos.sh https://raw.githubusercontent.com/webmin/webmin/master/setup-repos.sh
chmod +x ./setup-repos.sh
sudo apt-get install webmin --install-recommends
apt-get install libsocket6-perl
Once installed, you will be able to access webmin on port
10000. The first and most important thing to do is to change this port to some other value, by going to
Webmin > Webmin Configuration > Ports and Addresses.
An interesting alternative is Cockpit, but I think webmin is the more mature product of the two.
udevil and smartmontools
With using a Debian system as a base, especially the server version, we might need some help managing the usb drives. Here is where
udevil comes in handy.
sudo apt install udevil
smartmontools is a handy tool for monitoring the health and performance of your storage devices to ensure data integrity and prevent data loss due to hardware failures.
sudo apt install smartmontools
We are interested in knowing the performance of our Pi. For this we can use the
htop utility, or we can install something more custom, like btop:
sudo apt install btop
Docker is the tool at the root of our configuration. We will use it as a base for any service we will run on the Pi. To install it on debian, we will run the following commands:
# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
# Add the repository to Apt sources:
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
At this point, the next thing to do is actually install docker on the Pi:
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
The last thing to do is to solve the permission issues when you run the
docker command without sudo:
sudo groupadd docker
sudo usermod -aG docker $USER
A quick way to get a visual representation for the status of your containers, you can use
lazydocker in your terminal. For some weird reason, I could not set the
DIR variable properly, so I just let the script install where it does by default and then moved and cleaned up:
curl https://raw.githubusercontent.com/jesseduffield/lazydocker/master/scripts/install_update_linux.sh | bash
chmod +x ~/.local/bin/lazydocker
sudo mv ~/.local/bin/lazydocker /usr/local/bin
rm -Rf ~/.local
We will use
samba to share files across the network. To install it, run:
sudo apt-get install samba samba-common-bin
sudo mkdir -m 1777 /share
sudo nano /etc/samba/smb.conf
Add these lines in the samba config file:
Comment = Pi shared folder
Path = /share
Browseable = yes
Writeable = Yes
only guest = no
create mask = 0777
directory mask = 0777
Public = yes
Guest ok = yes
force user = pi
The last thing to do is to restart the samba server:
sudo systemctl restart smbd
We will have at least one usb drive connected to the Pi, so we will use
autofs to mount it. It will generally be always connected, so
fstab would have worked as well. But for these few situations when it’s not there,
autofs seems like the better choice.
sudo apt install autofs
sudo blkid#used to show the list of available devices and their UUID
sudo nano /etc/auto.master
- add the following line:
/home/pi/media /etc/auto.usb --timeout=60 --ghost
sudo nano /etc/auto.usb
- add the following line:
drivename -fstype=auto,uid=pi,gid=pi,rw UUID="111CCC222DDD"
sudo systemctl restart autofs.service
We will use /home/pi/media to mount the device. It’s a yolo approach taken especially because the file will be owned by this user. It helps that we will mainly have only one user on the device. Using
/media, a special user and better permissions would be a cleaner approach.
As far as the services go, the aim is to show the more important ones. The main thing we will do is create a folder for all our services:
If done so and considering that our username is
pi, the absolute path for this folder will be
/home/pi/docker-services. Should we want to keep track of the changes here, we can also create a git repository.
One thing we must pay attention to is using the tag
latest in our docker compose configs. If used, it will keep us close to the bleeding edge as far as versions go, but it might lead to compatibility issues with the data we have in our volumes.
Another thing that we already mentioned is that some containers run with elevated privileges. Since we don’t usually use
root, we might end up having some permission issues. One fix is to update the owner of the files:
sudo chown -R pi:pi /home/pi/docker-services
The first thing we want to install is a helper to use when managing the containers we intend to use. Classic choices here are Portainer and Yacht. They are both great and complex tools, which will probably solve anything you might throw at them. A solution with a very-very nice interface is CasaOS, but this seems to bring too much pretty to a developer’s server. 🙂
However, this is a small homeserver, so we don’t need too much fancy stuff in this matter. Thus, the choice for Dockge, which is rather simple and comes with the clear advantage of easily storing the configuration files on your local filesystem.
mkdir -p /home/pi/docker-services/dockge
docker compose up -d
When running many services on a home server, it’s nice to have a dashboard to see all of them, instead of remembering all the ports and addresses. My choice for this is
homarr, as it seems to be quite a mature project. Some alternatives are Heimdall, Dashy, Flame, Homer.
To install, run:
mdkir -p /home/pi/docker-services/homarr
docker compose up -d
The content of compose YAML is:
- /var/run/docker.sock:/var/run/docker.sock # Optional, only if you want docker integration
To protect ourselves from unwanted ads, we can use Pi Hole as a DNS provider, to block the ads at that level as well.
# More info at https://github.com/pi-hole/docker-pi-hole/
To enhance the default list, we can look in Github (https://github.com/topics/pi-hole?o=desc&s=stars) to find popular repositories managing lists of ads providers. The ones I have used are at: