Nextcloud mit Nginx und PHP-FPM

Nextcloud mit Nginx und PHP-FPM

Für Nextcloud stehen 2 verschiedene Docker Images bereit:

  • mit Webserver (Apache2) und PHP-FPM (-apache-Tag, latest [Standard])
  • PHP-FPM ohne Webserver (-fpm-Tag)

Grundlegend unterscheiden sich die Images darin, dass die Webserver-Version direkt an einem HTTP Port binden kann und Anfragen via HTTP/S annehmen kann. Sollte man einen eigenen Webserver / Proxy (z.B. nginx) davor stellen wollen, ist es eine künstliche Verkomplizierung den Apache2 im Container zu nutzen.
In diesem Beispiel wird gezeigt wie ein bereits funktionierender Nginx direkt mit dem PHP-FPM im Docker-Container verbunden wird.

Was ist PHP-FPM

Als FastCGI Process Manager sorgt dieser dafür, dass Anfragen via FastCGI beantwortet werden können. FPM verwaltet seine Kind-Prozesse (starten, stoppen) und verteilt Anfragen an diese.

Kommunikation Webserver & PHP-FPM

Alle Anfragen zwischen PHP-FPM und dem Webserver erfolgen über das FastCGI Protokoll. In diesem stehen beispielsweise die Informationen aus dem HTTP-Request, Nutzdaten (z.B. von einem POST), die URL und weitere relevante Informationen, je nachdem wie der Webserver konfiguriert ist.

Die Kommunikation erfolgt hierbei via Unix-Socket (lokale "Datei"), oder TCP-IP Socket (Port), wobei in dieser Anleitung beide Fälle der Konfiguration berücksichtigt werden sollen.

Grundsätzlich sieht die Kommunikation folgenderweise aus:

Der Browser fragt eine Datei beim Webserver an. Dieser erkennt, dass es sich z.B. um eine PHP-Datei handelt, oder gibt die Anfrage aufgrund von Rewrite-Rules an den PHP-FPM. Ein Beispiel für den letzten Fall sind Webseiten wo man nur Pfadangaben hat und die PHP-Datei nicht direkt im Pfad steht (z.B. ttp://my.domain/my/profile).

Der Webserver gibt die nötigen Informationen mittels fast-cgi an den Socket von PHP-FPM, welcher die Informationen an seinen Kind-Prozess gibt und dieser versteht, welche Datei er parsen (einlesen) und verarbeiten muss.

Nach der Bearbeitung wird die Ausgabe an den Webserver weitergegeben, etwaige Returncodes (z.B: 404, 401, 200) werden ebenfalls übergeben, sodass der Webserver diese mit dem Inhalt der Antwort an den Browser zurücksenden kann.

Diese Erklärung ist rudimentär und nicht vollumfänglich ;)

Docker-Compose für Nextcloud

# File: /src/docker/nextcloud/docker-compose.yml
version: '3'

services:
  nextcloud:
    container_name: nextcloud
    image: nextcloud:22-fpm
    volumes:
      - /srv/docker/nextcloud/html/:/var/www/html/
      # der Pfad ist für den Unix-Socket nötig, den nginx anfragt
      - /srv/docker/nextcloud/run:/var/run/php/
      # php-fpm Konfigurationen werden hier abgelegt:
      # /srv/docker/nextcloud/config/php-fpm.d) 
      # WENN dieser Ordner eingehangen wird, müssen die Konfigurationsdateien
      # bereits vorliegen, sonst startet der FPM Container NICHT!
      # Weitere Informationen siehe unten (Anpassung der PHP Einstellungen)!
      - /srv/docker/nextcloud/config/php-fpm.d:/usr/local/etc/php-fpm.d/
    restart: always
    # Logindaten liegen außerhalb der compose-datei
    env_file: /srv/docker/nextcloud/config/env_nextcloud
    networks:
      default:
        ipv4_address: 192.168.1.2

  # wird für Caching verwendet
  redis:
    container_name: nextcloud_redis
    image: redis:alpine
    volumes:
      - /srv/docker/nextcloud/redis_data:/data:rw
    restart: unless-stopped
    networks:
      default:
        ipv4_address: 192.168.1.3

  # zur Entlastung des Servers kann die Cloud via Websocket auch pushen
  # hierfür muss aber erst in Nextcloud die App notify_push installiert werden, sonst existiert die Binary zum starten nicht!
  push:
    container_name: nextcloud_push
    image: nextcloud:22-fpm
    volumes:
      - /srv/docker/nextcloud/run:/var/run/php/
      - /srv/docker/nextcloud/html/:/var/www/html/
    restart: always
    # credentials outside the data dir
    env_file: /srv/docker/nextcloud/config/env_push
    working_dir: /var/www/html/config
    command: /var/www/html/custom_apps/notify_push/bin/x86_64/notify_push config.php
    networks:
      default:
        ipv4_address: 192.168.1.4

networks:
  default:
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: 192.168.1.0/24
    driver_opts:
      com.docker.network.bridge.name: br_ncloud

Docker-Compose Environment File

Die Datei wird mittels env_file verlinkt und kann somit für Credentials oder jegliche Environment-Variable genutzt werden, die der Docker-Container unterstützt. Einfach mal bei Nextcloud schauen...

# Hier wird von einem existierenden MySQL-Server ausgegangen!
MYSQL_DATABASE=nextcloud
MYSQL_USER=nextcloud
MYSQL_PASSWORD=MY_SECRET_PASSWORD
# IP wird variieren, es wird davon ausgegangen, dass dieser auf dem Host läuft und somit auf seiner IP erreichbar ist. (127.0.0.1 klappt nicht, wenn die Cloud isoliert ist, weil der Container seine eigene 127.0.0.1 Adresse hat!!!)
MYSQL_HOST=192.168.1.1

# Admin Login
NEXTCLOUD_ADMIN_USER=nextcloud@my-domain.de
NEXTCLOUD_ADMIN_PASSWORD=MY_ADMIN_PASSWORD

# erlaubte Adressen um die Cloud aufzurufen (siehe Anpassungen der nginx Konfiguration)
NEXTCLOUD_TRUSTED_DOMAIN=cloud.example.com

Nginx Konfiguration

Hier bietet sich die Verwendung der offiziellen Dokumention an:
https://docs.nextcloud.com/server/latest/admin_manual/installation/nginx.html

Folgende Änderungen sind empfehlenswert. Dies ist keine vollständige Liste aller Parameter:

# Nicht benötigte Option auskommentieren! Eine darf aktiv sein. (Unix empfohlen)
upstream php-handler {
    # Kommunikation mittels TCP
    server 127.0.0.1:9000;
    # Kommunikation via Unix-Socket (außerhalb des Containers verfügbar)
    # Der Pfad /var/run/php/ ist nur im Container verfügbar, außerhalb ist es der Pfad gemäß der docker-compose.yml
    # siehe /srv/docker/nextcloud/run:/var/run/php
    server unix:/srv/docker/nextcloud/run/nc.sock;
}

server {

    # http2 kann Probleme verursachen, ggf. deaktivieren

    # festes Binden des Servers auf eine IP:Port Kombination
    listen 1.2.3.4:443      ssl http2;
    # wenn kein IPv6 vorhanden oder erwünscht, auskommentieren!
    #listen [::]:443 ssl http2;
    
    # Hostnamen für den Zugriff, sollte mit dem Zertifikat übereinstimmen
    server_name cloud.example.com;

    # der Pfad zu den Daten der Nextcloud
    root /srv/docker/nextcloud/html/;
    
    location ~ \.php(?:$|/) {
      # notwendig, weil im Docker-Container der Pfad der Nextcloud-Daten anders liegt. Sonst würde das try_files in der Konfiguration eine 404 generieren!
      fastcgi_param SCRIPT_FILENAME /var/www/html/$fastcgi_script_name;
      
      # das original würde dem PHP-FPM mitteilen, die Dateien unter dem root (siehe oben) zu suchen, deshalb passen wir den Pfad zu den PHP-Dateien an
      #fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
    
    # Weiterleitung von /push/ an den Container ausführender Push-Binary
    # Nur nötig wenn auch Push installiert und gestartet wird (siehe compose)
    location /push/ {
      proxy_pass http://192.168.125.4:7867/;
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "Upgrade";
      proxy_set_header Host $host;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Wichtige Hinweise (Berechtigungen des Sockets)

Bei Verwendung des Unix-Sockets sollte geprüft werden, ob nginx auf diesen zugreifen darf. Das PHP-FPM im Container wird als Benutzer www-data (UID 33, GID 33) ausgeführt und wird den Socket für die selbe ID anlegen.

Wird nginx mit einer anderen ID ausgeführt (z.B. Archlinux), muss dies korrigiert werden. Unter Ubuntu läuft nginx unter der selben ID und funtioniert somit auf Anhieb.

Anpassung der PHP-Einstellungen

Hier findet man eine Anleitung für die Anpassung von Nextclouds PHP Einstellungen: https://dr3st.de/nextcloud-php-einstellungen-im-docker-setup/

Hier wird auch beschrieben, wie von TCP auf Unix-Socket umgestellt wird.
Dies ist nach Verwendung der obigen docker-compose.yml absolut notwendig, andernfalls startet der Container nicht, weil das config/php-fpm.d Verzeichnis leer ist!