Running Nextcloud on the Raspberry Pi 3 – Nginx Reverse Proxy, fixes for upload timeouts and more

Under my TV I have a Raspberry Pi 3 running Kodi, which works great for watching/listening to all my media. However, with it idling and always-on, I thought it would be great to be able to use it as a Nextcloud server as well. Here’s some details on my setup, as well as a some fixes for errors I encountered. Diving right in, here’s some details of my Pi’s OS install:

root@tvpi ###~> uname -a
Linux tvpi 4.19.57-v7+ #1244 SMP Thu Jul 4 18:45:25 BST 2019 armv7l GNU/Linux
root@tvpi ###~> lsb_release -a
No LSB modules are available.
Distributor ID:	Raspbian
Description:	Raspbian GNU/Linux 9.9 (stretch)
Release:	9.9
Codename:	stretch
root@tvpi ###/etc> docker --version
Docker version 19.03.1, build 74b1e89

The setup I’m going for is:
HTTPS -> nginx proxy (in raspbian) -> nextcloud instance (in docker)

Setting up Nginx as a reverse proxy

The first part of the setup is to get nginx operating as a reverse proxy. The nginx server will do several things:

  • Redirect http traffic to https
  • Terminate https TLS traffic, and then proxy the traffic via http to the nextcloud server running in docker
  • Split traffic up based on the hostname used – to allow me to run other sites from the same Pi

To get a secure TLS configuration I am using Mozilla’s fantastic SSL Configuration Generator https://ssl-config.mozilla.org/. You then need some reverse proxy configuration, including setting X-Forwarded headers to allow the Nextcloud instance to block IP’s that are trying to brute force and also detect the correct hostname:

    location / {
        proxy_pass http://localhost:8088;
    }
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Proto 'https';
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

With my original setup I was seeing these errors:
"jtuckey/files/Documents/testfile.bin.upload.part" is locked
which would show as a 423 error in the Firefox dev-tools. According to Wikipedia this error is 423 Locked (WebDAV; RFC 4918). Here is screenshot of the problem:

These are the fixes I used for these issues with uploads, both with the maximum size of file uploads, and with file uploads timing out when I uploaded a large file. Nextcloud uploads large files in 10M chunks, and then re-combines them at the end into a single file. However, because of the Pi’s slow USB2 disk connection the re-combining can cause a timeout in the Nginx reverse proxy, which then re-tries the MOVE request, to which Nextcloud responds that the file is locked (it is already re-combining the file).

Also, when using the “file drop (upload only)” the uploads are not chunked, so to use that feature with large files you will need to set nginx’s max upload size to a very large value:

    # Allow uploads for NC - within the file app they upload in 10M chunks, however using file-drop
    # you need a value that is large enough for your largest file size
    client_max_body_size 5000M;

    # Allow long running connections, so NC can re-combine large files on a slow pi
    proxy_read_timeout 3600s;
    proxy_send_timeout 3600s;

Here’s the final nginx configuration I have:

Running docker from an external USB SSD

To get extra storage and performance, I use a USB attached SSD connected to my pi, which you can see in the picture. This external disk is mounted at /mnt/tosh. To have docker put it’s storage on the external disk what I did is create a symbolic link from /var/lib/docker to the external disk:

root@tvpi ###/v/lib> pwd
/var/lib
root@tvpi ###/v/lib> ls -lah docker
lrwxrwxrwx 1 root root 18 Jun  9 09:11 docker -> /mnt/tosh/dockerd/

Make sure you haven’t got any data in docker that you want to keep, like existing volumes. Once you are happy, rename/remove the old /var/lib/docker directory and link it to the path you want to use. To create the link you can run:

root@tvpi ###/v/lib> ln -s /mnt/tosh/dockerd/ docker

Running the Nextcloud Docker image via docker-compose

The next part of the setup is to run the nextcloud docker image. The best way I found to do this was to use docker-compose. However, docker-compose doesn’t install on raspbian because it is arm32, so instead I installed it using pip, with the help of pyenv to install the latest version of python and pip. Pyenv is available here: https://github.com/pyenv/pyenv. To get the install requirements setup I had to run the line from the https://github.com/pyenv/pyenv/wiki page:

sudo apt-get update; sudo apt-get install --no-install-recommends make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev

Once docker-compose is installed, I was able to use my docker-compose.yml file:

Note Line 15 – mapping in the extra config file for extending the apache2 timeout to help with file re-combining with large uploads. This is the contents of the apache_timeout.conf file:

# Timeout: The number of seconds before receives and sends time out.
Timeout 3600

Also take note of Lines 26 to 30 – where I set the IP Address Management. This allows setting the correct trusted_proxies value in the nextcloud config.php file.

Finally, also see in line 16 where I map in some extra external storage. I use Syncthing to do device synchronisation, because I find it’s syncing to be more reliable and efficient.

Running the Nextcloud cron job inside the docker container with a systemd timer

One situation the Nextcloud docker image has is that it doesn’t run cron within the container, so the nextcloud cron job isn’t regularly executed. To fix this, you need to run the job on a schedule on the host, to execute the cron.php file within the container. To do this, you can use cron, but I chose to use a systemd timer instead. The benefit this has is better logging and status checking. To create the config I used the excellent arch documentation here: https://wiki.archlinux.org/index.php/Systemd/Timers. Here are my nextcloud_cron.service and nextcloud_cron.timer files:

Setting the trusted_domains and trusted_proxies values in config.php

The final part of the configuration is to add a couple of extra values to the nextcloud config.php file. This should be added once you have started up the nextcloud container and run through the setup process. Once that’s complete, navigate to the nextcloud_config volume (will usually be at /var/lib/docker/volumes/nextcloud_config/_data) and add the extra values. Here’s what I added:

  'trusted_domains' =>  
  array (
    0 => 'localhost:8088',
    1 => 'nextcloud.jaytuckey.duckdns.org',
    2 => 'nc.jaytuckey.name',
  ),  
  'trusted_proxies' =>  
  array (
    0 => '172.16.0.1',
  ),  

Note the trusted_proxies value matches the subnet range we put in our docker-compose.yml file.

Bonus Points – An Ansible Playbook to configure most of these steps

To manage some of my configuration, I have created an ansible playbook to help set things up. Here’s my playbook:

Leave a Reply

Your email address will not be published. Required fields are marked *