Victor is a full stack software engineer who loves travelling and building things. Most recently created Ewolo, a cross-platform workout logger.
Setting up a raspberry pi 4 home server

With the release of the raspberry pi 4, which features a 1.5 Ghz processor, 1-4 Gb RAM, built-in networking and USB 3.0 ports, I decided to finally take the plunge and set up a home server with the pi. In this post we will go through setting up and configuring the pi to do 3 main things:

  1. function as a media center on the local network using the tv as the only display
  2. run an internet connected CI service using jenkins
  3. run an internet connected file-sharing service using nextcloud

A couple of notes before we begin: since I was a pi noob, I went ahead and bought the raspberry pi 4 desktop kit which came with a 16 Gb SD-card pre-formatted with NOOBs (the os installer for the pi), a keyboard, a mouse, hdmi cables, a book on getting started with the pi and of course, the pi itself with 4Gb of RAM in a case. As mentioned above, the display for the pi was a tv via HDMI.

Initial setup

I was not aware of this when I bought the pi, but it is actually an ARM processor running a 32-bit operating system. It comes with Raspbian, a Debian-based operating system with a LXDE-based desktop called PIXEL. It is also possible to install other distributions like Ubuntu Server, Ubuntu MATE, but not all distributions had been certified for the pi 4 at the time of writing so I decided to keep it simple and stick with Raspbian. If you're like me and are completely new to the pi, connect the peripherals first and power up the pi by simply plugging it in. Follow the white rabbit and setup Raspbian via the wizard.

One of the first issues that popped up was the fact that the debian distribution that Raspbian is based on changed from testing to stable and thus the sources needed to be updated accordingly: sudo apt-get update --allow-releaseinfo-change. Once the os has been configured and all software updated, we begin by installing some basic utilities: sudo apt install tmux vim.

The next thing to do is to enable SSH via the Raspberry configuration. Once ssh has been setup, we will install fail2ban since we will be exposing the pi to the internet.

Finally, we will also install and configure conky which displays a plethora of system information on the desktop:

$ sudo apt-get install conky -y
$ wget -O /home/pi/.conkyrc
$ sudo vim /usr/bin/

(sleep 4s && conky) &
exit 0

$ sudo vim /etc/xdg/autostart/conky.desktop

[Desktop Entry]
Exec=sh /usr/bin/
Comment=system monitoring tool.

Read on for the various services that we can setup on the pi.

Internet access

Take a note of your lan ip (e.g. Install nginx via: sudo apt install nginx and head to your router settings and port forward 80, 443, 22 and any other ports that you might want. I just went with opening all ports from 4001-9999. Test whether everything is working by trying to hit the nginx default home page via http://your-public-ip. You can find out your public ip address via

Since your public ip is assigned via your ISP and might change once in a while, it is recommended to setup a dynamic dns which basically means that you get a domain which always points to your machine despite a changing ip address. This is accomplished by running some sort of a script from your pi which will update the ip address on the DNS server. There are a few free services out there which provided dynamic DNS, no-ip being one of them. Follow the instructions here for setting up the no-ip dynamic update client. Note that if you would like to have the client also start on boot add the binary (/usr/local/bin/noip2) to /etc/rc.local right before the exit 0 statement.

In my case, I already had a domain from namecheap and I was very happy to discover that they also provide dynamic dns entries. I settled for having a subdomain like as a pointer to my pi. I also added a wildcard * so that I can run separate services like, etc. Note that namecheap provides a really simple way of keeping the public ip address updated via a http request:$DOMAIN&password=$PASS&ip=$IP. Someone created a bash script that can be run every once in a while to update the namecheap dynamic dns records: namecheap-ddns-update. I set up a cron job to run this script once a day:

1 1 */1 * * NC_DDNS_PASS=xxx /home/pi/namecheap-ddns-update/namecheap-ddns-update -e -d -s 'home,*.home' > /home/pi/cron-logs/nc-ddns.log 2>&1

Don't forget to create the ~/cron-logs folder. Once the dns has been setup, the pi should be accessible from the internet ( and we can continue with setting up further services.

Media center / file server

Since the SD card that came with the desktop kit was only 16 Gb, I decided to buy an external HDD to store media. I went with a simple 1 Terabyte Western Digital HDD rather than a SSD. This saved me a bit of money on the disk at the expense of a bit extra voltage. I setup an ext4 filesystem on the external drive and copied media such as videos and music under a /share folder. Plugging in the HDD to the pi (be sure to use the USB 3.0 port) will mount the drive under /media/pi/<disk-name>.

Setting up a file server on the pi is pretty straight-forward. Note that the samba service can be restarted with: sudo service smbd restart.

Once the file server is setup, we can proceed with installing media center software. There are a few choices but I decided to go with Kodi which comes with remote control apps for both Android and iOS. Installing kodi is as simple as sudo apt install kodi and it comes with some pretty sane defaults. What is amazing is that if you use an external drive then you can simply play videos via the "Videos" interface option. To run kodi simply type kodi in a terminal. In order to enable a smartphone based remote control follow the instructions here. If you've been following the instructions in this article to the letter, you probably don't want to allow access to kodi via the internet. To this effect, change the kodi http port to something above 10000 and ensure that you have a decently strong password configured!

Note that instead of a fully blown media center, you can also play videos from the command line using omxplayer, e.g.: omxplayer -o hdmi filename.mp4

Continuous integration with Jenkins

Before we start installing jenkins, we will first install docker:

$ sudo apt-get update
$ sudo apt-get install apt-transport-https ca-certificates software-properties-common
$ curl -fsSL | sudo apt-key add -
$ sudo echo "deb stretch stable" > /etc/apt/sources.list.d/docker.list
$ sudo apt install --no-install-recommends docker-ce
$ sudo apt install docker-compose
$ sudo usermod -aG docker pi

$ sudo systemctl enable docker.service
$ sudo systemctl start docker.service

As you can see, installing docker is not as straight-forward unfortunately. The first thing that I tried was: curl -fsSL -o && sh and this failed due to a bad version specified in the sources. After fixing the sources file to use debian stretch, I tried installing docker without recommended packages and it still failed due to a bad aufs-dkms installation. The solution in my case was to sudo apt purge aufs-dkms before proceeding with the docker installation.

With docker out of the way, lets install java:

$ sudo apt install openjdk-8-jdk
$ sudo update-java-alternatives -l
$ sudo update-java-alternatives -s java-1.8.0-openjdk-armhf

We are now ready to install Jenkins: sudo apt install jenkins

Before we proceed with configuring jenkins, we will setup a virtual host on nginx to forward all requests on to jenkins:

server {

  server_name ci.home.*;
  index index.html index.htm;

  location / {
    proxy_pass          http://localhost:8080;
    proxy_set_header    Host             $host;
    proxy_set_header    X-Real-IP        $remote_addr;
    proxy_set_header    X-Forwarded-For  $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-NginX-Proxy true;

    proxy_redirect off;

    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";


Don't forget to add jenkins to the docker group: sudo usermod -aG docker jenkins and reboot your machine in order to allow jenkins to run pipeline builds. Thus, our ci service is now reachable via the internet and to secure it further we can get SSL certificates via letsencrypt:

$ sudo apt install certbot
$ sudo apt-get install -y python-certbot-nginx
$ sudo certbot --nginx -d
$ sudo certbot --nginx -d
Database (Mariadb / MySQL)

Note that MySQL is not available for the pi, we need to use Mariadb instead:

$ sudo apt install mariadb-server
$ sudo mysql_secure_installation
$ sudo mysql -u root

CREATE USER 'exampleuser'@'localhost' IDENTIFIED BY 'xxx';
GRANT ALL PRIVILEGES ON exampledb.* TO 'exampleuser'@'localhost';
NAT loopback or why can I only access my PI from outside the LAN?

If it so happens that the pi is accessible with the public ip address / domain from outside your home but not from within the LAN then most likely your router does not support NAT loopback. This means that the router cannot find the pi via the public ip, it requires the local ip of the pi (e.g.

If your client machine is always connected to the pi's local network, you can simply fix the DNS resolution of the domain to resolve to the local LAN address of the pi. In Ubuntu, this can be done by adding an entry the /etc/hosts file. However, a more elegant solution is to setup a DNS server on the local network and let all client devices use this local DNS server. This DNS server can be configured to direct any queries for the pi's domain to it's local LAN ip address.

We will thus install dnsmasq, a very simple and lightweight DNS server on the pi itself:

$ sudo apt-get update
$ sudo apt install dnsmasq dnsutils
$ sudo service dnsmasq stop
$ sudo vim /etc/dnsmasq.conf

# uncomment the following options
domain-needed         # Never forward plain names (without a dot or domain part)
bogus-priv            # Never forward addresses in the non-routed address spaces.

# save and quit
$ sudo service dnsmasq start
$ sudo journalctl -u dnsmasq -f

# check logs for errors
# test dns resolution on the pi returns the public ip address

$ dig

We will now configure the pi to resolve its domains to it's local LAN ip address and check that the DNS lookup now resolves correctly.

$ sudo vim /etc/hosts

$ sudo service dnsmasq restart
$ dig

Once dnsmasq is setup on the pi, configure the machines on the LAN to use the pi as a DNS server. On Ubuntu with Gnome this can be configured via the Network Manager on a per connection level. Note that a restart of the network-manager might be required for the DNS settings to take effect. Once more test that the DNS resolution for resolves to the local LAN ip address.

Once this is setup, we can continue with the Nextcloud installation.

File sharing and sync with Nextcloud

Since we are running Nginx, we will do a manual Nextcloud installation. Download and untar the nextcloud archive under /var/www/nextcloud/. Thereafter, prepare the system:

$ sudo apt install php-fpm php-ctype php-xml php-gd php-json php-mysql php-file php-bz2 php-bcmath php-apcu
$ sudo chown -R www-data:www-data /var/www/nextcloud/
$ sudo mysql -u root

CREATE USER 'username'@'localhost' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON nextcloud.* TO 'username'@'localhost' IDENTIFIED BY 'password';
FLUSH privileges;

Create the Nginx site configuration as instructed here (as is evident from the nextcloud installation location, pick "Nextcloud installed in the webroot of nginx"). Restart Nginx, navigate to your nextcloud: and setup your nextcloud instance! Note that if you choose a data directory other than /var/www/nextcloud/data, then you need to ensure that it has the correct owner via sudo chown -R www-data:www-data <data-dir>.

In my case, my router did not support NAT loopback but I managed to browse web-applications on the pi and even setup nextcloud without needing dnsmasq. Only when I setup my nextcloud client and started syncing data on my laptop, did the issue manifest itself. I noticed extremely slow upload/download speeds with the router randomly choking leading to both the pi and my laptop losing their wifi connections. Once I had the pi's DNS resolving to its local LAN address, everything worked smooth as butter :).


Please note the following issues at the time of writing:

  • While setting up the pi, I noticed that there was a constant flashing thermometer on the display. This indicates that the pi is heating up. The temperature can be checked via: vcgencmd measure_temp. Note that you can also right click the top panel and add a "CPU temperature" panel item to keep an eye on the temperature. Anyways, in my case the temperature was constantly around 85 degrees C which I found quite high given that the CPU was mostly idle. It turns out that the pi4 has a heating issue which can be somewhat mitigated via a firmware patch. The firmware update reduced the pi's temperature to 77 degrees and I also removed the top of the case which further reduced the temperature to 60 degrees. I also ended up buying a heatsink for the pi which provided another 5 degrees worth of cooling to bring the total down to 55 degrees.
  • The display on my tv is too big and at the time of writing the pi4 does not support overscan for HDMI output :(.
Update February 20, 2020
Both the temperature and overscan issues have now been fixed with the latest firmware updates! The pi now runs at a decent 50 degrees with kodi running and overscan for older HDMI tvs also works (/boot/config.txt):

# uncomment if you get no picture on HDMI for a default "safe" mode

# uncomment this if your display has a black border of unused pixels visible
# and your display can output without overscan

# uncomment the following to adjust overscan. Use positive numbers if console
# goes off screen, and negative if there is too much border

Ok well that's about it - despite the minor issues, I'm really happy to have a home server setup and I'll keep this article updated with any other services that I decide to install. Feel free to get in touch or discuss this on HN via the link below.