Self-hosting on a Raspberry Pi

Using a RaspberryPi 4 and Docker to self-host various services

I’ve gotten into self-hosting over the last couple years and have experimented with several different approaches. This is a topic that a lot of people are interested in exploring but the barrier is still pretty high. I hope that this tutorial will help those who are technically inclined but have limited time and resources to devote to the investiment of “schooling” before experimenting.

Disclaimer These services will be exposed to the internet, caution is recommended.

Here is what you need to get started:

  • RaspberryPi 4 (a three will probably work if you have fewer containers)
  • Ubuntu 20.04 image
  • Understanind how to adjust settings on your router
  • Dynamic DNS service (I use a paid NO-IP subscription, free versions and other providers are available but not discussed in this tutorial)
  • Knowledge of ssh
  • Have a general understanding of Docker (not how to use it, just what it is)
  • You know the difference between dynamic and static IPs, can port forward and setup dynamic DNS on your own

Setting up the Pi

Once the image is written, you need to add a blank file called ssh to the boot partition. There should be no extension to the file!

I HIGHLY RECOMMEND YOU SETUP A STATIC IP AS SOON AS POSSIBLE. You’ll need to know the MAC address ahead of time or do it as soon as the pi is on your network.

Now, you can boot the pi and login using the default username and password of ubuntu. I recommend using ssh for the process rather than a dedicated screen since this machine will be running headless.

Perform the necessary updates by running apt update && apt upgrade -y and once that has finished, it will be time to install docker and docker-compose.

Docker and Docker-Compose

To install docker, run the command apt install docker.io and then when complete, run apt install docker-compose

Once these have finished, you need to add your user to the docker group so that you don’t constantly have to use sudo with every command. Source Documentation

  • sudo groupadd docker
  • sudo usermod -aG docker ubuntu

Log out and back in for the effects to take place. Make sure you replace “ubuntu” with your current username if you changed the default.

Now, test to make sure that everything has installed correctly up to this point by running the a simple docker image:

  • docker run hello-world

If this runs, then you know you are in good shape up to this point.

Getting Docker Containers from a reliable source

I personally try to use containers and images from Linuxserver.io
because they support multiple archituctures and have excellent documentation. Docker Hub is another possible place to grab images from, but will not be used in this guide.

Quoted from: Source
“Before you create your container, first create a directory on the host machine that will act as the home for your persisted data. We recommend creating the directory /opt/appdata. Under this tree, you can create a single configuration directory for each of your containers.”

To do this, run:
mkdir /opt/appdata
This will create a folder where all the future container information will be stored. Now, before we go too crazy adding services, we are going to setup a reverse proxy so that once things get exposed to the internet, you’ll be “safer” (I am not a sercurity expert). What this basically means is that instead of having multiple ports exposed for each service, all information will move through only two ports, 80 and 443.

There are lots of options for reverse proxy configurations. People use services like Caddy, Ngnix and Traeffic but for this guide, I will be using a variation of Nginx called Nginx Proxy Manager. This is a good approach for starters because it offers a web GUI to setup your proxies.

Before we set up the docker instance for this service, make sure you are in your home directory
cd ~/

Now run the following command:

nano docker-compose.yaml

You can replace nano with whatever you normally use.

Inside of the editor, you can copy and paste my compose file. If you are using nano, to paste use ctrl+shift+v.

version: '3'
services:
  app:
    image: 'jc21/nginx-proxy-manager:latest'
    ports:
      - '80:80'
      - '81:81'
      - '443:443'
    volumes:
      - ./config.json:/app/config/production.json
      - ./data:/data
      - ./letsencrypt:/etc/letsencrypt
  db:
    image: 'yobasystems/alpine-mariadb'
    environment:
      MYSQL_ROOT_PASSWORD: 'CHANGE ME'
      MYSQL_DATABASE: 'npm'
      MYSQL_USER: 'CHANGE ME'
      MYSQL_PASSWORD: 'CHANGE ME'
    volumes:
      - ./data/mysql:/var/lib/mysql

Make sure you change the appropriate variables in the db section. It should be noted that the database image used is different from the source website because there are some bugs on the Pi. This replacement came from scowering forums and blog posts and finding one that worked.

Once that has been modified, you can exit and save your changes by using ctrl+x and hitting Y and hitting enter.

Now you need to create a json file for the container to reference.
nano config.json

Copy and paste the following into nano:

{
  "database": {
    "engine": "mysql",
    "host": "db",
    "name": "npm",
    "user": "REPLACE WITH USER FROM PREVIOUS FILE",
    "password": "REPLACE WITH PASSWORD FROM PREVIOUS FILE",
    "port": 3306
  }
}

Now that both files are up and running, you can bring up the containers and download the images by using the following command:
docker-compose up -d

This will pull down all the needed data and configure everything on the backend. When it is all complete, you should be able to go to the IP of the machine in a web browswer and see the GUI for the reverse proxy.

  • example: 192.168.1.500:81

You should login with the default username and password and change them.

Email:    admin@example.com
Password: changeme

Networking

Setup port forwarding on your router so that ports 80 and 443 point to your machine. Also, if your router has the built in DyDDNS function, point it towards your no-ip account. You may need to install the no-ip software on your machine to update the public ip.

Once you have done this, you should be able to visit your domain and see the default Nginx landing page.

Add Codimd

Open your docker-compose.yaml file in your editor and paste the following into it:

  codimd:
    image: linuxserver/codimd:latest
    container_name: codimd
    restart: always
    depends_on:
      - mariadb
    volumes:
      - /opt/appdata/codimd:/config
    environment:
      - DB_HOST=mariadb
      - DB_USER=codimd
      - DB_PASS=CHANGE ME
      - DB_NAME=codimd
      - DB_PORT=3306
      - PGID=1000
      - PUID=1000
      - TZ=America/Chicago
    ports:
      - 3000:3000

Save and exit the editor and then, run docker-compose up -d codimd to get codimd onto your machine.
You can access it by using your IP and the port number right away.

  • example: 192.168.1.500:3000

To be able to access it from the web, you need to setup a proxy host in the Nginx web GUI. That will be covered in more detail in another post.

Hope this was helpful!!

2 Likes