Compose-Nextcloud-Portainer

Änderungsstand: 2021-12-14

Docker-Compose muss im Unraid installiert sein! Hier der Guide dazu.

Nun das Erstellen eines Compose-Stack mittels Portainer. Immer wieder kommen Anfragen diesbezüglich. Ich persönlich verwende diese Möglichkeit nicht. Wer wissen möchte, wie ich das realisiere, verwendet den vorherigen Guide 🙂 .

Es wird folgendes Szenario eingerichtet: Nextcloud:FPM mit PostgreSQL als Datenbank, einem Redis-Cache, einem eigenen NginX-Webserver. Portainer wird als Docker unter Unraid vorausgesetzt. Am Ende werde ich das Script noch etwas abändern und Adminer hinzufügen. Da das aber im Normalfall nicht benötigt wird, gehe ich auch nicht gesondert darauf ein.

Nun, da jetzt Docker-Compose läuft, lege ich eine NginX-Konfigurationsdatei an. Dafür erstelle ich unter appdata einen weiteren Ordner Namens Composevolumes. Dort lege ich dann auch die einzelnen Docker-Compose-Configs ab, damit die Übersicht erhalten bleibt und das Ganze nicht mit den normalen Docker-Configs von Unraid vermischt wird:

mkdir -p /mnt/user/appdata/Composevolumes/Nextcloud-Nginx
nano /mnt/user/appdata/Composevolumes/Nextcloud-Nginx/nginx.conf
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;
        }
    }
}

Strg-x, y, Enter

Quelle:

https://github.com/nextcloud/docker/blob/master/.examples/docker-compose/with-nginx-proxy/postgres/fpm/web/nginx.conf

.

Portainer:

Nun erstelle ich im Portainer das Docker-Compose-Script. Die Config-Dateien der jeweiligen Docker lege ich ebenfalls in das Dockervolumes-Verzeichnis. Somit muss ich am Ende nicht unnötig suchen. Das Nextcloud-Datenverzeichnis lege ich auf das Array zu mnt/user/data/NextcloudComposeData:

  • Stacks
    • + Add stack
      • Name: nextcloudcompose

Folgenden Test-Code füge ich hinzu:

version: '2'

services:
  db:
    image: postgres
    restart: always
    volumes:
      - /mnt/user/appdata/Composevolumes/PostgresData:/var/lib/postgresql/data
    environment:
      - POSTGRES_DB=nextcloud
      - POSTGRES_USER=nextcloud
      - POSTGRES_PASSWORD=PASSWORT456
 
  redis:
    image: redis:alpine
    restart: unless-stopped
    command: redis-server --requirepass PASSWORT789 # Redis Passwort
 
  app:
    image: nextcloud:fpm
    restart: always
    volumes:
      - /mnt/user/appdata/Composevolumes/NextcloudConfig:/var/www/html
      - /mnt/user/data/NextcloudComposeData:/var/www/html/data
    environment:
      - REDIS_HOST=redis
      - REDIS_HOST_PASSWORD=PASSWORT789 # Redis Passwort von oben
      - POSTGRES_HOST=db
      - POSTGRES_DB=nextcloud
      - POSTGRES_USER=nextcloud
      - POSTGRES_PASSWORD=PASSWORT456
    links:
      - db
      - redis
   
  web:
    image: nginx
    restart: always
    ports:
      - 8099:80
    links:
      - app
    volumes:
      - /mnt/user/appdata/Composevolumes/Nextcloud-Nginx/nginx.conf:/etc/nginx/nginx.conf:ro
    volumes_from:
      - app
  
volumes:
  db:
  nextcloud:
  redis:

Anschließend „Deploy the stack“ klicken.

Der Vorgang dauert nun eine ganze Weile. Wurde der Befehl abgearbeitet, erscheint der erste verfügbare Stack – meine soeben erstellte Docker-Compose.

Klickt man diesen Stack an, erscheint eine Übersicht der laufenden Docker im Stack:

.

Bevor man nun Nextcloud im Browser startet, ca. 1 Minute warten, damit die Datenbank initialisiert werden kann. Ansonsten erhält man einen „502 Bad Gateway“ – Fehler.

Um Nextcloud zu starten, im Browser folgendes eingeben:

Server-IP:8099

Hier muss man nur ein Benutzer-Konto anlegen. Der Rest, Datenbankzuweisung und Zugangsnamen und Passwort, wird im Script direkt zugewiesen und benötigt keine weitere Aufmerksamkeit.

Verwendet man Nextcloud nur intern, kann man das nun Folgende vernachlässigen. Aber dafür ist das eigentlich zu schade 🙂 . Obendrein verwende ich hier das originale Nextcloud. Das heißt, dass der interne Zugriff über http erfolgt. Interner Zugriff über http s, wenn auch nur Selbstsigniert, kann nur linuxserver/nextcloud. Von anderen, selbstgebastelten Nextcloudimages, mal abgesehen.

Jetzt wäre es sinnvoll, Nginx Proxy Manager als normale Dockeranwendung zu installieren. Hier ist der Guide dazu. Das Grafana-Beispiel am Ende des Guides wird nicht benötigt. Ich richte jetzt NPM für die hier im Guide vorgestellte Nextcloud-Version ein. Meine erstellte Testdomain lautet troubadix.duckdns.org. Auf dieser Domain bezieht sich das Folgende.

Nginx Proxy Manager für Nextcloud einrichten:

„NginX Proxy Manager“ Weboberfläche:

  • „Host“
    • „Proxy Host“
      • „Add Proxy Host“
        • Details:
          • Domain Names: nextcloud.troubadix.duckdns.org
            • (Eigene Domain verwenden)
          • Scheme: http
            • (8099 ist ein HTTP-Port)
          • Forward Hostname / IP: die Server-IP, worauf sich Nextcloud befindet
          • Forward Port: 8099
            • (8099 ist mein Nextcloud – Port)
          • Cache Assets: On
          • Websockets Support: On
          • Block Common Exploits: On
          • Access List: nichts ändern
        • SSL:
          • SSL Certificate: „Request a new SSL Certificate“
          • Force SSL: On
          • Http/2 Support: On
          • HSTS Enabled: ON
          • Email Address for Lets‘ Encrypt: Eine gültige Email-Adresse
          • I Agree to the LE Terms of Service: On
        • Advanced:
location ^~ /.well-known {
location = /.well-known/carddav     { return 301 /remote.php/dav/; }
location = /.well-known/caldav      { return 301 /remote.php/dav/; }
location ^~ /.well-known            { return 301 /index.php$uri; }
try_files $uri $uri/ =404;
  }

Save

Nach ca. 10-20 Sekunden wurde das Zertifikat erstellt und der Dienst Nextcloud ist unter der angewendeten Domain, gesichert, von Außen erreichbar. Allerdings würde jetzt Nextcloud den Zugriff mit folgendem Fehler verweigern:

Um das zu beheben, muss ich noch die Konfigurationsdatei ändern.

Nextcloud-Konfigurationsdatei anpassen

(eigenes Konfigurationsverzeichnis verwenden)

nano /mnt/user/appdata/Composevolumes/NextcloudConfig/config/config.php

Folgendes am Ende, aber vor dem letzten ); einfügen:

  'default_phone_region' => 'DE', 
  'overwriteprotocol' => 'https',
  'trusted_proxies' => 
   array (
     0 => '192.168.1.110:80',
   ),

Die Ip-Adresse unter Trusted Proxies ist die IP-Adresse meines „Nginx Proxy Manager“ inkl. Portangabe. Ich verwende dafür ein anderes Gerät. Deshalb steht dort auch eine andere IP-Adresse.

Die Zeile:

'overwrite.cli.url' => 'http://192.168.1.200:8099',

ändern in (eigene Domain verwenden):

'overwrite.cli.url' => 'https://nextcloud.troubadix.duckdns.org',

Dann die „Trusted Domains“ erweitern:

  array (
      0 => '192.168.1.200:8099',
      1 => 'nextcloud.troubadix.duckdns.org',
     2=> 'localhost',
  ),

Strg-x, y, Enter

Info: So in etwa sieht die Nextcloud-Config zum Schluss aus, wenn diese geändert wurde:

Bei Trusted Proxies wird die IP-Adresse:Port des Nginx Proxy Managers angegeben. Die IP-Adresse unter Trusted Domains ist die IP des Servers, auf dem NC installiert ist. Da ich NPM auf einem extra Gerät am Laufen habe, sind bei mir 2 verschiedene IP-Adressen angegeben.

Beispiel-Config:

<?php
$CONFIG = array (
  'htaccess.RewriteBase' => '/',
  'memcache.local' => '\\OC\\Memcache\\APCu',
  'apps_paths' =>
  array (
    0 =>
    array (
      'path' => '/var/www/html/apps',
      'url' => '/apps',
      'writable' => false,
    ),
    1 =>
    array (
      'path' => '/var/www/html/custom_apps',
      'url' => '/custom_apps',
      'writable' => true,
    ),
  ),
  'memcache.distributed' => '\\OC\\Memcache\\Redis',
  'memcache.locking' => '\\OC\\Memcache\\Redis',
  'redis' =>
  array (
    'host' => 'nextcloud-redis',
    'password' => 'PASSWORT789',
    'port' => 6379,
  ),
  'instanceid' => 'ogkft892jtbh',
  'passwordsalt' => 'cRWVUgFVJjl3jiOtBTGd2QnzVAlHRA',
  'secret' => 'SD0QceN3HJT+bBjDw2J31/mFGwK0xJMdwjVBmdq+Fw2sJbd1',
  'trusted_domains' =>
  array (
    0 => '192.168.1.200:8099',
    1 => 'nextcloud.troubadix.duckdns.org',
    2=> 'localhost',
  ),
  'datadirectory' => '/var/www/html/data',
  'dbtype' => 'pqsql',
  'version' => '23.0.0.10',
  'overwrite.cli.url' => 'https://nextcloud.troubadix.duckdns.org',
  'dbname' => 'nextcloud',
  'dbhost' => 'db',
  'dbport' => '',
  'dbtableprefix' => 'oc_',
  'mysql.utf8mb4' => true,
  'dbuser' => 'nextcloud',
  'dbpassword' => 'PASSWORT456',
  'installed' => true,
  'default_phone_region' => 'DE',
  'overwriteprotocol' => 'https',
  'trusted_proxies' =>
  array (
    0 => '192.168.1.50:80',
  ),
);

Optional: Stack neu starten:

Im Portainer den erstellten Stack auswählen, alle darin befindlichen Docker selektieren und Restart klicken. Das ist aber nur optional und muss nicht abgearbeitet werden!

Nextcloud kann nun über die Domain geöffnet werden. In meinem Fall ist das:

nextcloud.troubadix.duckdns.org

Es wird automatisch zu http s verbunden und Nextcloud ist, gesichert, erreichbar.

Wurde alles soweit übernommen, sind die Sicherheitshinweise, außer „Dem Modul php-imagick fehlt die SVG-Unterstützung“ behoben. Doch das ist relativ egal…

SSL Labs bringt ein A+ zur Ausgabe.

Adminer im Compose-Script:

Wer Adminer verwenden möchte, sei es um einfach Einsicht in der Datenbank zu erhalten oder um ein Backup (Export) der Datenbank anzulegen, kann dieses erweiterte Compose-Script verwenden. Hier sind alle Sachen von oben mit drin, inkl. hinzugefügtem Adminer:

version: '2'

services:
  db:
    image: postgres
    restart: always
    volumes:
      - /mnt/user/appdata/Composevolumes/PostgresData:/var/lib/postgresql/data
    environment:
      - POSTGRES_DB=nextcloud
      - POSTGRES_USER=nextcloud
      - POSTGRES_PASSWORD=PASSWORT456
 
  adminer:
    image: adminer
    restart: always
    ports:
      - 8098:8080
    links:
      - db

  redis:
    image: redis:alpine
    restart: unless-stopped
    command: redis-server --requirepass PASSWORT789 # Redis Passwort
 
  app:
    image: nextcloud:fpm
    restart: always
    volumes:
      - /mnt/user/appdata/Composevolumes/NextcloudConfig:/var/www/html
      - /mnt/user/data/NextcloudComposeData:/var/www/html/data
    environment:
      - REDIS_HOST=redis
      - REDIS_HOST_PASSWORD=PASSWORT789 # Redis Passwort von oben
      - POSTGRES_HOST=db
      - POSTGRES_DB=nextcloud
      - POSTGRES_USER=nextcloud
      - POSTGRES_PASSWORD=PASSWORT456
    links:
      - db
      - redis
   
  web:
    image: nginx
    restart: always
    ports:
      - 8099:80
    links:
      - app
    volumes:
      - /mnt/user/appdata/Composevolumes/Nextcloud-Nginx/nginx.conf:/etc/nginx/nginx.conf:ro
    volumes_from:
      - app
  
volumes:
  db:
  nextcloud:
  redis:
  adminer:

Aufruf von Adminer im Browser: Server-IP:8098

Datenbank System: PostgreSQL

Benutzer, Passwort und Datenbank eingeben, wie es im Script angelegt wurde.

Hierbei rate ich allerdings dringlichst ab, an dieser Stelle im Nginx Proxy Manager eine weitere Subdomain anzulegen, um darüber Adminer aufzurufen. Das sollte IMMER Local verwendet werden! Wer, aus welchen Gründen auch immer, nicht anders kann, sollte dann unbedingt Benutzer und Datenbank umbenennen und verschiedene Namen verwenden. Hierbei ist es auch sinnvoll, im NPM, ein separates Web-Passwort, für die Adminer-Domain, anzulegen.

.

Erstelle eine Website wie diese mit WordPress.com
Jetzt starten