feat(nextcloud): switch to fpm-alpine + nginx sidecar + Redis caching wiring
Drop Apache+mod_php for the actual performance win — nginx + PHP-FPM — without the LinuxServer image cascade (custom auto-install, /custom-cont-init.d, abc-vs- www-data rewrites in the auth adapter + every tool, HTTPS-by-default quirks). The official fpm-alpine image keeps env-var auto-install and the www-data user, so the auth adapter, all tools, and the compose-tags hook keep working unchanged. - Compose: nextcloud-service is now fpm-alpine (still container_name=nextcloud- service so docker exec ... nextcloud-service php occ in the auth adapter is untouched). New nextcloud-web nginx sidecar serves :80 over the shared ./html volume, terminating FastCGI to nextcloud-service:9000. Traefik labels + PORTS_ TAG_1 move to nextcloud-web (the HTTP face); backup.files stays on -service (the file-owning brain). nextcloud-db + nextcloud-redis unchanged. - resources/nginx.conf: Nextcloud's recommended nginx config, trimmed for behind-Traefik (no TLS), large-upload + caldav/carddav/.well-known redirects. - scripts/nextcloud_update_specifics.sh: NEW post-install hook — appUpdateSpecifics_nextcloud waits for first-boot occ install to complete (config.php + occ status=installed), then wires Redis as memcache.distributed + memcache.locking via occ config:system:set. Idempotent. Auto-install is unchanged (official image's NEXTCLOUD_ADMIN_USER + MYSQL_* env flow). Redis caching now actually USED by Nextcloud (previously the container was up but config.php had no memcache config). Container-side backup capture still the right answer for the perm boundary — image change doesn't affect it. Verified statically: yaml structure, hook parses + dispatches + has the right graceful-timeout fallback when occ isn't reachable. Live verification (sync performance + actual Redis hit rate + traefik proxy of FastCGI) needs a fresh install on a throwaway box. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> Signed-off-by: librelad <librelad@digitalangels.vip>
This commit is contained in:
parent
447b9432f0
commit
d30c309d1d
@ -3,8 +3,13 @@ networks:
|
||||
external: true
|
||||
|
||||
services:
|
||||
# PHP-FPM backend. Same container_name as before (nextcloud-service) so the
|
||||
# auth adapter + tools (`docker exec ... nextcloud-service php occ`) keep
|
||||
# working unchanged. The image's entrypoint runs occ maintenance:install
|
||||
# automatically on first boot when the NEXTCLOUD_ADMIN_USER + MYSQL_* env
|
||||
# vars are set, so no user wizard.
|
||||
nextcloud-service: #LIBREPORTAL|SERVICE_TAG_1|nextcloud-service
|
||||
image: nextcloud:31-apache
|
||||
image: nextcloud:31-fpm-alpine
|
||||
container_name: nextcloud-service
|
||||
environment:
|
||||
- TZ=TIMEZONE_DATA #LIBREPORTAL|TIMEZONE_TAG|TIMEZONE_DATA
|
||||
@ -16,10 +21,6 @@ services:
|
||||
- NEXTCLOUD_ADMIN_PASSWORD=NEXTCLOUD_ADMIN_PASSWORD_DATA #LIBREPORTAL|NEXTCLOUD_ADMIN_PASSWORD_TAG|NEXTCLOUD_ADMIN_PASSWORD_DATA
|
||||
- NEXTCLOUD_TRUSTED_DOMAINS=NEXTCLOUD_TRUSTED_DOMAINS_DATA #LIBREPORTAL|NEXTCLOUD_TRUSTED_DOMAINS_TAG|NEXTCLOUD_TRUSTED_DOMAINS_DATA
|
||||
- REDIS_HOST=nextcloud-redis
|
||||
# GLUETUN_OFF_BEGIN
|
||||
ports:
|
||||
- "PORTS_DATA_1" #LIBREPORTAL|PORTS_TAG_1|PORTS_DATA_1
|
||||
# GLUETUN_OFF_END
|
||||
volumes:
|
||||
- ./html:/var/www/html
|
||||
restart: unless-stopped
|
||||
@ -33,16 +34,6 @@ services:
|
||||
libreportal.title: "TITLE_DATA" #LIBREPORTAL|TITLE_TAG|TITLE_DATA
|
||||
libreportal.backup.db: "mariadb:nextcloud-db:db_data:"
|
||||
libreportal.backup.files: "nextcloud-service:/var/www/html:html:33:33"
|
||||
traefik.enable: TRAEFIK_ENABLE_DATA #LIBREPORTAL|TRAEFIK_ENABLE_TAG|TRAEFIK_ENABLE_DATA
|
||||
# TRAEFIK_PORT_1_BEGIN
|
||||
traefik.http.routers.nextcloud-service.entrypoints: web,websecure
|
||||
traefik.http.routers.nextcloud-service.rule: Host(`DOMAINSUBNAME_DATA_1`) #LIBREPORTAL|DOMAINSUBNAME_TAG_1|DOMAINSUBNAME_DATA_1
|
||||
traefik.http.routers.nextcloud-service.tls: true
|
||||
traefik.http.routers.nextcloud-service.tls.certresolver: production
|
||||
traefik.http.services.nextcloud-service.loadbalancer.server.port: PORT_INTERNAL_DATA_1 #LIBREPORTAL|PORT_INTERNAL_TAG_1|PORT_INTERNAL_DATA_1
|
||||
traefik.http.routers.nextcloud-service.middlewares: MIDDLEWARE_DATA_1 #LIBREPORTAL|MIDDLEWARE_TAG_1|MIDDLEWARE_DATA_1
|
||||
# TRAEFIK_PORT_1_END
|
||||
traefik.docker.network: DOCKER_NETWORK_DATA #LIBREPORTAL|DOCKER_NETWORK_TAG|DOCKER_NETWORK_DATA
|
||||
# GLUETUN_OFF_BEGIN
|
||||
networks:
|
||||
DOCKER_NETWORK_DATA: #LIBREPORTAL|DOCKER_NETWORK_TAG|DOCKER_NETWORK_DATA
|
||||
@ -71,12 +62,50 @@ services:
|
||||
nextcloud-redis: #LIBREPORTAL|SERVICE_TAG_3|nextcloud-redis
|
||||
image: redis:alpine
|
||||
container_name: nextcloud-redis
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./redis_data:/data
|
||||
networks:
|
||||
DOCKER_NETWORK_DATA: #LIBREPORTAL|DOCKER_NETWORK_TAG|DOCKER_NETWORK_DATA
|
||||
ipv4_address: IP_DATA_3 #LIBREPORTAL|IP_TAG_3|IP_DATA_3
|
||||
|
||||
# nginx sidecar — terminates HTTP/static, proxies *.php to nextcloud-service:9000
|
||||
# via FastCGI. Shares ./html (read-only) with the FPM container. This is the
|
||||
# whole performance win over Apache+mod_php: no per-request PHP init, nginx
|
||||
# handles static + serves connections through the FPM process pool.
|
||||
nextcloud-web: #LIBREPORTAL|SERVICE_TAG_4|nextcloud-web
|
||||
image: nginx:alpine
|
||||
container_name: nextcloud-web
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- nextcloud-service
|
||||
volumes:
|
||||
- ./html:/var/www/html:ro
|
||||
- ./resources/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
# GLUETUN_OFF_BEGIN
|
||||
ports:
|
||||
- "PORTS_DATA_1" #LIBREPORTAL|PORTS_TAG_1|PORTS_DATA_1
|
||||
# GLUETUN_OFF_END
|
||||
labels:
|
||||
traefik.enable: TRAEFIK_ENABLE_DATA #LIBREPORTAL|TRAEFIK_ENABLE_TAG|TRAEFIK_ENABLE_DATA
|
||||
# TRAEFIK_PORT_1_BEGIN
|
||||
traefik.http.routers.nextcloud-web.entrypoints: web,websecure
|
||||
traefik.http.routers.nextcloud-web.rule: Host(`DOMAINSUBNAME_DATA_1`) #LIBREPORTAL|DOMAINSUBNAME_TAG_1|DOMAINSUBNAME_DATA_1
|
||||
traefik.http.routers.nextcloud-web.tls: true
|
||||
traefik.http.routers.nextcloud-web.tls.certresolver: production
|
||||
traefik.http.services.nextcloud-web.loadbalancer.server.port: PORT_INTERNAL_DATA_1 #LIBREPORTAL|PORT_INTERNAL_TAG_1|PORT_INTERNAL_DATA_1
|
||||
traefik.http.routers.nextcloud-web.middlewares: MIDDLEWARE_DATA_1 #LIBREPORTAL|MIDDLEWARE_TAG_1|MIDDLEWARE_DATA_1
|
||||
# TRAEFIK_PORT_1_END
|
||||
traefik.docker.network: DOCKER_NETWORK_DATA #LIBREPORTAL|DOCKER_NETWORK_TAG|DOCKER_NETWORK_DATA
|
||||
# GLUETUN_OFF_BEGIN
|
||||
networks:
|
||||
DOCKER_NETWORK_DATA: #LIBREPORTAL|DOCKER_NETWORK_TAG|DOCKER_NETWORK_DATA
|
||||
ipv4_address: IP_DATA_4 #LIBREPORTAL|IP_TAG_4|IP_DATA_4
|
||||
# GLUETUN_OFF_END
|
||||
# GLUETUN_ON_BEGIN
|
||||
# network_mode: "container:gluetun-service"
|
||||
# GLUETUN_ON_END
|
||||
|
||||
# >>> libreportal-monitoring >>>
|
||||
#nextcloud-exporter:
|
||||
# container_name: nextcloud-exporter
|
||||
@ -85,7 +114,7 @@ services:
|
||||
# depends_on:
|
||||
# - nextcloud-service
|
||||
# environment:
|
||||
# - NEXTCLOUD_SERVER=http://nextcloud-service:PORT_INTERNAL_DATA_1 #LIBREPORTAL|PORT_INTERNAL_TAG_1|PORT_INTERNAL_DATA_1
|
||||
# - NEXTCLOUD_SERVER=http://nextcloud-web:PORT_INTERNAL_DATA_1 #LIBREPORTAL|PORT_INTERNAL_TAG_1|PORT_INTERNAL_DATA_1
|
||||
# - NEXTCLOUD_USERNAME=NEXTCLOUD_ADMIN_USER_DATA #LIBREPORTAL|NEXTCLOUD_ADMIN_USER_TAG|NEXTCLOUD_ADMIN_USER_DATA
|
||||
# - NEXTCLOUD_PASSWORD=NEXTCLOUD_ADMIN_PASSWORD_DATA #LIBREPORTAL|NEXTCLOUD_ADMIN_PASSWORD_TAG|NEXTCLOUD_ADMIN_PASSWORD_DATA
|
||||
# - NEXTCLOUD_TLS_SKIP_VERIFY=true
|
||||
@ -93,4 +122,3 @@ services:
|
||||
# networks:
|
||||
# DOCKER_NETWORK_DATA: #LIBREPORTAL|DOCKER_NETWORK_TAG|DOCKER_NETWORK_DATA
|
||||
# <<< libreportal-monitoring <<<
|
||||
|
||||
|
||||
90
containers/nextcloud/resources/nginx.conf
Normal file
90
containers/nextcloud/resources/nginx.conf
Normal file
@ -0,0 +1,90 @@
|
||||
# Nginx sidecar config for the Nextcloud PHP-FPM container. Based on the
|
||||
# Nextcloud admin-manual's recommended config, trimmed to what we need behind
|
||||
# Traefik (no TLS here — Traefik terminates).
|
||||
|
||||
worker_processes auto;
|
||||
events { worker_connections 1024; }
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
tcp_nodelay on;
|
||||
server_tokens off;
|
||||
|
||||
keepalive_timeout 65;
|
||||
client_max_body_size 10G; # large uploads
|
||||
fastcgi_buffers 64 4K;
|
||||
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_comp_level 4;
|
||||
gzip_min_length 256;
|
||||
gzip_proxied any;
|
||||
gzip_types application/atom+xml text/javascript application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/wasm 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;
|
||||
|
||||
# Traefik does the TLS, so Nextcloud sees its forwarded headers.
|
||||
map $http_x_forwarded_proto $forwarded_scheme {
|
||||
default off;
|
||||
https on;
|
||||
}
|
||||
|
||||
upstream php-handler {
|
||||
server nextcloud-service:9000;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
root /var/www/html;
|
||||
|
||||
# Security headers (Nextcloud expects these; Traefik may also add some)
|
||||
add_header Referrer-Policy "no-referrer" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Permitted-Cross-Domain-Policies "none" always;
|
||||
add_header X-Robots-Tag "noindex, nofollow" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
fastcgi_hide_header X-Powered-By;
|
||||
|
||||
# .well-known redirects (CalDAV / CardDAV / WebFinger / NodeInfo)
|
||||
location = /.well-known/carddav { return 301 /remote.php/dav/; }
|
||||
location = /.well-known/caldav { return 301 /remote.php/dav/; }
|
||||
location = /.well-known/webfinger{ return 301 /index.php$uri; }
|
||||
location = /.well-known/nodeinfo { return 301 /index.php$uri; }
|
||||
|
||||
# Block sensitive paths
|
||||
location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/) { return 404; }
|
||||
location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) { return 404; }
|
||||
|
||||
index index.php index.html /index.php$request_uri;
|
||||
try_files $uri $uri/ /index.php$request_uri;
|
||||
|
||||
location ~ \.php(?:$|/) {
|
||||
rewrite ^/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+|.+\/richdocumentscode\/proxy)/.+\.php$ /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 $forwarded_scheme;
|
||||
fastcgi_param modHeadersAvailable true;
|
||||
fastcgi_param front_controller_active true;
|
||||
fastcgi_pass php-handler;
|
||||
fastcgi_intercept_errors on;
|
||||
fastcgi_request_buffering off;
|
||||
fastcgi_read_timeout 300;
|
||||
}
|
||||
|
||||
location ~ \.(?:css|js|svg|gif|png|jpg|ico|woff2?|otf|wasm|tflite|map|html|json)$ {
|
||||
try_files $uri /index.php$request_uri;
|
||||
expires 6M;
|
||||
access_log off;
|
||||
}
|
||||
}
|
||||
}
|
||||
39
containers/nextcloud/scripts/nextcloud_update_specifics.sh
Normal file
39
containers/nextcloud/scripts/nextcloud_update_specifics.sh
Normal file
@ -0,0 +1,39 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Post-install/update specifics for Nextcloud — dispatched by appUpdateSpecifics.
|
||||
#
|
||||
# Nextcloud's official image runs `occ maintenance:install` on first boot from the
|
||||
# NEXTCLOUD_ADMIN_USER + MYSQL_* env vars (so no web wizard). After that, wire
|
||||
# Redis as the distributed cache + file-locking backend — this is the actual
|
||||
# performance win for sync-heavy workloads. The redis container is on the same
|
||||
# docker network as nextcloud-service, so the hostname `nextcloud-redis` resolves.
|
||||
# Idempotent: `occ config:system:set` with the same value is a no-op.
|
||||
appUpdateSpecifics_nextcloud() {
|
||||
# Wait for Nextcloud's first-boot init to complete (config.php exists +
|
||||
# occ reports installed). 60 × 2s = 2 minute timeout.
|
||||
local i=0
|
||||
while (( i++ < 60 )); do
|
||||
if runFileOp docker exec -u www-data nextcloud-service test -f /var/www/html/config/config.php 2>/dev/null \
|
||||
&& runFileOp docker exec -u www-data nextcloud-service php occ status 2>/dev/null | grep -q 'installed: true'; then
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
if (( i >= 60 )); then
|
||||
isNotice "Nextcloud not yet initialized; skipping Redis wiring (re-run by reinstalling once it's up)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
isNotice "Wiring Redis caching into Nextcloud..."
|
||||
local rc=0
|
||||
runFileOp docker exec -u www-data nextcloud-service php occ config:system:set memcache.distributed --value '\OC\Memcache\Redis' >/dev/null 2>&1 || rc=1
|
||||
runFileOp docker exec -u www-data nextcloud-service php occ config:system:set memcache.locking --value '\OC\Memcache\Redis' >/dev/null 2>&1 || rc=1
|
||||
runFileOp docker exec -u www-data nextcloud-service php occ config:system:set redis host --value nextcloud-redis >/dev/null 2>&1 || rc=1
|
||||
runFileOp docker exec -u www-data nextcloud-service php occ config:system:set redis port --value 6379 --type=integer >/dev/null 2>&1 || rc=1
|
||||
runFileOp docker exec -u www-data nextcloud-service php occ config:system:set filelocking.enabled --value true --type=boolean >/dev/null 2>&1 || rc=1
|
||||
if [[ $rc -eq 0 ]]; then
|
||||
isSuccessful "Nextcloud → Redis caching + file-locking wired."
|
||||
else
|
||||
isNotice "Redis wiring had errors (non-fatal; Nextcloud still functions without it)"
|
||||
fi
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user