Setting Up Your Raspberry Pi as a Home Server

Step-by-step setup of the Raspberry Pi as proxy manager with firewall and adblocker.

Setting Up Your Raspberry Pi as a Home Server-heroimage

CAUTION

Please note that initially, I wrote this blog post in German. This translation is for your convenience. Although every effort has been made to ensure accuracy, there may be translation errors. I apologize for any discrepancies or misunderstandings resulting from the translation. I am grateful for any corrections in the comments or via mail.

A Raspberry Pi is a good choice for a home server due to its low cost, energy efficiency, and versatility. In this guide, we’ll transform a Raspberry Pi into a home server that manages network traffic, blocks ads, and provides a custom homepage for easy access to your favorite services.

Requirements

Before we begin, make sure you have the following:

  • Raspberry Pi or similar device
  • Storage medium (e.g., a USB stick or microSD card)
  • Power supply
  • Network cable
  • Computer with SSH client

You don’t necessarily need a Raspberry Pi for a proxy manager or ad blocker; in principle, it should work with any device Docker can be installed on.

A Raspberry Pi has the advantage that it is inexpensive and consumes relatively little power. A Raspberry Pi 3b+ requires just under 2 watts in idle mode, a Raspberry Pi 4b just under 3 watts. Under load, the values increase to approx. 5-6 watts (3b+) and 6-7 watts (4b) respectively.

Enter data here to calculate the electricity costs:

Ergebnis



Installing the Operating System

First, an operating system must be installed on the Raspberry Pi. A USB stick (or USB hard disk) or a microSD card can be used as a storage medium. With an older model than the Raspberry Pi 4b, limitations may need to be taken into account. Otherwise, just make sure that the power supply is sufficient.

I use a Crucial SSD with 128 GB of storage space packed in an external housing.

External SSD as system hard disk

It used to work in another device and is therefore not displayed when I want to install an operating system later on. If you also use Windows and have the same problem, open the administration via the control panel.

System Management

There we are interested in the link to “Computer Management”. The menu item “Disk Management” is on the left side.

Computer management

I want to use the disk for my Raspberry Pi, so I delete the individual volumes first.

Delete volume

Volume deleted

Now follows the download of the Raspberry Pi Imager and its installation. The download button can be found on this page.

The official Raspberry Pi OS (formerly Raspbian) has three versions: Lite, Desktop (standard), and Full. The Lite version has no graphical user interface and fewer pre-installed programs. In return, it uses less disk space and memory. In addition to the desktop, Full also contains other programs that may be useful. Unfortunately, the 64-bit version is currently only available as a beta. Raspberry Pi OS is also based on Debian.

Should you necessarily use a 64-bit operating system with more than 3 GB of RAM? No, because the system can continue to use the entire RAM.

Our default operating system image uses a 32-bit LPAE kernel and a 32-bit userland. This allows multiple processes to share all 8GB of memory, subject to the restriction that no single process can use more than 3GB

I choose the Lite version of the Raspberry Pi OS, because an interface is not needed.

Raspberry Pi Imager

I select the hard disk I deleted as the “SD card” and press “Write”. Once the write process is complete, I remove and reconnect the hard disk. I see it in the data explorer with the label “system-boot”.

Directory after installation

A drive letter must be assigned to the system-boot partition if the drive is not visible.

Assign drive letter

After I switch on the display for file name extensions via the view tab, I create a new file.

filename extension

This file must be named ssh.

SSH text file

The file extension must be deleted, it is not a text file.

SSH file

The installation is now complete.

Connecting the Raspberry Pi to the network

This guide uses a Raspberry Pi 4b with 8 GB RAM.

Raspberry Pi 4

I connect the drive I just flashed to the Raspberry’s USB3 port and use a network cable to connect it to my router or a switch. Now I need the IP address of the Raspberry. To do this, I open the terminal (alternatively PowerShell).

Windows Terminal

I want to use this to find out the gateway address.

ipconfig

In my case, the default gateway should now be “192.168.0.1”. This address must be called up in the browser to reach the router under normal circumstances. After you have logged in there, you will see a list of the devices in the network. The new Raspberry Pi should also be on the list.

IP address of the Raspberry Pi

In my case, it has been assigned the address 192.168.0.47.

Vodafone station problem

My Raspberry Pi is no longer assigned an IP address on the Vodafone station after I reinstalled it. I suspect it has something to do with the Vodafone Station as a DHCP server. I have often read about the problem of Raspberry Pis without an IP address, but I have not found a simple solution.

Fixed IP assignment

It is advisable to assign a permanent IP address in the router settings if this is possible.

In a Fritz!Box clicking on the device’s details is possible in the Home Network/Network/Network Settings menu.

Fixed IP address assignment](@assets/images/blog/setup-raspberry/images/feste-ip.png)

After the IP address is known, it is time to set up the system.

Set up Raspberry Pi OS

Suppose you are not using a Raspberry Pi OS. In that case, most instructions can also be transferred to Ubuntu or Debian.

Connect

First, we need to connect to the Raspberry Pi. The login data for the Raspberry Pi are username: pi and password: raspberry. This is also the reason why ssh is disabled by default. The username and password are not secure. Open a terminal to log in:

ssh pi@192.168.0.47
yes
raspberry

Update

Update your system:

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

sudo apt update

Confirm with Y if necessary.

Password

Change the default password:

passwd
raspberry
newPassword
newPassword

The new password should be more secure.

Time setting

Now we set the correct time if required.

date

If the correct time is not displayed:

tzselect

You can select your time zone using the numbers.

sudo timedatectl set-timezone 'Europe/Berlin'

Monitoring

With the command htop, we can see the system load.

htop

raspberry pi os

Ubuntu requires slightly more RAM without further installations

This makes it easy to see how much the CPU and RAM are used. Press F10 or CTRL + C to exit the view. However, not only the load but also the temperature is essential.

cat /sys/class/thermal/thermal_zone0/temp

This displays the temperature in degrees Celsius * 100.

55.5 °C - warm, but I have no cooling

Git

Next, we need git.

sudo apt install git

Host Name

We can also rename our Raspberry.

sudo nano /etc/hostname

hostname

The file is exited in Nano with Ctrl + X. Nano will ask if you want to save the changes (confirm with Y) and what the file should be called. Since we want to overwrite it, we do not change the name and simply confirm with Enter.

Docker

Now we need Docker.

curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
rm get-docker.sh

Be careful with curl, as you could download something dangerous The command sudo docker version should now output the version of Docker.

Next, we need Docker-Compose. Under this link we can see which version is the latest. Unfortunately, we would get an older version via apt install docker-compose. That’s why we use pip.

sudo apt install -y python3-pip libffi-dev
sudo pip3 install docker-compose

This may take a while. If I now enter docker-compose, an error appears because the program cannot be found.

Docker-Compose error

So I create a link to the real location and give execute permissions for security.

sudo chmod +x /usr/local/bin/docker-compose
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose

So I have the latest version of Docker-Compose at this time

Next, we need a user group for Docker (which may already be created) and add our user account.

sudo groupadd docker
sudo usermod -aG docker pi
newgrp docker
# wait

If the user is not called “pi”, the command must be changed. When entering id, docker should also appear in the list of groups.

Now, you can install Lazydocker, a management program for Docker in the shell.

curl https://raw.githubusercontent.com/jesseduffield/lazydocker/master/scripts/install_update_linux.sh | bash
lazydocker

You can exit the window again with q

Zsh

I also want a different shell.

sudo apt install zsh
curl -fsSL https://raw.githubusercontent.com/zimfw/install/master/install.zsh | zsh
chsh -s /usr/bin/zsh
exec bash

Now it should also automatically complete my commands for Docker.

mkdir -p ~/.zsh/completion
curl \ -L https://raw.githubusercontent.com/docker/compose/1.29.1/contrib/completion/zsh/_docker-compose \ -o ~/.zsh/completion/_docker-compose
fpath=(~/.zsh/completion $fpath)

We add autoload -Uz compinit && compinit -i to the configuration.

nano ~/.zshrc

zshrc

The server should now be restarted.

sudo reboot
### wait ###
ssh pi@192.168.0.47

ZSH Shell

Setting Up the Proxy Manager

The home folder should only be filled with hidden folders at this point. We can check this with ls -Al (ls lists the files and folders of the current directory)

Home directory

I would like to create the Docker folder.

mkdir docker
ls -l

The folder now exists I could also delete it again.

rm -r docker
# If the folder already contains files, it can be deleted with rm -rf docker
ls -l

The folder is gone But I need it, so I create it again.

mkdir docker

The Docker group needs the rights to the folder.

sudo apt install acl
sudo setfacl -Rdm g:docker:rwx ~/docker
sudo chmod -R 775 ~/docker

In contrast to my instructions with Traefik, I would instead follow the approach here that each service gets its own folder. So I need one for a proxy manager. There are several proxy managers, such as Traefik, Caddy or Nginx. I want to use Nginx as a proxy. Still, since I don’t feel like manually writing the Nginx configuration file and taking care of my Lets Encrypt certificates, I use the Nginx Proxy Manager.

cd ~/docker
mkdir nginx-proxy-manager
cd nginx-proxy-manager

By the way, commands can be completed with Tab. If I am in the home folder cd ~ I can enter cd d and then press Tab. With the previously installed extension, Docker commands are also quickly selected. If I enter docker with a space at the end and press Tab twice, a list of options appears. You can click through the last commands entered using the up arrow key.

So back to the new Nginx folder

cd ~/docker/nginx-proxy-manager

We need three more folders for persistent data.

mkdir data/mysql -p
mkdir letsencrypt

Now we create a new Docker network for NPM.

docker network create npm_net

We still need an environment file with the passwords for the database.

touch .env
% echo "DB_PWD=$(openssl rand -hex 16)" >> .env
% echo "DB_ROOT_PWD=$(openssl rand -hex 16)" >> .env

Finally, only the Docker-Compose file is missing.

nano docker-compose.yml
version: "3.9"

### Networks ###
networks:
    npm_net:
        external: true
    internal:
        external: false
        name: internal

### Services ###
services:
    nginx-proxy-manager:
        container_name: nginx-proxy-manager
        image: jc21/nginx-proxy-manager
        restart: always
        networks:
            - npm_net
            - internal
        ports:
            - "80:80"
            - "443:443"
            - "81:81"
        environment:
            DB_MYSQL_HOST: npm_db
            DB_MYSQL_PORT: 3306
            DB_MYSQL_USER: npm
            DB_MYSQL_PASSWORD: $DB_PWD
            DB_MYSQL_NAME: npm
            # DISABLE_IPV6: true
        volumes:
            - ./data:/data
            - ./letsencrypt:/etc/letsencrypt
        depends_on:
            - npm_db

    npm_db:
        container_name: npm_db
        image: yobasystems/alpine-mariadb:10.4.17-arm32v7
        # image: yobasystems/alpine-mariadb
        restart: always
        networks:
            - internal
        environment:
            MYSQL_ROOT_PASSWORD: $DB_ROOT_PWD
            MYSQL_DATABASE: npm
            MYSQL_USER: npm
            MYSQL_PASSWORD: $DB_PWD
        volumes:
            - ./data/mysql:/var/lib/mysql

The line should be commented out if IPv6 is unavailable in the network. Also, the image is only for the Raspberry Pi; if you use another system, you do not need the arm32v7 tag.

Time to test the configuration.

docker-compose up -d

Now we look at the log.

lazydocker

NPM Logs

Everything seems to be okay so far. So we can call up the NPM GUI by entering the IP address of the Raspberry Pi followed by :81 in the browser.

IP address to NPM GUI

This should take us to the login screen.

NPM login screen

The user data is as follows:

Email: admin@example.com Password: changeme

You will be asked for a new password the first time you log in. The Proxy Manager is now ready for use.

NPM Dashboard

As there are currently no other devices in the network that should be accessible from outside, we do not need to deal with this any further for the time being.

Updates can also be installed very easily.

cd ~/docker/nginx-proxy-manager
docker-compose down
docker pull jc21/nginx-proxy-manager
docker-compose up -d

It is possible to be notified about new versions on Github. I prefer this to using Watchtower, which automates the updates.

Github notification

A new release does not mean that you have to update or that the update is already on Docker.hub

Configuring the Firewall

Now we need a firewall, preferably an uncomplicated one, i.e. the uncomplicated firewall.

Install UFW:

sudo apt install ufw

Set up basic rules:

sudo ufw allow 80/tcp #this is the http port
sudo ufw allow 443/tcp #this is the https port
sudo ufw allow 22/tcp #this is the ssh port
sudo ufw logging medium
sudo ufw default deny incoming
sudo ufw enable

Now we need to restart the server for the changes to take effect.

sudo reboot

We test the firewall by trying to call {IP address from server}:81 again and are surprised that it works. This is because Docker changes the iptables when we open a port.

The best solution I have found for this is the repository chaifeng/ufw-docker.

sudo nano /etc/ufw/after.rules

We add this code to the end of this file.

# BEGIN UFW AND DOCKER
*filter
:ufw-user-forward - [0:0]
:ufw-docker-logging-deny - [0:0]
:DOCKER-USER - [0:0]
-A DOCKER-USER -j ufw-user-forward

-A DOCKER-USER -j RETURN -s 10.0.0.0/8
-A DOCKER-USER -j RETURN -s 172.16.0.0/12
-A DOCKER-USER -j RETURN -s 192.168.0.0/16

-A DOCKER-USER -p udp -m udp --sport 53 --dport 1024:65535 -j RETURN

-A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16
-A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8
-A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12
-A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 192.168.0.0/16
-A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 10.0.0.0/8
-A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 172.16.0.0/12

-A DOCKER-USER -j RETURN

-A ufw-docker-logging-deny -m limit --limit 3/min --limit-burst 10 -j LOG --log-prefix "[UFW DOCKER BLOCK] "
-A ufw-docker-logging-deny -j DROP

COMMIT
# END UFW AND DOCKER

Then we restart the Raspberry.

sudo reboot

We try to call up the GUI again and see the GUI again. Did it not work? Yes, it did in part. The code block ensures that the services can still be accessed via 192.168.0.0/16. When sharing to the Internet, access is blocked.

Error

But since 80 and 443 should really be released if necessary, we have to add the firewall.

sudo ufw route allow 80/tcp
sudo ufw route allow 443/tcp
sudo ufw reload

The firewall is now set up.

Adblocker

If possible, I would like to block advertising and malware in my network at the DNS level. The advantage is that the advertising is also blocked on the cell phone. Although there are alternatives such as Pi-Hole and AdGuard, I have opted for Blocky this time.

cd ~/docker
mkdir blocky
cd blocky
mkdir whitelists
mkdir blacklists
mkdir logs

Now we have three folders in the newly created directory. First, I create a new network and fill the Docker Compose file.

docker network create blocky_net
nano docker-compose.yml
version: "3.9"

### Networks ###
networks:
    blocky_net:
        external: true

### Services ###
services:
    blocky:
        container_name: blocky
        image: spx01/blocky
        restart: unless-stopped
        networks:
            - blocky_net
        ports:
            - "53:53/tcp"
            - "53:53/udp"
            - "4000:4000/tcp"
        environment:
            - TZ=Europe/Berlin
        volumes:
            - ./config.yml:/app/config.yml
            - logs:/logs
            - ./blacklists:/app/blacklists/
            - ./whitelists:/app/whitelists/

Now we need the config file. We take the example file instead of editing the complete file.

nano config.yml
upstream:
    externalResolvers:
        - 5.9.164.112 #digitalcourage
        - 80.241.218.68 #dismail
        - tcp-tls:fdns1.dismail.de:853
        - https://dns.digitale-gesellschaft.ch/dns-query
blocking:
    blackLists:
        suspicious:
            - https://raw.githubusercontent.com/PolishFiltersTeam/KADhosts/master/KADhosts.txt
            - https://raw.githubusercontent.com/FadeMind/hosts.extras/master/add.Spam/hosts
            - https://v.firebog.net/hosts/static/w3kbl.txt
        ads:
            - https://adaway.org/hosts.txt
            - https://v.firebog.net/hosts/AdguardDNS.txt
            - https://v.firebog.net/hosts/Admiral.txt
            - https://raw.githubusercontent.com/anudeepND/blacklist/master/adservers.txt
            - https://s3.amazonaws.com/lists.disconnect.me/simple_ad.txt
            - https://v.firebog.net/hosts/Easylist.txt
            - https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=0&mimetype=plaintext
            - https://raw.githubusercontent.com/FadeMind/hosts.extras/master/UncheckyAds/hosts
            - https://raw.githubusercontent.com/bigdargon/hostsVN/master/hosts
        tracking:
            - https://v.firebog.net/hosts/Easyprivacy.txt
            - https://v.firebog.net/hosts/Prigent-Ads.txt
            - https://raw.githubusercontent.com/FadeMind/hosts.extras/master/add.2o7Net/hosts
            - https://raw.githubusercontent.com/crazy-max/WindowsSpyBlocker/master/data/hosts/spy.txt
            - https://hostfiles.frogeye.fr/firstparty-trackers-hosts.txt
            - https://raw.githubusercontent.com/Kees1958/W3C_annual_most_used_survey_blocklist/master/TOP_EU_US_Ads_Trackers_HOST
        malicious:
            - https://raw.githubusercontent.com/DandelionSprout/adfilt/master/Alternate%20versions%20Anti-Malware%20List/AntiMalwareHosts.txt
            - https://osint.digitalside.it/Threat-Intel/lists/latestdomains.txt
            - https://s3.amazonaws.com/lists.disconnect.me/simple_malvertising.txt
            - https://v.firebog.net/hosts/Prigent-Crypto.txt
            - https://bitbucket.org/ethanr/dns-blacklists/raw/8575c9f96e5b4a1308f2f12394abd86d0927a4a0/bad_lists/Mandiant_APT1_Report_Appendix_D.txt
            - https://phishing.army/download/phishing_army_blocklist_extended.txt
            - https://gitlab.com/quidsup/notrack-blocklists/raw/master/notrack-malware.txt
            - https://v.firebog.net/hosts/Shalla-mal.txt
            - https://raw.githubusercontent.com/Spam404/lists/master/main-blacklist.txt
            - https://raw.githubusercontent.com/FadeMind/hosts.extras/master/add.Risk/hosts
            - https://urlhaus.abuse.ch/downloads/hostfile/
        coin:
            - https://zerodot1.gitlab.io/CoinBlockerLists/hosts_browser
    clientGroupsBlock:
        default:
            - suspicious
            - ads
            - tracking
            - malicious
            - coin
port: 53
httpPort: 4000
docker-compose up -d

Now you should see something under port 4000.

Blocky

The only thing left to do is change the router’s DNS server. With the Fritz!Box, this can be done under IPv4 settings. The IP address of the Raspberry Pi must be entered there as the local DNS server.

Router network settings

Furthermore, we enter the IPv4 address of the Raspberry Pi instead of the previous DNS servers under Access data and DNS server.

DNS Server

This blocks most of the advertising.

Of course, you can edit many more settings, which I won’t go into now. For the change to take effect, you must leave the network and reconnect.

What I would at least do would be to create three bookmarks in my web browser.

http://ipadressevompi:4000/api/blocking/status To see the status.

http://ipadressevompi:4000/api/blocking/enable To switch on Blocky.

http://ipadressevompi:4000/api/blocking/disable To turn Blocky off.

Creating a Custom Homepage

To test the proxy manager and run something else on Raspberry, I would like to have a dashboard that could serve as a start page in the browser. There are also several options here. I choose Homer.

cd ~/docker
mkdir homer
cd homer
mkdir assets
id

We remember the number of our user (User ID) and the group Docker (Group ID).

nano .env

UID=1000 GID=123

Your own values must be entered. Then we need a Docker-Compose file.

nano docker-compose.yml

version: "3.9"

### Networks ###
networks:
    npm_net:
        external:
            name: npm_net
    default:
        driver: bridge

### Services ###
services:
    homer:
        container_name: homer
        image: b4bz/homer
        restart: unless-stopped
        networks:
            - npm_net
        ports:
            - 8080:8080
        environment:
            - UID=$UID
            - GID=$GID
        volumes:
            - ./config.yml:/www/config.yml
            - ./assets/:/www/assets

The configuration file still needs to be included before deployment. Here, you can see what is available.

nano config.yaml
title: "Homepage"
subtitle: "Homer"
logo: "assets/logo.png"

links:
    - name: "Github"
      icon: "fab fa-github"
      url: "https://github.com/"
      target: "_blank"
    - name: "Deployn"
      icon: "fab fa-octopus-deploy"
      url: "https://deployn.de"
    - name: "Reddit"
      icon: "fab fa-reddit"
      url: "https://www.reddit.com/"

services:
    - name: "Example"
      icon: "fas fa-code-branch"
      items:
          - name: "Startpage"
          - subtitle: "Search engine"
          - url: "https://startpage.com"

Done.

docker-compose up -d

The page is now located under the IP address of the Raspberry Pi with the port number 8080. Homer Dashboard

Enabling Public Access

The last thing that needs to be added is the possibility of accessing this homepage from outside the network. To do this, a domain must point to the public IP address of the home network, as shown in the instructions for the Fritz! In the NPM GUI, only the desired subdomain must be entered and forwarded to port 8080.

Step 1: Add new proxy host

Enter subdomain, IP address and port

Request new SSL certificate

It is also possible to use a DNS challenge for a domain.

If you have saved the setting and try to access the page within your network, an error message appears because the DNS rebind protection prevents access from your own network via the external IP for security reasons. The error message usually tells you how to change this behavior.

But even if you leave your own network and try to access the subdomain, you will receive an error, this time 504 Gateway Time-out. Why? Because the firewall is working.

sudo ufw allow 8080/tcp

As soon as you send this command, the page should be accessible.

The setup is now complete 😀


This website uses cookies. These are necessary for the functionality of the website. You can find more information in the privacy policy