Running NextCloudPi with Docker Compose behind NGINX Reverse Proxy with TLS

Published by Philipp Schuster on

I have successfully managed to run NextCloudPi on my Raspberry Pi 4 (4GB) with Docker Compose behind a NGINX as reverse proxy. It was quite some work to figure out how to configure everything. I want to share what I have learned in case you want to do something similar. Hopefully I can help you. This tutorial will assume that you know how to setup NGINX as reverse proxy with TLS on your Pi. If not look at my approach here. I assume you have Docker and Docker Compose already installed. THis tutorial will mainly focus on the connection between NGINX and NextCloudPi and the integration with Docker Compose. Let’s start.

I choosed to run NextCloudPi rather than NextCloud because everything is preconfigured. Perhaps you can achieve something similar by running several Docker containers for NextCloud and a database as well. NextCloudPi has the big advantage that it’s one single image and most importantly there are pre-compiled images for ARM available on Docker Hub – that’s what we need for the Pi.

The challenge was that NextCLoudPi expects to be the primary web server on a machine, thus owning ports 80 and 443 as well as doing the TLS-handshake. This wont work if we use NGINX that handles TLS as reverse proxy. This way I can run multiple web services on my Pi whereas NextCloudPi is just one of them. I want NGINX to forward requests specified by the subdomain to the corresponding service.

TL;DR

A request to https://nextcloud.domain.tld will be internally forwarded to https://nextcloudpi using NGINX and Docker Compose. All services on the Pi (NGINX, NextCloudPi, …) will be part of one big docker-compose.yml.

NGINX reverse proxy

My NGINX comes from a Dockerfile that copies my nginx.conf into the NGINX image. It’s a pretty simple Dockerfile. The interesting part is the nginx.conf. Also I have a docker-compose.yml to start everything up. The docker-compose.yml and the nginx.conf are highly connected to each other, i.e. nginx.conf expects Docker Compose to do DNS internally for "https://nextcloudpi" and so on.

Dockerfile

FROM nginx:1.17.5 # to get nginx_more_headers 
RUN apt-get update && apt-get install -y nginx-extras 
RUN rm /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx 
VOLUME /var/log/nginx

Build with: $ docker build -t yourname/raspberry-pi-master-nginx .

⚠️ Before build: make sure that the file nginx.conf (the content is listed below) must be at the correct path. In this case in "./nginx.conf", i.e. the directory where the Dockerfile is located. ⚠️

nginx.conf

The important lines are proxy_pass https://nextcloudpi; for the regular web UI and proxy_pass https://nextcloudpi:4443; for the web configuration UI.

http {
  # if you have many server{}-declarations, 32 is not enough ---> 64
  server_names_hash_bucket_size 64;

  access_log /var/log/nginx/access.log;
  error_log /var/log/nginx/error.log;

  # Common TLS Config
  # ..
  # not covered in this tutorial
  
  # redirects all http requests to https requests
  server {
    listen 80 default_server;
    listen [::]:80 default_server;
    return 301 https://$host$request_uri;
  }

  server {
    # replace with your domain
    server_name nextcloud.domain.tld;

    listen 443 ssl http2;
    listen [::]:433 ssl http2;

    # maximum 3GB Upload File; change to fit your needs
    client_max_body_size 3G;

    location / {
      # remove upgrade h2 header (breaks some http clients, like curl or safari)
      # from NextCloudPi's Apache; we do HTTP/2 between Client and NGINX
      more_clear_headers 'upgrade';
      # we set this already in our reverse proxy
      more_clear_headers 'Strict-Transport-Security';

      # nextcloudpi only works with https but it's ssl has no valid 
      # certificate  configured
      proxy_ssl_verify off;
      // docker-compose does DNS for "nextcloudpi" 
      proxy_pass https://nextcloudpi;
      # set proper x-forwarded-headers
      proxy_set_header 'X-Forwarded-Host' nextcloud.domain.tld;
      proxy_set_header 'X-Forwarded-Proto' https;
      # -For and -IP:
      # see https://stackoverflow.com/questions/19366090/what-is-the-difference-between-x-forwarded-for-and-x-forwarded-ip
      proxy_set_header 'X-Forwarded-For' $remote_addr;
      proxy_set_header 'X-Forwarded-IP' $remote_addr;
    }
  }

  server {
    # replace with your domain
    server_name nextcloud.domain.tld;

    # 4443 ist der port von nextcloudpi für das konfig web ui
    listen 4443 ssl http2;
    listen [::]:4433 ssl http2;


    location / {
      more_clear_headers 'upgrade';
      more_clear_headers 'Strict-Transport-Security';

      # nextcloudpi only works with https but it's ssl has no valid 
      # certificate configured
      proxy_ssl_verify off;
      proxy_pass https://nextcloudpi:4443;
      # forward basic auth 
      proxy_pass_header Authorization;
      # not necessary; but perhaps the next instance will use this somehow
      # and don't do double TLS
      proxy_set_header 'X-Forwarded-Proto' https;
      proxy_set_header 'X-Forwarded-For' $remote_addr;
      proxy_set_header 'X-Forwarded-IP' $remote_addr;
    }
  }
}

Docker Compose

Now the real magic happens. Ports 80 and 443 on the host system are bound to NGINX but NextCloudPi also wants Port 80 and Port 443. Docker Compose helps us and creates a Docker Compose network. Inside this network Docker Compose will provide DNS by container names. NGINX can therefore forward requests to “https://nextcloudpi” (which equals https://nextcloudpi:443). Thats it basically.

networks:
  nextcloudpi:

services:
  # main nginx : reverse proxy for ssl
  nginx:
    restart: always
    container_name: raspberry-pi-master-nginx
    image: yourname/raspberry-pi-master-nginx:latest
    ports:
      - 80:80
      - 443:443
      - 4443:4443 # configuration port for nextcloudpi
    volumes:
      # for error.log (choose any volume path you want)
      - .my_docker_compose_volume/nginx:/var/log/nginx
      # nginx.conf expects ssl files here
      - /etc/letsencrypt/live/:/etc/letsencrypt/live
      - /etc/letsencrypt/archive:/etc/letsencrypt/archive
      # DH params (needs to be generated first, see e.g.: `sudo openssl dhparam -out /etc/ssl/dhparam.pem 4096`
      - /etc/ssl/:/etc/ssl
    depends_on:
      - nextcloudpi
    networks:
      - nextcloudpi

  ##################

  # nextcloud pi
  nextcloudpi:
    restart: always
    container_name: nextcloudpi
    image: ownyourbits/nextcloudpi-armhf
    expose:
      - 80
      - 443
      - 4443 # nextcloudpi web config ui port
    volumes:
    - .docker_compose_volume/next_cloud_pi:/data
    - /etc/localtime:/etc/localtime:ro
    networks:
      - nextcloudpi

First run

Open in your browser https://nextcloud.mydomain.tld/activate and you should see NextCloud. After you set everything up you should also be able to call https://nextcloud.mydomain.tld:4443 for the NextCloudPi configuration web UI.

After first run

Unfortunately I didn’t found a way to configure the trusted host of NextCloud via Docker Compose. Shut down your container and open “nextcloud/config/config.php” in the Docker volume of NextCloudPi.

  • set ‘overwriteprotocol’ to https
  • set ‘overwritehost’ to your domain (e.g. nextcloud.domain.tld)
  • add your domain to the trusted hosts array

That’s it. Everything should work! This last step are necessary because otherwise NextCloud thinks it runs on “https://nextcloudpi” under several circumstances. I hope it works for you as well. If you have questions feel free to ask!

PS

I stripped lot’s of information how my NGINX gets access to the let’s encrypt certificates. I thought it be better to strip this information here. If you have questions feel free to ask! The basically workflow is the following:

  • shut down NGINX
  • use certbot --standalone ... to get the certificate for all domains
  • using Docker Compose volumes to link /etc/letsencrypt/... into the NGINX container
  • start NGINX

UPDATE I added a little guide how to glue together Lets Encrypt certificates and NGINX inside a Docker container.


Philipp Schuster

Hi, I'm Philipp and interested in Computer Science. I especially like low level development, making ugly things nice, and de-mystify "low level magic".

10 Comments

Sascha · 2020-01-28 at 22:50

Sehr interessant. Danach suche ich seit Monaten, weil ich gerne neben NCP noch eine Hompage hosten wollen würde und am besten sogar PiHole, wobei das wegen lighttpd wohl nicht gehen wird. Aber sehr interessant. Bin ebenfalls 22 und IT-Werkstudent, deshalb umso cooler 🙂
Ich hoffe ich bekomme das auch so hin wie du. Aber auf jeden Fall schonmal danke!

David · 2020-07-11 at 15:49

Hi, ich bekomme beim nginx immer folgende Meldung: “unknown directive “more_clear_headers” in /etc/nginx/nginx.conf:32″

Hast du eine Idee, was das Problem sein könnte?

Danke dir im Voraus

    Philipp Schuster · 2020-07-12 at 00:01

    Hi David 🙂
    Beim Bauen des nginx Containers die Zeile `RUN apt-get update && apt-get install -y nginx-extras` beachten.

    Dann wird das auch gefunden

Jonathan Aud · 2021-06-15 at 18:26

Hallo Philipp,
Das Deutsche wird mit Google Translate übersetzt.

Ich befolge die Schritte in dem Artikel, hänge aber beim Docker-Build-Befehl auf.
Dies ist eine Neuinstallation von Ubuntu 20.04 auf ext4

hier ist die Dockerbuild-Datei
VON nginx: 1.17.5
# um nginx_more_headers zu erhalten
apt-get update ausführen && apt-get install -y nginx-extras
RUN rm /etc/nginx/conf.d/default.conf
/var/log/nginx.conf /etc/nginx . KOPIEREN
LAUTSTÄRKE /var/log/nginx

Beim Ausführen des Builds erhalte ich den Fehler
debconf: Verzögerung der Paketkonfiguration, da apt-utils nicht installiert ist

Ich habe apt-utils manuell installiert

Allerdings bekomme ich den Fehler
Schritt 4/5: /var/log/nginx.conf /etc/nginx . KOPIEREN
KOPIEREN fehlgeschlagen: Datei im Build-Kontext nicht gefunden oder von .dockerignore ausgeschlossen: stat var/log/nginx.conf: Datei existiert nicht

Ich habe es mit –no-cache versucht, gleiches Ergebnis.

Ich versuche, nextcloudpi zusammen mit mehreren anderen Apps mit SSL auszuführen.

Ihre Website ist die einzige, die ich gefunden habe, die eine Möglichkeit bietet, dies zu tun.
Könnten Sie bei diesem Fehler helfen?
Vielen Dank

    Philipp Schuster · 2021-06-26 at 16:18

    Hi Jonathan,
    I’m confused by “Das Deutsche wird mit Google Translate übersetzt.” – do you want me to answer in english or german? 😀 I’ll just use English.
    The error message is relatively clear, “nginx.conf” is not found. Inside the directory where you execute “docker build”, you need `Dockerfile` as well as `nginx.conf`. The content of this `nginx.conf` is listed above in my blogpost. There is no much magic behind this.

    PS: I edited the blogpost a bit to make this more clear to future readers.

Silas · 2023-09-14 at 19:29

Hi,
ich habe den Proxy mit dem Home Assistant Addon Nginx Proxy Manager aufgesetzt. Im NCP habe ich https-only deaktiviert und leite im Nginx Proxy Manager auf den Port 80 weiter. Das funktioniert für mehrere Minuten tadellos, dann kommt auf einmal “502 Bad Gateway” von einem nginx server. Allerdings benutzt NCP soweit ich weiß gar kein nginx, die Meldung kommt wohl vom Home Assistant Addon.

Der Log sieht ungefähr so aus:
[14/Sep/2023:19:24:06 +0200] – 502 502 – GET https cloud.xxx.de “/push/ws” [Client xxx.xxx.xxx.xxx] [Length 150] [Gzip -] [Sent-to 192.168.178.10] “-” “-”
2023/09/14 19:24:06 [warn] 4677#4677: *3777 using uninitialized “server” variable while logging request, client: xxx.xxx.xxx.xxx, server: cloud.xxx.de, request: “����T��R”
[14/Sep/2023:19:24:06 +0200] – – 400 – – https cloud.xxx.de “-” [Client xxx.xxx.xxx.xxx] [Length 150] [Gzip -] [Sent-to ] “-” “-”

Warum geht die Verbindung erst einige Minuten und reißt dann plötzlich ab?

    Philipp Schuster · 2023-09-20 at 16:16

    Hey! Kann ich dir so leider auch nicht sagen. Da müsste man mal mehr in die Container zur Laufzeit reindebuggen. Bin aber selbst seit Jahren leider gar nicht mehr im Docker-Umfeld aktiv. Ich hoffe, du kannst das Problem bald noch lösen! Viel Erfolg

Leave a Reply

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