How to Install Nextcloud+Syncthing+Filebrowser in Open Media Vault 6

Install Open Media Vault 6 using this command:

wget -O - https://github.com/OpenMediaVault-Plugin-Developers/installScript/raw/master/install | sudo bash

Once installed, create a Samba share called ‘appdata’ containing a ‘configs’ folder and a separate Samba share for each of your users. The following nginx.conf file is to be saved in your appdata/configs folder:

worker_processes auto;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    upstream php-handler {
        server app:9000;
    }

    server {
        listen 80;

        # HSTS settings
        # WARNING: Only add the preload option once you read about
        # the consequences in https://hstspreload.org/. This option
        # will add the domain to a hardcoded list that is shipped
        # in all major browsers and getting removed from this list
        # could take several months.
        #add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;" always;

        # set max upload size
        client_max_body_size 512M;
        fastcgi_buffers 64 4K;

        # Enable gzip but do not remove ETag headers
        gzip on;
        gzip_vary on;
        gzip_comp_level 4;
        gzip_min_length 256;
        gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
        gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;

        # Pagespeed is not supported by Nextcloud, so if your server is built
        # with the `ngx_pagespeed` module, uncomment this line to disable it.
        #pagespeed off;

        # HTTP response headers borrowed from Nextcloud `.htaccess`
        add_header Referrer-Policy                      "no-referrer"   always;
        add_header X-Content-Type-Options               "nosniff"       always;
        add_header X-Download-Options                   "noopen"        always;
        add_header X-Frame-Options                      "SAMEORIGIN"    always;
        add_header X-Permitted-Cross-Domain-Policies    "none"          always;
        add_header X-Robots-Tag                         "none"          always;
        add_header X-XSS-Protection                     "1; mode=block" always;

        # Remove X-Powered-By, which is an information leak
        fastcgi_hide_header X-Powered-By;

        # Path to the root of your installation
        root /var/www/html;

        # Specify how to handle directories -- specifying `/index.php$request_uri`
        # here as the fallback means that Nginx always exhibits the desired behaviour
        # when a client requests a path that corresponds to a directory that exists
        # on the server. In particular, if that directory contains an index.php file,
        # that file is correctly served; if it doesn't, then the request is passed to
        # the front-end controller. This consistent behaviour means that we don't need
        # to specify custom rules for certain paths (e.g. images and other assets,
        # `/updater`, `/ocm-provider`, `/ocs-provider`), and thus
        # `try_files $uri $uri/ /index.php$request_uri`
        # always provides the desired behaviour.
        index index.php index.html /index.php$request_uri;

        # Rule borrowed from `.htaccess` to handle Microsoft DAV clients
        location = / {
            if ( $http_user_agent ~ ^DavClnt ) {
                return 302 /remote.php/webdav/$is_args$args;
            }
        }

        location = /robots.txt {
            allow all;
            log_not_found off;
            access_log off;
        }

        # Make a regex exception for `/.well-known` so that clients can still
        # access it despite the existence of the regex rule
        # `location ~ /(\.|autotest|...)` which would otherwise handle requests
        # for `/.well-known`.
        location ^~ /.well-known {
            # The rules in this block are an adaptation of the rules
            # in `.htaccess` that concern `/.well-known`.

            location = /.well-known/carddav { return 301 /remote.php/dav/; }
            location = /.well-known/caldav  { return 301 /remote.php/dav/; }

            location /.well-known/acme-challenge    { try_files $uri $uri/ =404; }
            location /.well-known/pki-validation    { try_files $uri $uri/ =404; }

            # Let Nextcloud's API for `/.well-known` URIs handle all other
            # requests by passing them to the front-end controller.
            return 301 /index.php$request_uri;
        }

        # Rules borrowed from `.htaccess` to hide certain paths from clients
        location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/)  { return 404; }
        location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console)                { return 404; }

        # Ensure this block, which passes PHP files to the PHP process, is above the blocks
        # which handle static assets (as seen below). If this block is not declared first,
        # then Nginx will encounter an infinite rewriting loop when it prepends `/index.php`
        # to the URI, resulting in a HTTP 500 error response.
        location ~ \.php(?:$|/) {
            # Required for legacy support
            rewrite ^/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+|.+\/richdocumentscode\/proxy) /index.php$request_uri;

            fastcgi_split_path_info ^(.+?\.php)(/.*)$;
            set $path_info $fastcgi_path_info;

            try_files $fastcgi_script_name =404;

            include fastcgi_params;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            fastcgi_param PATH_INFO $path_info;
            #fastcgi_param HTTPS on;

            fastcgi_param modHeadersAvailable true;         # Avoid sending the security headers twice
            fastcgi_param front_controller_active true;     # Enable pretty urls
            fastcgi_pass php-handler;

            fastcgi_intercept_errors on;
            fastcgi_request_buffering off;
        }

        location ~ \.(?:css|js|svg|gif)$ {
            try_files $uri /index.php$request_uri;
            expires 6M;         # Cache-Control policy borrowed from `.htaccess`
            access_log off;     # Optional: Don't log access to assets
        }

        location ~ \.woff2?$ {
            try_files $uri /index.php$request_uri;
            expires 7d;         # Cache-Control policy borrowed from `.htaccess`
            access_log off;     # Optional: Don't log access to assets
        }

        # Rule borrowed from `.htaccess`
        location /remote {
            return 301 /remote.php$request_uri;
        }

        location / {
            try_files $uri $uri/ /index.php$request_uri;
        }
    }
}

Create a Nextcloud Stack in Portainer:

version: '2'

volumes:
  nextcloud:
  db:

services:
  db:
    image: mariadb:10.5
    restart: always
    command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW
    volumes:
      - /srv/dev-disk-by-uuid-abb9e9c9-907c-49e0-a8cb-b357d0e7e113/appdata/mariadb:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=password
      - MYSQL_PASSWORD=password
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud

  app:
    image: nextcloud:fpm
    restart: always
    links:
      - db
    volumes:
      - /srv/dev-disk-by-uuid-abb9e9c9-907c-49e0-a8cb-b357d0e7e113/appdata/nextcloud:/var/www/html
      - /srv/dev-disk-by-uuid-abb9e9c9-907c-49e0-a8cb-b357d0e7e113/user1:/user1
      - /srv/dev-disk-by-uuid-abb9e9c9-907c-49e0-a8cb-b357d0e7e113/user2:/user2
    environment:
      - MYSQL_PASSWORD=password
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud
      - MYSQL_HOST=db

  web:
    image: nginx
    restart: always
    ports:
      - 8080:80
    links:
      - app
    volumes:
      - type: bind
        source: /srv/dev-disk-by-uuid-abb9e9c9-907c-49e0-a8cb-b357d0e7e113/appdata/configs/nginx.conf
        target: /etc/nginx/nginx.conf
        read_only: true
#    volumes_from: <- Not supported by Libcompose, the library used to deploy stacks against docker standalone. This would be fixed by this proposed change - https://github.com/portainer/portainer/issues/3750
#      - app
      - /srv/dev-disk-by-uuid-abb9e9c9-907c-49e0-a8cb-b357d0e7e113/appdata/nextcloud:/var/www/html

Go to the container console in Portainer and enter the following for each of your users:

chown -R www-data:www-data /user1
chown -R www-data:www-data /user2

Also, add the following line to Nextcloud’s config.php file:

'overwriteprotocol' => 'https',

Create a Nextcloud-Proxy Stack in Portainer:

version: '3'
services:
  app:
    image: 'jc21/nginx-proxy-manager:latest'
    restart: unless-stopped
    ports:
      - '80:80'
      - '81:81'
      - '443:443'
    volumes:
      - /srv/dev-disk-by-uuid-abb9e9c9-907c-49e0-a8cb-b357d0e7e113/appdata/nextcloud-proxy/data:/data
      - /srv/dev-disk-by-uuid-abb9e9c9-907c-49e0-a8cb-b357d0e7e113/appdata/nextcloud-proxy/letsencrypt:/etc/letsencrypt

# If you are using FreeDNS and your router doesn't support it, then add the following section.
  dynamic-dns:
    container_name: dynamic-dns
    image: joweisberg/dynamic-dns:latest
    restart: unless-stopped
    environment:
      - TZ=America/Los_Angeles
      - USER="username"
      - PASSWORD="password"
      - SERVICE=freedns
      - HOSTNAME="subdomain.domain.com"
      - DETECTIP=1
      - INTERVAL=10
    healthcheck:
      test: ["CMD", "/usr/bin/healthcheck"]
      interval: 30s
      timeout: 10s
      retries: 5

Log into the proxy portal on port 81 using ‘admin@example.com’ for the user and ‘changeme’ for the password. You will be required to change the user and password. Then create a new proxy host with the port forwarded to your Nextcloud server (port 8080) and request a new new SSL certificate.

Create a Syncthing Stack in Portainer:

version: "2.1"
services:
  syncthing:
    image: lscr.io/linuxserver/syncthing
    container_name: syncthing
    hostname: syncthing #optional
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=America/Los_Angeles
    volumes:
      - /srv/dev-disk-by-uuid-abb9e9c9-907c-49e0-a8cb-b357d0e7e113/appdata/syncthing/config:/config
      - /srv/dev-disk-by-uuid-abb9e9c9-907c-49e0-a8cb-b357d0e7e113/user1:/user1
      - /srv/dev-disk-by-uuid-abb9e9c9-907c-49e0-a8cb-b357d0e7e113/user2:/user2
    ports:
      - 8384:8384
      - 22000:22000/tcp
      - 22000:22000/udp
      - 21027:21027/udp
    restart: unless-stopped

Create a File Browser Container in Portainer:

Name=Filebrowser: The name of the container. This is important because you can have several containers based on the same image.
Restart=always: Restart the container if it stops. If it stops manually, it is only restarted when the container is manually restarted, or the Docker service is restarted.
Port 8086:80: File Browser exposes port 80 for File Explorer access. As port 80 is usually used for any Web service; We indicate that port 80 of the container is exposed for example in port 8086. In this way we make other services that want to use port 80 to use it.
Volume /:/srv: Links a folder on the server (left), with a folder on the container (right). File Browser uses by default the internal root /srv folder to display in the file explorer. By indicating the root / in the server folder, we indicate that we want to show all the files on the server.
Environment Variable TZ='America/Los_Angeles': The time zone of the container machine. Optional. It is very useful so that the logs and notifications are with the local time.
filebrowser/filebrowser:latest: Indicates the image used to mount the container. If no version is indicated, it will take the latest stable version.

/etc/fstab

proc /proc proc defaults 0 0
PARTUUID=4d09410b-01 /boot vfat defaults,flush 0 2
PARTUUID=4d09410b-02 / ext4 defaults,noatime 0 1
# a swapfile is not a swap partition, no line here
# use dphys-swapfile swap[on|off] for that

# Mount external drive containing home directories on boot.
/dev/sda1 /media/external xfs usrquota,grpquota,prjquota 0 0
/media/external/home /home none bind

# Create NFS export folder.
/media/external/tftpboot /export/tftpboot none bind

# Reduce SD card wear by writing temporary files to RAM.
tmpfs /tmp tmpfs defaults,noatime 0 0

/etc/exports

# /etc/exports: the access control list for filesystems which may be exported

# to NFS clients. See exports(5).
/export/tftpboot 10.0.1.0/255.255.255.0(async,rw,insecure,no_root_squash,fsid=3bb7ab83-2664-4f00-9a97-d3f568495ada)

# NFSv4 – pseudo filesystem root
/export 10.0.1.0/255.255.255.0(no_subtree_check,fsid=0,ro)

Install Packages:

$ sudo apt-get install tftpd-hpa pwauth smbclient syncthing nfs-kernel-server apcupsd apcupsd-cgi libauthen-sasl-cyrus-perl libauthen-sasl-perl smartmontools nginx php7.4-fpm php7.4-mbstring php7.4-mysql php7.4-curl php7.4-gd php7.4-curl php7.4-zip php7.4-xml