Selfhost Nexcloud AIO with swag as reverse proxy

Nicolae_Grigorescu_-_Care_cu_boi_la_Oratii

Preamble

I self host on my home server several services for me, my family and our little business. With Nextcloud I am covering two basic needs for us:

  1. Accessing our documents with ease from multiple platforms. For personal computer access I rely on VPN, but for access from our phones or from other computers I need a solution (Note: be careful when accessing your data, selfhosted or not, from foreign computers).
  2. Sync of our photos done with the phones.

I had already a Nextcloud AIO running but changing the home modem changed the entire local topology and Nextcloud was not working anymore. After I have managed to make it work, I decided to share it, maybe somebody else will need it...

(My) Network Topology

Looks like this:

  • I have my own domain, as you can see from this blog.
  • I have a subdomain for nextcloud that I am using.
  • Since I do not have a fixed external IP address I use https://www.dynu.com DDNS service to make them point to my external IP address. I have a script that updates my external IP when required, but this is another story. I know that DYNU is not one of the most famous DDNS providers, but I am using it for quite a while and I am happy with them. At this moment in order to also contribute somehow to their financial flow I use them as a registrar for the bunch of domains that I personally use.
  • The home router has a fix local IP address, let's call it 192.168.1.1.
  • The server hosting the Nextcloud AIO instance has also a fixed IP address, let's call it 192.168.1.2.
  • The router has following ports forwarded to the server: 80 (requests are forwarded on 443 on the local server), 443, 3478 (for Nextcloud Talk), 8080 (for Nextcloud AIO interface) and 8443 (I should check if this is really required).
  • I use since a while swag as a reverse proxy and I am quite happy with it.
  • I intent to serve Nextcloud via swag.
  • My server as home runs Open Media Vault with Proxmox Kernel because of convenience.
  • Docker and docker-compose are installed on the server and I use docker-compose yml files for spinning my dockers.
  • I want to access Nextcloud via a subdomain of mydomain.

My Docker Compose files

For the sake of simplicity I will include here only the settings for swag and nextcloud AIO:

version: "3.8"
volumes:
  nextcloud_aio_mastercontainer:
    name: nextcloud_aio_mastercontainer
    driver_opts:
      o: bind
      type: none
      device: /persist/nextcloudaio
services:
  swag:
    image: linuxserver/swag
    container_name: swag
    cap_add:
      - NET_ADMIN
    environment:
      - PUID=1000 
      - PGID=100  
      - TZ=Europe/Vienna 
      - URL=mydomain.com
      - EXTRA_DOMAINS=nextcloud.mydomain.com
      - VALIDATION=http
      - EMAIL=admin@mydomain.com
    volumes:
      - /persist/swag:/config  
    network_mode: "host"
    restart: unless-stopped
    extra_hosts:
      - "host.docker.internal:host-gateway"
  nextcloud:
    image: nextcloud/all-in-one:latest
    restart: unless-stopped
    container_name: nextcloud-aio-mastercontainer
    volumes:
      - nextcloud_aio_mastercontainer:/mnt/docker-aio-config
      - /var/run/docker.sock:/var/run/docker.sock:ro
    ports:
      - 85:80
      - 8080:8080
      - 8443:8443
    environment:
      - APACHE_PORT=11000
      - APACHE_IP_BINDING=0.0.0.0
      - NEXTCLOUD_DATADIR=/my_data_dir/nextcloud
      - NEXTCLOUD_ENABLE_DRI_DEVICE=true #for hardware transcoding

Now a couple of explanations:

  • I like to keep docker persistent files into a easy accessible folder. That is why on my server I have created a directory for it, let's call it /persist. Such a directory can be created in Linux with sudo mkdir /persist. This will create a directory in the root of your file system called persist. Inside one can place subfolders for each service/container/docker.
  • Inside the /persist folder I have created a subfolder for Nextcloud AIO this is the /persist/nextcloudaio and here Nextcloud will place its data. This data will not be deleted when the docker is destroyed (therefore the name persist).
  • cap_add: - NET_ADMIN is a setting required for swag, more details here: https://docs.docker.com/compose/compose-file/compose-file-v3/#cap_add-cap_drop and here: https://man7.org/linux/man-pages/man7/capabilities.7.html.
  • PUID and PGID are detailed here: https://docs.linuxserver.io/general/understanding-puid-and-pgid/#using-the-variables. In my case these I run the containers as my user on the server and I have found these values running id <user> on my server. These are the default values in Debian (which is the base of Open Media Vault). If I am not wrong Ubundu uses a PGID of 1000 instead of 100.
  • URL=mydomain.com is my main domain pointing to my home server. If you have only one service that you can use here directly the url for the Nextcloud.
  • as I host multiple services and use multiple subdomains I have them declared here: EXTRA_DOMAINS=nextcloud.mydomain.com. Additional subdomains can be mentioned like this: EXTRA_DOMAINS=nextcloud.mydomain.com,jellyfin.mydomain.com,anotherdomain.com.
  • My persistent information for the swag container is stored in the /persist/swag:/config. The directory can be created with sudo mkdir /persist/swag. Swag will place there all the important files, including configuration files.
  • network_mode: "host" was the paramount setting for making Nextcloud AIO work with swag, as also mentioned here: https://github.com/nextcloud/all-in-one/blob/main/reverse-proxy.md#1-configure-the-reverse-proxy.
  • the extra_hosts option should make the nextcloud-aio-apache container be reachable via host.docker.internal reference. I think the network_mode setting invalidates this as, as you will see, nextcloud will be called via the IP address of the home server.
  • APACHE_PORT=11000 and APACHE_IP_BINDING=0.0.0.0 are also recommended settings as per the above link from Nextcloud AIO.
  • NEXTCLOUD_DATADIR=/my_data_dir/nextcloud this is where the Nextcloud data will leave. This should be an existing directory where you have enough space for all your files. :-)

This are pretty much the details of the compose file. Save it as docker-compose.yml and run sudo docker-compose up -d in the same directory. Docker-compose should make the installation. Nextcloud AIO is not yet available as we need to instruct swag how to serve it.

The reverse proxy config

In order to access the Nextcloud AIO we need to instruct swag on how to serve it. I will not detail the principle of reverse-proxy but basically we need to tell to swag what to call and what to serve in case he receives a request for nextcloud.mydomain.com. This is done via a configuration file that should be placed inside /persist/swag/nginx/proxy-configs/ on the server. In the same location one can find some generic proposal for various services. There is also one named nextcloud.subdomain.conf.sample for reference. My file looks something like this:

map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}

server {
    listen 80;
    listen [::]:80;            # comment to disable IPv6

    if ($scheme = "http") {
        return 301 https://$host$request_uri;
    }
    listen 443 ssl;
    listen [::]:443 ssl;

    server_name nextcloud.*;
    include /config/nginx/ssl.conf;
    add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;";
    client_max_body_size 0;

    location / {
      proxy_pass http://192.168.1.2:11000$request_uri;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Port $server_port;
      proxy_set_header X-Forwarded-Scheme $scheme;
      proxy_set_header X-Forwarded-Proto $scheme;      
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header Accept-Encoding "";
      proxy_set_header Host $host;

      proxy_hide_header X-Frame-Options;

      client_body_buffer_size 512k;
      proxy_read_timeout 86400s;
      client_max_body_size 0;

      # Websocket
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection $connection_upgrade;
    }

    ssl_certificate /etc/letsencrypt/live/mydomain.com/fullchain.pem;   # managed by certbot on host machine
    ssl_certificate_key /etc/letsencrypt/live/mydomain.com/privkey.pem; # managed by certbot on host machine

    ssl_session_cache shared:MozSSL:10m; # about 40000 sessions
}

Special attention to these points:

  • proxy_pass http://192.168.1.2:11000$request_uri; - the internal IP should be the local IP of the server where Nextcloud is installed. The 11000 port should be the one mentioned in the docker compose file at APACHE_PORT=11000.
  • ssl_certificate and ssl_certificate_key should point to where the SSL certificates are stored by swag. On the above configuration they should be visible at /persist/swag/etc/letsencrypt/live here you should see the mydomain.com folder where the certs are saved.

Save the nextcloud.subdomain.conf file and restart the swag docker using sudo docker restart swag.

Now Nextcloud should be available at https://nextcloud.mydomain.com.

Closing notes and disclaimer

This is my working configuration and it might depend on some other (small) settings that I have done. This is due to the fact that this is a "story in the middle of the journey": server is setup quite a while and saw already a simple Nextcloud installation (with one container for Nextcloud, one for MariaDB, one container for Collabora Office and the link between them) and another config of Nextcloud AIO that borked when my network topology changed. Next I will write how I have made my Nextcloud AIO interface available from the web in order to be able to update it remotely. VPN is one way, but I have made it available via another subdomain https://nextcloudaio.mydomain.com. I hope this helps somebody, or at least me later, when I will need to tweak this again. :-)

Add a comment