Goodbye Dropbox, Hello Nextcloud

Dropbox is Expensive Link to heading

I remember when I used to pay for Dropbox. It cost roughly $11.00/month for 2TB. Now don’t get me wrong, I wanted to host my entire collection of anime and some pictures. However, over time the $11.00/month started to add up and Dropbox became expensive. Plus I didn’t want my files to be on another person’s server. I wanted to take back ownership of my files and self host.

But how exactly would I be able to do just that???

Well, I had a raspberrypi 4B 8GB of RAM version and a 2TB hard drive laying around so ofcourse my initial thought would be to host my files on that. But the one cool thing that dropbox gave me was an easy UI. Plus, I did not want anyone to access my files nor would I want my raspberrypi to be attacked by hackers. I needed a level of security when accessing my raspberry pi from the outside.

Nextcloud + Tailscale Link to heading

Nextcloud is a free and open source solution for file hosting. Tailscale is a vpn service that allows you to access end devices through a “tailnet” using Wireguard. With these two services, I am able to create a file hosting service that I can access on my end device so long as its connected to the “tailnet”. Plus nextcloud has an iPhone application that I can use to access my files from the outside.

Setup Link to heading

TLDR: I’m using docker-compose to set up nextcloud. I’ve also installed tailscale on the raspberrypi running Ubuntu Server 24.

Materials Link to heading

  1. Raspberry Pi 4B 8GB RAM
  2. Raspberry PI power supply (I’m using Power over Ethernet via the POE Hat. It provides both power and networking for the Pi)
  3. Ethernet cable (CAT 6)
  4. MicroSD Card (32GB)
  5. 2TB External Hard-drive formatted to ext4

Raspberry Pi Link to heading

Flashing the SD Card Link to heading

The easiest way to flash an Operating System onto the SD card is through the Raspberry Pi Imager. Raspberry PI Image

You can download it from this link. Follow the instructions on installing the application and open it.

From the Device list, select Raspberry Pi 4. Raspberry Pi 4 Selection

Now we need to choose an operating system. I’m going to install Ubuntu Server 24. Click on Other general-purpose OS and select Ubuntu Server 24. Raspberry Pi 4 OS Selection Ubuntu Server 24

Finally, choose the storage location which is the SD card and click Next. If you’d like, you can set up some extra configurations like ssh-keys or password or even the Wi-Fi for the raspberry pi to connect to. I normally set the default account as username: pi and password: raspberry but feel free to set it up with your own credentials. I am using a wired connection so I’m going to skip this but feel free to edit the configurations. Now image your pi and wait until the application completes.

Connecting to the Pi Link to heading

With your Pi connected to the network, we are going to ssh into the raspberry pi.

  1. Run the ssh command below:
ssh pi@<IP>
  1. Now we always need to make sure our linux server is up to date with its package. To do that we need to run and update and and upgrade
sudo apt update && sudo apt upgrade

Docker Link to heading

Now that we got our raspberry pi updated with the latest packages, we need to install docker. I followed the instructions from the docs.docker website linked here. Scroll to the section that says Install using the apt repository. These would be the commands we need to run on the raspberry pi.

# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Add the repository to Apt sources:
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update

Once we have run the commands above, we can install docker.

sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Do a quick test as suggested in the website by pulling the hello-world image.

sudo docker run --rm hello-world

I added the --rm flag to clean up the container once it exits.

Docker Compose Environment Link to heading

  1. Change directory to the pi home with the cd command.
cd ~pi
  1. Create two directories that are nested. I like to call the parent directory docker-compose and the child directory nextcloud
mkdir -p docker-compose/nextcloud

The -p flag will create the parent directories if needed.

Docker Compose File Link to heading

For the actual docker compose file, I wrote out the nextcloud service like this:

services:
  nextcloud:
    image: linuxserver/nextcloud
    container_name: nextcloud
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Etc/UTC
    ports:
      - 443:443
    volumes:
      - /home/pi/nextcloud/config:/config
      - /media/external/nextcloud/data:/data
    restart: unless-stopped

Let’s take a quick look at the volumes. I decided to store the nextcloud configuration on the home directory of the raspberry pi. However the data that will be used by nextcloud will be stored on the external 2TB drive. I’ll go over mounting the external drive onto your raspberrypi using both the mount command and making a change in fstab. Go ahead and save the file, We won’t run docker compose yet because we still need to mount the external drive.

Mount External Drive Link to heading

Unlike Windows or MacOS, most distros of Linux will not automatically mount your external drive. You’re going to need to mount it yourself. The first thing to do is identify where the drive is. You should see it as /dev with either sda1, sda2, sdb1,etc.

  1. To find out where the drive is, type in the lsblk command.
lsblk

Your result should look something like this lsblk output Here we can see that the 2TB external drive is attached to /dev/sda2 which means we need to mount that drive to /media/external

Mount Command Link to heading

For a one time mount, we can use the mount command to mount our drive to /media/external.

sudo mount /dev/<sda#,sdb#> /media/external

To make sure we mounted it properly, we can simply type the mount command and check the output to see if our external drive mounted properly.

FSTAB Link to heading

Now the mount command is great for a one time thing. But let’s say that your raspberrypi gets disconnected. You’ve now lost your mount point. To prevent this, we can have the external drive mounted on start up by adding an entry to /etc/fstab. Normally when making an addition to the fstab file, we can add our device name, some configurations, and we’d be good. However, device names are dynamic and we want to make sure that we mount just the external hard drive for now.

  1. Type the blkid command to get the blockids of the devices.

  2. Look for your external hard drive at /dev/<sda,sdb>

  3. On that line, you should see something that says UUID. Copy that value by highlighting it and right-clicking it. We will use that value as an entry for fstab.

  4. Open /etc/fstab in a file editor of your choice. I’m going to use VIM for this example but feel free to use NANO.

sudo vim /etc/fstab
  1. Scroll to the bottom of the file.

  2. Type i for insert mode in VIM.

  3. Add the following line in the following pattern:

UUID=<UUID you copied> /media/external ext4 defaults 0 0

Let’s deep dive into what that particular line is.

  • The first value is the device’s UUID which is a unique ID for that particular drive. This means that only that drive will be able to mount on the mount point.
  • The second value is the mount point. In this example, we are mounting the device to /media/external but you can mount it wherever you see fit. Just don’t forget to change the mount point in the docker-compose file!
  • The third value is the filesystem of the drive. The drive should be of ext4 filesystem but if it isn’t, you’re going to need to format the drive to ext4. Most of the time you can simply use the mkfs.ext4 command on your device and the filesystem should be set to ext4.
  • The fourth value are the mount options. We don’t need any special options here and simply just need the drive to mount on startup.
  • The fifth value is a backup operation which is outdated.
  • The sixth value is a file system check order. This is a priority assigned to check the filesystem. Values with a 0 means that the filesystem will not be checked. We want to avoid using 1 because this disk is not a root filesystem. I’m setting it to 0 because I don’t need the filesystem to be checked.
  1. Once you’ve added your line to fstab, close out the file. In VIM, we can use a :wq to save and close the file.

  2. Type sudo systemctl daemon-reload to reload the fstab.

Now we have successfully added a line to fstab and our external drive will mount automatically when the raspberrypi boots up!

Tailscale Link to heading

We got our docker-compose file setup and our external drive mounted. Now we need a way to access the raspberrypi from the outside. I’m going with Tailscale as my connection choice. To learn more about tailscale, click here. Setting up tailscale on the raspberrypi is super simple.

  1. Create a tailscale account.
  2. On the raspberrypi, type the following command as stated on the tailscale website.
curl -fsSL https://tailscale.com/install.sh | sh
  1. Now we want to turn on tailscale.
sudo tailscale up
  1. There should be an address output from the command. Open up a browser and paste that URL in.

  2. Connect to your device via the URL and your device should be added to your tailscale admin console.

And thats it for Tailscale! It’s super simple to set up and the webUI is also easy to navigate. At this point, I would download the Tailscale client on your laptop or iPhone. Tailscale should provide you with an IP Address of your raspberrypi. Make sure to save that IP Address off to the side because we will need it.

Running Nextcloud Link to heading

Now with our environment setup, we are good to run a docker compose up command.

  1. SSH into your raspberrypi.
  2. Change directory to the docker-compose.yml file we created above using the cd command.
  3. Perform a docker compose up in detached mode.
docker compose up -d

You should see docker pull the nextcloud image. To view the logs, type docker compose logs and you will be presented with the nextcloud service’s logs. You can even check to see if the container is running by typing docker ps and look for a container named nextcloud.

  1. We are going to take the IP address of the raspberry pi first and paste it in the URL browser. We will come back to the tailscale IP address since we need to make a configuration change. You should be presented with an initial login page Nextcloud Login

  2. Type in an admin account name and an admin password.

  3. For the database, we are going to use the default which is SQLite.

  4. Click on the Install button. The installation should take a few minutes.

  5. Once the installation is complete, we can stop Nextcloud with a docker compose down command.

docker compose down
  1. In order for us to use the tailscale IP address to work, we need to make a change in the configuration file. On the raspberrypi, go the nextcloud config directory. In the docker compose we have it set to /home/pi/nextcloud/config.
cd /home/pi/nextcloud/config
  1. To edit the configuration file, we need to go to the following directory:
cd www/nextcloud/config
  1. Using your editor of choice (in my case its VIM), edit the config.php file.

  2. Where it says array, add an entry with your Tailscale IP Address.

array (
  0 => '<raspberrypi local IP>',
  1 => '<raspberrypi tailscale IP>'
)
  1. Change directory back to where the next cloud docker-compose.yml file is.
cd /home/pi/docker-compose/nextcloud/
  1. Run docker compose up -d to start the nextcloud service in detached mode.

  2. Now enter your tailscale IP address and you should see your nextcloud login page.

If you see a login page and you are able to access your files, CONGRATULATIONS! You are now self hosting a file sharing application on a raspberrypi. Your files are with you and on a device you control. You’ve even added a security measure with tailscale to access the file server via the tailnet. You are now ready to start uploading files.

Additional Items Link to heading

Uploading Files into Nextcloud via SFTP Link to heading

Now that you have your nextcloud file hosting service up, you can start uploading files. However, using the webUI for uploading large files has errored out too many times for me. The better way to put your files directly on the nextcloud server (i.e. the raspberrypi) is to use SFTP or Secure File Transfer Protocol.

What is SFTP Link to heading

SFTP or Secure File Transfer Protocol is a network protocol that allows users to securely transfer files from a client to a server. It utilizes the same port as SSH so we don’t have to do any configurations for sftp to work.

Steps Link to heading

  1. On the client machine (a laptop), go the directory where you have the large file via the terminal.

  2. Type in the following:

sftp pi@<TAILSCALE IP>

You can also use your local raspberrypi IP address.

  1. Change directory to the location of the external hard drive’s nextcloud data. We mounted it in /media/external.
cd /media/external/nextcloud/data
  1. You should see your admin account directory. Change directory to the files directory within your admin account.
cd <admin account>/files
  1. Put your file using the put command. If you are putting a directory, you are going to need to add the flags -rf for recursive and force.
put <FILE>
put -rf <DIRECTORY>
  1. We can close the sftp session by typing bye.

Now if we go to the webUI of nextcloud, we won’t be able to see the new file/directory we just uploaded. To fix this, we have to go into the container and execute a file scan.

  1. Because we named our container nextcloud, we are going to type:
docker exec -it nextcloud /bin/bash

What this command does is it executes a particular command within the container. In this case, we want to create an interactive shell in side the container that runs the bash command to open a bash shell.

  1. Once inside the container, we can run a file scan.
occ files:scan --all
  1. Now we can open up the nextcloud webUI and we should see our new file/directory.