Compare commits

..

45 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
01046015a9 Fix opcache configuration in mastercontainer Dockerfile
Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/bb3ed1bf-d0f7-49a6-a72c-fc6124d7aab2

Co-authored-by: szaimen <42591237+szaimen@users.noreply.github.com>
2026-04-20 14:26:42 +00:00
Simon L.
d7030396cb mastercontianer: fix installation of opcache
Signed-off-by: Simon L. <szaimen@e.mail.de>
2026-04-20 16:07:52 +02:00
Simon L.
8a9c3c4512 perf(apache): improve Apache container performance and correctness (#7959) 2026-04-20 15:58:35 +02:00
copilot-swe-agent[bot]
49a24272f6 perf(apache): improve performance of the Apache container
- nextcloud.conf: increase BrotliCompressionQuality from 0 to 4
- nextcloud.conf: add mod_deflate fallback for non-Brotli clients
- nextcloud.conf: enable HTTP/2 cleartext (h2c) via mod_http2 IfModule block
- nextcloud.conf: remove Options Indexes and duplicate Options line
- nextcloud.conf: remove deprecated Satisfy Any directive
- nextcloud.conf: add KeepAlive On / KeepAliveTimeout 5 / MaxKeepAliveRequests 100
- nextcloud.conf: add EnableSendfile On / EnableMMAP Off
- Dockerfile: enable mod_http2 LoadModule via sed
- Dockerfile: tune mpm_event (MaxRequestWorkers 400, ServerLimit 16,
  ThreadsPerChild 25, StartServers 2, MinSpareThreads 25, MaxSpareThreads 75)
- Caddyfile: add encode gzip zstd for response compression
- supervisord.conf: remove duplicate nodaemon=true

Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/a79198ed-cd31-4e15-9074-b3d3f3f92069

fix(apache): disable HTTP/2, disable Caddy gzip, restore MaxRequestWorkers 5000

- Caddyfile: remove `encode gzip zstd` (disable gzip/zstd compression in Caddy)
- nextcloud.conf: remove HTTP/2 `<IfModule mod_http2.c>` block (disable h2c in Apache)
- Dockerfile: remove mod_http2.so LoadModule sed line
- Dockerfile: restore MaxRequestWorkers 5000, ServerLimit 200, and original comments

Kept from previous commit:
- BrotliCompressionQuality raised from 0 to 4
- mod_deflate fallback for non-Brotli clients
- KeepAlive On / KeepAliveTimeout 5 / MaxKeepAliveRequests 100
- EnableSendfile On / EnableMMAP Off
- ThreadsPerChild 25, StartServers 2, MinSpareThreads 25, MaxSpareThreads 75
- supervisord.conf: remove duplicate nodaemon=true

Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/66a07d0e-2276-4b0e-b17d-53c733530d07

fix(apache): prevent double-encoding, add explanatory comments

- Add SetEnvIfNoCase Accept-Encoding \bbr\b no-gzip inside mod_brotli block
  so mod_deflate is skipped for clients that already get Brotli-compressed
  responses, preventing double-encoding.
- Expand all comments to explain the purpose/benefit of each added directive.

Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/98098727-e2c1-4504-b0d5-47c8b06b794d

docs(apache/Dockerfile): add explanatory comments to all new directives

- Add comment block before the sed/LoadModule block explaining why mpm_event
  is chosen and why mod_brotli is enabled alongside the deflate fallback.
- Add per-line comments for ThreadsPerChild, StartServers, MinSpareThreads,
  and MaxSpareThreads explaining the rationale for each value.

Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/2dc7afc9-b8a1-4b0a-ba13-0ad89e5c2d37

fix(apache): disable EnableSendfile to allow output filters (Brotli/deflate) on static files

Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/54995b4a-7b5d-4e8e-a92a-684d32b42bd4

docs(apache/nextcloud.conf): add per-directive comments to KeepAlive block

Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/329da0e6-b5dd-4cdd-8c05-9ae0bf25ebfd

reduce MaxSpareThreads from 75 to 50

Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/f253e0c7-a192-4bda-96c2-c12a7e1cb81b

raise MaxKeepAliveRequests from 100 to 500 for Nextcloud sync clients

Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/89da74d6-b0d3-4d19-909d-dee06cbad060

remove deflate fallback — all Nextcloud-supported browsers have Brotli

Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/def9ae64-6793-4c8f-ba0c-e36ef6f8f28f

Apply suggestion from @szaimen

Signed-off-by: Simon L. <szaimen@e.mail.de>
Co-Authored-By: szaimen <42591237+szaimen@users.noreply.github.com>
2026-04-20 15:57:02 +02:00
Simon L.
7bae058dbe nextcloud: switch PHP-FPM to dynamic mode and add max_requests to recycle stale workers (#7969) 2026-04-20 15:38:55 +02:00
copilot-swe-agent[bot]
66236c1a2e nextcloud: switch PHP-FPM to dynamic mode and add max_requests to recycle stale workers
Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/29135c39-9a45-49bd-85fe-8d3eea344450

Signed-off-by: Simon L. <szaimen@e.mail.de>

docs: add per-setting inline comments to PHP-FPM sed block in Dockerfile

Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/398bb2f7-e5be-4ffc-942a-7ab10dbaa1be
Co-Authored-By: szaimen <42591237+szaimen@users.noreply.github.com>
2026-04-20 15:37:39 +02:00
Simon L.
091fb8e814 aio-interface: cache reads from GetDailyBackupTimeFile based on mtime (#7957) 2026-04-20 14:58:21 +02:00
copilot-swe-agent[bot]
ee4088744c aio-interface: cache reads from GetDailyBackupTimeFile based on mtime
Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/01d18358-7a1e-4747-85f5-3f002aa59253

aio-interface: cache reads from GetDailyBackupTimeFile based on mtime

Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/4df5e1bd-456d-4005-8432-4e9b537c8e54

fix: set dailyBackupFileMtime default to 0 instead of null

Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/ba492f66-d5f0-450f-b0e1-658806bf070d

fix: restore missing closing brace of deleteDailyBackupTime

Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/6e5fb177-9754-4b64-8e93-b5be989f5162
Co-Authored-By: szaimen <42591237+szaimen@users.noreply.github.com>
2026-04-20 14:50:46 +02:00
Simon L.
ac38ea38b7 talk container update (#7968) 2026-04-20 14:29:21 +02:00
Simon L.
ada407751a PHP dependency updates (#7967) 2026-04-20 14:29:11 +02:00
Simon L.
845d08ba09 perf(postgresql): tune PostgreSQL settings for improved Nextcloud performance (#7944) 2026-04-20 14:26:11 +02:00
szaimen
65a3244a2f talk-update automated change
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-20 12:22:52 +00:00
Simon L.
7c8433d07a feat: improve Redis container performance (#7948) 2026-04-20 14:22:45 +02:00
Simon L.
7e628b1200 perf(nextcloud): tune OPcache, APCu, realpath_cache, output_buffering, and Redis session settings (#7946) 2026-04-20 14:21:36 +02:00
szaimen
8ebd624aa8 php dependency updates
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-20 12:20:28 +00:00
copilot-swe-agent[bot]
fceec6f23e feat: improve Redis container performance
- Disable RDB persistence (--save "") since Redis is used as a pure
  cache and lock store; snapshotting causes fork/CoW pressure and I/O spikes
- Set --maxmemory-policy allkeys-lru to enable LRU eviction and prevent
  unbounded memory growth
- Enable lazyfree background deletions (--lazyfree-lazy-*) to move key
  eviction, expiry, and DEL operations off the main event loop thread
- Enable active memory defragmentation (--activedefrag yes) for long-running
  instances
- Increase background task frequency to --hz 15 for faster key expiry
- Add Transparent Huge Pages (THP) startup warning, consistent with the
  existing vm.overcommit_memory check
- Refactor start.sh to build arguments via a bash array, avoiding eval
  and safely handling the empty-string --save "" and the password argument
- Remove the pre-existing unreachable exec "$@" at the end of start.sh
- Add timeout and read_timeout (1.5s) to standalone Redis config in
  redis.config.php to prevent PHP workers from hanging indefinitely

Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/e68c1986-419f-49b4-9cd0-c1f0efda0351
Co-Authored-By: Simon L. <szaimen@e.mail.de>
Co-Authored-By: szaimen <42591237+szaimen@users.noreply.github.com>
2026-04-20 14:20:27 +02:00
Simon L.
ff86c6d066 perf(fulltextsearch): improve elasticsearch healthcheck and add performance tuning (#7947) 2026-04-20 14:11:20 +02:00
Simon L.
ed672fb99c ContainerDefinitionFetcher: cache the containers.json in apcu (#7951) 2026-04-20 14:09:04 +02:00
Simon L.
ef87e82f13 talk: improve startup ordering, NATS tuning, signaling timeouts, and healthcheck depth (#7945) 2026-04-20 14:01:49 +02:00
copilot-swe-agent[bot]
144c91ae02 ContainerDefinitionFetcher: cache the containers.json in apcu
Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/b8bc4ea2-eee0-4e3f-bd71-d1c5a38c93e9

perf: set apc.shm_size=32M explicitly in mastercontainer Dockerfile

Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/4af50c77-4ac1-4947-9b35-dc66d0d0cc8b

perf: revert all previous changes; cache containers.json in APCu

Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/59281e92-7e5d-40ef-a152-78e0620eb949

perf: address review comments - clarify cache comment, restore readonly constructors

Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/b8bc4ea2-eee0-4e3f-bd71-d1c5a38c93e9
Co-Authored-By: szaimen <42591237+szaimen@users.noreply.github.com>
2026-04-20 13:59:51 +02:00
copilot-swe-agent[bot]
5ead361c04 talk: improve startup ordering, NATS tuning, signaling timeouts, and healthcheck depth
Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/7fd39619-0f63-494c-902c-746f00d17d02
Co-Authored-By: szaimen <42591237+szaimen@users.noreply.github.com>
Signed-off-by: Simon L. <szaimen@e.mail.de>
2026-04-20 13:58:56 +02:00
Simon L.
2b5998e57d aio-interface: enable PHP OPcache and Twig template cache (#7950) 2026-04-20 13:55:33 +02:00
copilot-swe-agent[bot]
3c17a6af36 aio-interface: enable PHP OPcache and Twig template cache
Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/2d974f3d-5f37-47e9-aa1f-00a43bcd9838
Signed-off-by: Simon L. <szaimen@e.mail.de>
Co-Authored-By: szaimen <42591237+szaimen@users.noreply.github.com>
Signed-off-by: Simon L. <szaimen@e.mail.de>
2026-04-20 13:47:59 +02:00
Simon L.
a465baa259 aio-interface: Complete Permissions-Policy header with all MDN-listed directives (#7953) 2026-04-20 13:40:31 +02:00
Simon L.
457f7bfee9 imaginary: improve healthcheck and start.sh (#7949) 2026-04-20 13:40:19 +02:00
copilot-swe-agent[bot]
aade77437c imaginary: improve healthcheck and start.sh
Co-Authored-By: szaimen <42591237+szaimen@users.noreply.github.com>
2026-04-20 13:30:53 +02:00
Simon L.
ccda322888 aio-interface: combine all disable-*.js files into a single disable-containers.js (#7958) 2026-04-20 13:24:50 +02:00
Simon L.
c428bc3b71 aio-interface: preserve login session across container restarts after cookie name change (#7964) 2026-04-20 13:23:38 +02:00
copilot-swe-agent[bot]
479f68d69e aio-interface: combine all disable-*.js files into disable-containers.js
Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/a5859e89-d042-4dd5-82e9-9ceed4e22c70
Co-Authored-By: szaimen <42591237+szaimen@users.noreply.github.com>
Signed-off-by: Simon L. <szaimen@e.mail.de>
2026-04-20 13:15:46 +02:00
Simon L.
e1ae6444e5 increase version of click-handlers
Signed-off-by: Simon L. <szaimen@e.mail.de>
2026-04-20 13:08:47 +02:00
Simon L.
47ee453719 aio-interface: correct variable shadowing in show-password event handler (#7963) 2026-04-20 13:08:08 +02:00
copilot-swe-agent[bot]
e378f7faca aio-interface: preserve login session across container restarts after cookie name change
The session cookie was recently renamed from `PHPSESSID` to
   `__Host-Http-PHPSESSID` (commit 3871179a). When watchtower updates the
   mastercontainer, the browser still holds the old `PHPSESSID` cookie, but the
   new code only looks for `__Host-Http-PHPSESSID`. The old cookie is ignored, a
   fresh unauthenticated session is created, and the user is logged out.

   Fix: before starting the new session in index.php, check if the old `PHPSESSID`
   cookie exists and carries an authenticated session. If it does, destroy the old
   session and mark the new one as authenticated via `SetAuthState(true)`.

Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/cba0ea31-e257-4ec1-82ae-dd66f0f34d98

---

refactor: address review comments - use constant for session key and activity interval

Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/cba0ea31-e257-4ec1-82ae-dd66f0f34d98
Co-Authored-By: szaimen <42591237+szaimen@users.noreply.github.com>
2026-04-20 13:05:41 +02:00
copilot-swe-agent[bot]
4679c6c38d fix: correct variable shadowing in show-password event handler
Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/1927b3c2-7484-4876-b037-79d94f2ecb6a

Co-authored-by: szaimen <42591237+szaimen@users.noreply.github.com>
2026-04-20 10:09:04 +00:00
Simon L.
91e9e58c39 jellyfin: allow Jellyfin to resolve lldap hostname while running in host network mode (#7941) 2026-04-20 11:42:19 +02:00
Simon L.
5624dde376 alpine: fix labels
Signed-off-by: Simon L. <szaimen@e.mail.de>
2026-04-20 11:32:33 +02:00
copilot-swe-agent[bot]
d0b0bde4c8 jellyfin: allow Jellyfin to resolve lldap hostname while running in host network mode
Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/3bd90eb7-e6f2-4647-9e78-4f9349300a29

fix: use gethostbyname for lldap IP and fix community-container depends_on skip logic

Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/67d6ed5f-8bdc-4b9b-a33c-9ff73305c799

fix: ensure lldap starts before jellyfin via depends_on

Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/448ec7d3-f71e-4499-a4a4-67314434a77c

Update ContainerDefinitionFetcher.php

Signed-off-by: Simon L. <szaimen@e.mail.de>

fix: protect standard container dependencies from being skipped for community containers

Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/cdd6a51b-75a3-4f43-9d00-85b1df2f880f
Co-Authored-By: szaimen <42591237+szaimen@users.noreply.github.com>
2026-04-20 11:22:24 +02:00
copilot-swe-agent[bot]
310429c5fd fix: add shm_size=128M to nextcloud container to match apc.shm_size
Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/0b44e4a1-dccc-4541-b4cb-07fceaa046ca

Co-authored-by: szaimen <42591237+szaimen@users.noreply.github.com>
2026-04-19 22:03:09 +00:00
copilot-swe-agent[bot]
98a8861690 Add missing Permissions-Policy directives to headers.Caddyfile
Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/5735ef2f-5264-43ad-a107-433f2770d05f

Co-authored-by: szaimen <42591237+szaimen@users.noreply.github.com>
2026-04-18 10:53:10 +00:00
Simon L.
d2ecff2e06 aio-interface: offer system prune button (#7677) 2026-04-18 12:08:01 +02:00
copilot-swe-agent[bot]
29bac9dbf9 fix(fulltextsearch): clarify healthcheck to explicitly accept green/yellow cluster status
Use filter_path=status to get a minimal JSON response and explicitly match
only green or yellow status (single-node clusters run yellow by design).
This is clearer and more robust than the inverted grep approach.

Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/a036678d-a9f5-4db3-8cc6-3e3d52f4e97f

Co-authored-by: szaimen <42591237+szaimen@users.noreply.github.com>
2026-04-17 15:36:26 +00:00
copilot-swe-agent[bot]
7cd0450dae perf(nextcloud): tune OPcache, APCu, realpath_cache, output_buffering, and Redis session settings
Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/623eef77-7b4e-428b-b06f-e0c7403f8cf6

Co-authored-by: szaimen <42591237+szaimen@users.noreply.github.com>
2026-04-17 15:36:12 +00:00
copilot-swe-agent[bot]
b5dad7927a perf(fulltextsearch): improve elasticsearch healthcheck and add performance tuning settings
- Replace TCP-only nc healthcheck with HTTP cluster health API check via curl,
  so the container is only marked healthy when ES is actually ready (not just
  when the TCP port is open during startup)
- Add indices.fielddata.cache.size=20% to cap field-data cache and prevent
  unbounded heap growth / OOM kills on large datasets
- Add indices.memory.index_buffer_size=20% to improve bulk indexing throughput
  by buffering more data in memory before flushing to disk
- Add thread_pool.write.queue_size=1000 to avoid rejected indexing requests
  when Nextcloud's background jobs submit many files simultaneously

Changes applied to: Containers/fulltextsearch/healthcheck.sh,
php/containers.json, manual-install/latest.yml

Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/a036678d-a9f5-4db3-8cc6-3e3d52f4e97f

Co-authored-by: szaimen <42591237+szaimen@users.noreply.github.com>
2026-04-17 15:35:13 +00:00
copilot-swe-agent[bot]
fcc4d9502d docs(postgresql): clarify work_mem memory allocation semantics in comment
Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/0456b491-e148-4d26-9dc9-e1df9bfbf5d0

Co-authored-by: szaimen <42591237+szaimen@users.noreply.github.com>
2026-04-17 15:34:43 +00:00
copilot-swe-agent[bot]
80ea0c1151 perf(postgresql): tune postgres settings for improved Nextcloud performance
- shared_buffers: 128MB → 256MB (better hot-data caching)
- effective_cache_size: set to 1GB (planner hint, no memory allocated)
- work_mem: 4MB → 16MB (reduces disk spills in file listing / share queries)
- maintenance_work_mem: 64MB → 256MB (faster VACUUM and index builds)
- wal_buffers: auto → 16MB (reduces WAL write latency)
- checkpoint_timeout: 5min → 15min (spreads checkpoint I/O, reduces spikes)
- random_page_cost: 4.0 → 1.1 (SSD-appropriate; favours index scans)
- effective_io_concurrency: 1 → 200 (SSD-appropriate I/O prefetching)
- autovacuum_vacuum_scale_factor: 0.2 → 0.05 (vacuum sooner on large tables)
- autovacuum_analyze_scale_factor: 0.1 → 0.02 (keep planner stats fresh)

All changes are applied via sed in the existing postgresql.conf modification
block and are idempotent across container restarts (handles both commented
defaults on fresh installs and already-set values on restart).

Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/0456b491-e148-4d26-9dc9-e1df9bfbf5d0

Co-authored-by: szaimen <42591237+szaimen@users.noreply.github.com>
2026-04-17 15:33:46 +00:00
Simon L.
f25f588295 aio-interface: offer system prune button
Signed-off-by: Simon L. <szaimen@e.mail.de>
Co-Authored-By: szaimen <42591237+szaimen@users.noreply.github.com>
Co-Authored-By: Pablo Zmdl <57864086+pabzm@users.noreply.github.com>
Signed-off-by: Simon L. <szaimen@e.mail.de>
2026-04-17 14:33:53 +02:00
44 changed files with 470 additions and 143 deletions

View File

@@ -4,9 +4,9 @@ FROM alpine:3.23.4
RUN set -ex; \
apk upgrade --no-cache -a
LABEL org.opencontainers.image.title="Alpine for Nextcloud AIO"
org.opencontainers.image.description="Minimal Alpine Linux base image for Nextcloud All-in-One"
org.opencontainers.image.url="https://github.com/nextcloud/all-in-one"
org.opencontainers.image.source="https://github.com/nextcloud/all-in-one"
org.opencontainers.image.vendor="Nextcloud"
LABEL org.opencontainers.image.title="Alpine for Nextcloud AIO" \
org.opencontainers.image.description="Minimal Alpine Linux image for Nextcloud All-in-One" \
org.opencontainers.image.url="https://github.com/nextcloud/all-in-one" \
org.opencontainers.image.source="https://github.com/nextcloud/all-in-one" \
org.opencontainers.image.vendor="Nextcloud" \
org.opencontainers.image.documentation="https://github.com/nextcloud/all-in-one/blob/main/readme.md"

View File

@@ -60,6 +60,19 @@ RUN set -ex; \
grep -q '<IfModule mpm_event_module>' /usr/local/apache2/conf/extra/httpd-mpm.conf; \
# ServerLimit needs to be set to MaxRequestWorkers divided by ThreadsPerChild which is set to 25 by default
sed -i '/<IfModule mpm_event_module>/a\ \ \ \ ServerLimit 200' /usr/local/apache2/conf/extra/httpd-mpm.conf; \
# Pin ThreadsPerChild so the value is deterministic regardless of the httpd base-image
# defaults; 25 threads per process balances concurrency against per-process memory use.
sed -i 's|ThreadsPerChild.*|ThreadsPerChild 25|' /usr/local/apache2/conf/extra/httpd-mpm.conf; \
# Start two server processes on boot to absorb the first requests without spawning
# new processes on the critical path, while avoiding unnecessary memory overhead.
sed -i 's|StartServers.*|StartServers 2|' /usr/local/apache2/conf/extra/httpd-mpm.conf; \
# Keep at least 25 idle threads (one full process worth) so traffic bursts can be
# absorbed immediately without triggering new process creation.
sed -i 's|MinSpareThreads.*|MinSpareThreads 25|' /usr/local/apache2/conf/extra/httpd-mpm.conf; \
# Retire idle threads above 50 to reclaim memory during quiet periods. 50 is the
# minimum valid value (MinSpareThreads + ThreadsPerChild = 25 + 25) and is enough
# to absorb typical bursts without respawning a new process.
sed -i 's|MaxSpareThreads.*|MaxSpareThreads 50|' /usr/local/apache2/conf/extra/httpd-mpm.conf; \
\
rm -rf /usr/local/apache2/conf/original /var/www; \
mkdir -p /var/www; \

View File

@@ -9,6 +9,34 @@ Listen 8000
ErrorLogFormat "[%t] [%l] [%E] [client: %{X-Forwarded-For}i] [%M] [%{User-Agent}i]"
LogLevel warn
# KeepAlive On: allow the same TCP connection to carry multiple HTTP requests.
# Without this each asset (JS, CSS, image) would require a full TCP handshake,
# which is especially expensive on TLS connections and noticeably slows down
# Nextcloud's login page and file manager that load dozens of resources at once.
KeepAlive On
# KeepAliveTimeout: close an idle keep-alive connection after 5 seconds.
# A short timeout frees Apache worker threads quickly so they are available
# for new requests; 5 s is long enough to cover the gap between requests
# that a browser issues while rendering a page (typically < 1 s), yet short
# enough to avoid holding threads open for idle or slow clients.
KeepAliveTimeout 5
# MaxKeepAliveRequests: allow at most 500 requests per persistent connection.
# 100 (the Apache default) is too low for Nextcloud: the desktop and mobile
# sync clients issue many small API calls (PROPFIND, GET, PUT, checksums …)
# per sync cycle and routinely exceed 100 requests on a single connection.
# Hitting the limit forces a new TCP/TLS handshake, adding latency and CPU
# overhead. 500 gives sync clients enough headroom while still periodically
# recycling threads to contain per-process memory growth.
MaxKeepAliveRequests 500
# sendfile(2) is disabled because it bypasses Apache's output-filter chain: with
# it enabled, mod_brotli is silently skipped for static files (JS, CSS, SVG),
# negating the compression configured below. MMAP is also
# disabled because files can be replaced by Nextcloud at any time and mmap'd
# pages could serve stale data.
EnableSendfile Off
EnableMMAP Off
# PHP match
<FilesMatch "\.php$">
SetHandler "proxy:fcgi://${NEXTCLOUD_HOST}:9000"
@@ -17,20 +45,25 @@ Listen 8000
<Proxy "fcgi://${NEXTCLOUD_HOST}:9000" flushpackets=on>
</Proxy>
# Enable Brotli compression for js, css and svg files - other plain files are compressed by Nextcloud by default
# Compress JS, CSS and SVG responses with Brotli (quality 4 gives good
# compression with reasonable CPU cost; the default of 0 barely compresses).
# Other plain-text files are already compressed by Nextcloud itself.
# No deflate fallback is needed: every browser that Nextcloud supports
# (Chrome 49+, Firefox 44+, Safari 11+, Edge 15+ — all from 2016-2017)
# supports Brotli. Internet Explorer, the only browser that never gained
# Brotli support, was dropped by Nextcloud with NC15 (2019).
# Desktop and mobile sync clients never request JS/CSS/SVG assets.
<IfModule mod_brotli.c>
AddOutputFilterByType BROTLI_COMPRESS text/javascript application/javascript application/x-javascript text/css image/svg+xml
BrotliCompressionQuality 0
BrotliCompressionQuality 4
</IfModule>
# Nextcloud dir
DocumentRoot /var/www/html/
<Directory /var/www/html/>
Options Indexes FollowSymLinks
Options FollowSymLinks MultiViews
Require all granted
AllowOverride All
Options FollowSymLinks MultiViews
Satisfy Any
<IfModule mod_dav.c>
Dav off
</IfModule>

View File

@@ -1,6 +1,5 @@
[supervisord]
nodaemon=true
nodaemon=true
logfile=/var/log/supervisord/supervisord.log
pidfile=/var/run/supervisord/supervisord.pid
childlogdir=/var/log/supervisord/

View File

@@ -1,3 +1,3 @@
#!/bin/bash
nc -z 127.0.0.1 9200 || exit 1
curl -fs "http://127.0.0.1:9200/_cluster/health?filter_path=status" | grep -qE '"status":"(green|yellow)"' || exit 1

View File

@@ -1,3 +1,3 @@
#!/bin/bash
nc -z 127.0.0.1 "$PORT" || exit 1
wget -q -O /dev/null "http://127.0.0.1:${PORT}/health" || exit 1

View File

@@ -1,8 +1,11 @@
#!/bin/bash
echo "Imaginary has started"
if [ -z "$IMAGINARY_SECRET" ]; then
imaginary -return-size -max-allowed-resolution 222.2 "$@"
else
imaginary -return-size -max-allowed-resolution 222.2 -key "$IMAGINARY_SECRET" "$@"
IMAGINARY_ARGS=(-return-size -max-allowed-resolution 222.2)
if [ -n "$IMAGINARY_SECRET" ]; then
IMAGINARY_ARGS+=(-key "$IMAGINARY_SECRET")
fi
imaginary "${IMAGINARY_ARGS[@]}" "$@"

View File

@@ -53,6 +53,16 @@ RUN set -ex; \
build-base; \
pecl install APCu-5.1.28; \
docker-php-ext-enable apcu; \
{ \
echo 'apc.shm_size=32M'; \
} >> /usr/local/etc/php/conf.d/docker-php-ext-apcu.ini; \
{ \
echo 'opcache.enable=1'; \
echo 'opcache.memory_consumption=32'; \
echo 'opcache.interned_strings_buffer=8'; \
echo 'opcache.max_accelerated_files=4000'; \
echo 'opcache.validate_timestamps=0'; \
} > /usr/local/etc/php/conf.d/docker-php-ext-opcache.ini; \
rm -r /tmp/pear; \
runDeps="$( \
scanelf --needed --nobanner --format '%n#p' --recursive /usr/local/lib/php/extensions \

View File

@@ -23,7 +23,7 @@ header {
Cross-Origin-Resource-Policy "same-origin"; # Only allow the same origin to load resources. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Cross-Origin_Resource_Policy
# Permissions-Policy disables browser features that AIO does not use. Since there is no "deny all" option, all known features need to be listed explicitly. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Permissions-Policy
Permissions-Policy "accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(), gamepad=(), geolocation=(), gyroscope=(), hid=(), identity-credentials-get=(), idle-detection=(), local-fonts=(), magnetometer=(), microphone=(), midi=(), otp-credentials=(), payment=(), picture-in-picture=(), publickey-credentials-create=(), publickey-credentials-get=(), screen-wake-lock=(), serial=(), speaker-selection=(), storage-access=(), usb=(), web-share=(), window-management=(), xr-spatial-tracking=()"
Permissions-Policy "accelerometer=(), ambient-light-sensor=(), aria-notify=(), attribution-reporting=(), autoplay=(), battery=(), bluetooth=(), browsing-topics=(), camera=(), captured-surface-control=(), ch-ua-high-entropy-values=(), compute-pressure=(), cross-origin-isolated=(), deferred-fetch=(), deferred-fetch-minimal=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(), gamepad=(), geolocation=(), gyroscope=(), hid=(), identity-credentials-get=(), idle-detection=(), language-detector=(), local-fonts=(), magnetometer=(), microphone=(), midi=(), on-device-speech-recognition=(), otp-credentials=(), payment=(), picture-in-picture=(), private-state-token-issuance=(), private-state-token-redemption=(), publickey-credentials-create=(), publickey-credentials-get=(), screen-wake-lock=(), serial=(), speaker-selection=(), storage-access=(), summarizer=(), translator=(), usb=(), web-share=(), window-management=(), xr-spatial-tracking=()"
-Server
-X-Powered-By

View File

@@ -423,5 +423,11 @@ caddy fmt --overwrite /internal.Caddyfile
# Fix caddy log
chmod 777 /root
# Create Twig template cache directory (path must match TWIG_CACHE_PATH in php/public/index.php)
mkdir -p /tmp/twig-cache
rm -rf /tmp/twig-cache/*
chown www-data:www-data /tmp/twig-cache
chmod 770 /tmp/twig-cache
# Start supervisord
exec /usr/bin/supervisord -c /supervisord.conf

View File

@@ -114,18 +114,18 @@ RUN set -ex; \
# set recommended PHP.ini settings
# see https://docs.nextcloud.com/server/stable/admin_manual/installation/server_tuning.html#enable-php-opcache and below
{ \
echo 'opcache.max_accelerated_files=10000'; \
echo 'opcache.max_accelerated_files=20000'; \
echo 'opcache.memory_consumption=256'; \
echo 'opcache.interned_strings_buffer=64'; \
echo 'opcache.save_comments=1'; \
echo 'opcache.revalidate_freq=60'; \
echo 'opcache.jit=1255'; \
echo 'opcache.jit_buffer_size=8M'; \
echo 'opcache.jit_buffer_size=128M'; \
} > /usr/local/etc/php/conf.d/opcache-recommended.ini; \
\
{ \
echo 'apc.enable_cli=1'; \
echo 'apc.shm_size=64M'; \
echo 'apc.shm_size=128M'; \
} >> /usr/local/etc/php/conf.d/docker-php-ext-apcu.ini; \
\
{ \
@@ -135,6 +135,9 @@ RUN set -ex; \
echo 'max_execution_time=${PHP_MAX_TIME}'; \
echo 'max_input_time=-1'; \
echo 'default_socket_timeout=${PHP_MAX_TIME}'; \
echo 'output_buffering=0'; \
echo 'realpath_cache_size=8M'; \
echo 'realpath_cache_ttl=600'; \
} > /usr/local/etc/php/conf.d/nextcloud.ini; \
\
{ \
@@ -142,7 +145,10 @@ RUN set -ex; \
echo 'session.save_path = "tcp://${REDIS_HOST}:${REDIS_PORT}?database=${REDIS_DB_INDEX}${REDIS_USER_AUTH}&auth[]=${REDIS_HOST_PASSWORD}"'; \
echo 'redis.session.locking_enabled = 1'; \
echo 'redis.session.lock_retries = -1'; \
echo 'redis.session.lock_wait_time = 10000'; \
echo '; 100ms in microseconds - prevents timeout on long requests such as large file uploads'; \
echo 'redis.session.lock_wait_time = 100000'; \
echo '; prevents stale locks from crashed workers (seconds)'; \
echo 'redis.session.lock_expire = 60'; \
echo 'session.gc_maxlifetime = 86400'; \
} > /usr/local/etc/php/conf.d/redis-session.ini; \
\
@@ -238,12 +244,27 @@ RUN set -ex; \
imagemagick-tiff \
coreutils; \
\
grep -q '^pm = dynamic' /usr/local/etc/php-fpm.d/www.conf; \
sed -i 's/^pm = dynamic/pm = ondemand/' /usr/local/etc/php-fpm.d/www.conf; \
# Sync this with max db connections and MaxRequestWorkers
# We don't actually expect so many children but don't want to limit it artificially because people will report issues otherwise.
# Also children will usually be terminated again after the process is done due to the ondemand setting
# Use dynamic pm mode: spare workers stay alive between requests so every request is served immediately
# without waiting for a new process to spawn (unlike ondemand which forks on every request when idle).
# pm.max_children: upper bound on worker processes; synced with max DB connections and MaxRequestWorkers.
# Set high so users never hit an artificial limit under peak load — spare-server bounds keep idle memory usage low.
sed -i 's/^pm.max_children =.*/pm.max_children = 5000/' /usr/local/etc/php-fpm.d/www.conf; \
# pm.start_servers: number of workers pre-forked at container startup.
# Having 2 workers ready immediately means the first requests after boot are served without any spawn delay.
sed -i '/^;pm.start_servers/s/^;//' /usr/local/etc/php-fpm.d/www.conf; \
sed -i 's/^pm.start_servers =.*/pm.start_servers = 2/' /usr/local/etc/php-fpm.d/www.conf; \
# pm.min_spare_servers: floor of idle workers kept alive at all times.
# Guarantees at least 1 ready worker so a sudden burst of requests is handled without any fork wait.
sed -i '/^;pm.min_spare_servers/s/^;//' /usr/local/etc/php-fpm.d/www.conf; \
sed -i 's/^pm.min_spare_servers =.*/pm.min_spare_servers = 1/' /usr/local/etc/php-fpm.d/www.conf; \
# pm.max_spare_servers: ceiling of idle workers kept alive during quiet periods.
# Capping at 3 limits idle memory consumption while still keeping a small ready pool.
sed -i '/^;pm.max_spare_servers/s/^;//' /usr/local/etc/php-fpm.d/www.conf; \
sed -i 's/^pm.max_spare_servers =.*/pm.max_spare_servers = 3/' /usr/local/etc/php-fpm.d/www.conf; \
# pm.max_requests: recycle each worker after handling 500 requests.
# PHP extensions and apps can leak memory over time; recycling prevents those leaks from accumulating indefinitely.
sed -i '/^;pm.max_requests/s/^;//' /usr/local/etc/php-fpm.d/www.conf; \
sed -i 's/^pm.max_requests =.*/pm.max_requests = 500/' /usr/local/etc/php-fpm.d/www.conf; \
sed -i 's|access.log = /proc/self/fd/2|access.log = /proc/self/fd/1|' /usr/local/etc/php-fpm.d/docker.conf; \
\
echo "[ -n \"\$TERM\" ] && [ -f /root.motd ] && cat /root.motd" >> /root/.bashrc; \

View File

@@ -7,6 +7,8 @@ if (getenv('REDIS_MODE') !== 'rediscluster') {
if (getenv('REDIS_HOST')) {
$CONFIG['redis']['host'] = (string) getenv('REDIS_HOST');
$CONFIG['redis']['timeout'] = 1.5;
$CONFIG['redis']['read_timeout'] = 1.5;
}
if (getenv('REDIS_HOST_PASSWORD')) {

View File

@@ -151,23 +151,65 @@ fi
# Modify postgresql.conf
if [ -f "/var/lib/postgresql/data/postgresql.conf" ]; then
echo "Setting postgres values..."
PGCONF="/var/lib/postgresql/data/postgresql.conf"
# Sync this with max pm.max_children and MaxRequestWorkers
# 5000 connections is apparently the highest possible value with postgres so set it to that so that we don't run into a limit here.
# We don't actually expect so many connections but don't want to limit it artificially because people will report issues otherwise
# Also connections should usually be closed again after the process is done
# If we should actually exceed this limit, it is definitely a bug in Nextcloud server or some of its apps that does not close connections correctly and not a bug in AIO
sed -i "s|^max_connections =.*|max_connections = 5000|" "/var/lib/postgresql/data/postgresql.conf"
sed -i "s|^max_connections =.*|max_connections = 5000|" "$PGCONF"
# Do not log checkpoints
if grep -q "#log_checkpoints" /var/lib/postgresql/data/postgresql.conf; then
sed -i 's|#log_checkpoints.*|log_checkpoints = off|' /var/lib/postgresql/data/postgresql.conf
if grep -q "#log_checkpoints" "$PGCONF"; then
sed -i 's|#log_checkpoints.*|log_checkpoints = off|' "$PGCONF"
fi
# Closing idling connections automatically seems to break any logic so was reverted again to default where it is disabled
if grep -q "^idle_session_timeout" /var/lib/postgresql/data/postgresql.conf; then
sed -i 's|^idle_session_timeout.*|#idle_session_timeout|' /var/lib/postgresql/data/postgresql.conf
if grep -q "^idle_session_timeout" "$PGCONF"; then
sed -i 's|^idle_session_timeout.*|#idle_session_timeout|' "$PGCONF"
fi
# Increase shared_buffers from the 128MB default for better data caching
sed -i "s|^#shared_buffers = .*|shared_buffers = 256MB|" "$PGCONF"
sed -i "s|^shared_buffers = .*|shared_buffers = 256MB|" "$PGCONF"
# Hint to the query planner about available OS page cache (does not allocate memory)
sed -i "s|^#effective_cache_size = .*|effective_cache_size = 1GB|" "$PGCONF"
sed -i "s|^effective_cache_size = .*|effective_cache_size = 1GB|" "$PGCONF"
# Increase per-operation sort/hash memory to reduce disk spills for file listing and share queries.
# Note: this is allocated per sort/hash operation, not per connection, so the theoretical worst-case
# (max_connections × work_mem) is rarely approached in practice.
sed -i "s|^#work_mem = .*|work_mem = 16MB|" "$PGCONF"
sed -i "s|^work_mem = .*|work_mem = 16MB|" "$PGCONF"
# Increase memory for VACUUM, CREATE INDEX, and other maintenance operations
sed -i "s|^#maintenance_work_mem = .*|maintenance_work_mem = 256MB|" "$PGCONF"
sed -i "s|^maintenance_work_mem = .*|maintenance_work_mem = 256MB|" "$PGCONF"
# Increase WAL buffers to reduce WAL write latency under concurrent write load
sed -i "s|^#wal_buffers = .*|wal_buffers = 16MB|" "$PGCONF"
sed -i "s|^wal_buffers = .*|wal_buffers = 16MB|" "$PGCONF"
# Spread checkpoint I/O over a longer window to reduce spikes
sed -i "s|^#checkpoint_timeout = .*|checkpoint_timeout = 15min|" "$PGCONF"
sed -i "s|^checkpoint_timeout = .*|checkpoint_timeout = 15min|" "$PGCONF"
# Tune for SSD storage: random reads are nearly as fast as sequential reads
sed -i "s|^#random_page_cost = .*|random_page_cost = 1.1|" "$PGCONF"
sed -i "s|^random_page_cost = .*|random_page_cost = 1.1|" "$PGCONF"
# Allow the kernel to issue more concurrent I/O prefetch requests (suitable for SSDs)
sed -i "s|^#effective_io_concurrency = .*|effective_io_concurrency = 200|" "$PGCONF"
sed -i "s|^effective_io_concurrency = .*|effective_io_concurrency = 200|" "$PGCONF"
# Trigger autovacuum earlier on large Nextcloud tables (e.g. oc_filecache, oc_activity)
# to prevent table bloat accumulating before the default 20% threshold is reached
sed -i "s|^#autovacuum_vacuum_scale_factor = .*|autovacuum_vacuum_scale_factor = 0.05|" "$PGCONF"
sed -i "s|^autovacuum_vacuum_scale_factor = .*|autovacuum_vacuum_scale_factor = 0.05|" "$PGCONF"
sed -i "s|^#autovacuum_analyze_scale_factor = .*|autovacuum_analyze_scale_factor = 0.02|" "$PGCONF"
sed -i "s|^autovacuum_analyze_scale_factor = .*|autovacuum_analyze_scale_factor = 0.02|" "$PGCONF"
fi
do_database_dump() {

View File

@@ -6,12 +6,31 @@ if [ "$(sysctl -n vm.overcommit_memory)" != "1" ]; then
echo "See https://github.com/nextcloud/all-in-one/discussions/1731 how to enable overcommit"
fi
# Run redis with a password if provided
echo "Redis has started"
if [ -n "$REDIS_HOST_PASSWORD" ]; then
exec redis-server --requirepass "$REDIS_HOST_PASSWORD" --loglevel warning
else
exec redis-server --loglevel warning
# Warn if Transparent Huge Pages are enabled (causes latency spikes)
if [ -f /sys/kernel/mm/transparent_hugepage/enabled ]; then
if grep -q '\[always\]' /sys/kernel/mm/transparent_hugepage/enabled; then
echo "WARNING: Transparent Huge Pages (THP) are enabled. This can cause latency and memory issues with Redis."
echo "Consider disabling THP by running: echo never > /sys/kernel/mm/transparent_hugepage/enabled"
fi
fi
exec "$@"
# Build the redis-server argument list.
REDIS_ARGS=(
--loglevel warning
--save "" # Disable RDB persistence (Redis is used as a pure cache/lock store)
--maxmemory-policy allkeys-lru # Evict least-recently-used keys when memory is full
--lazyfree-lazy-eviction yes # Perform evictions in a background thread
--lazyfree-lazy-expire yes # Expire keys in a background thread
--lazyfree-lazy-server-del yes # DEL/UNLINK in background thread
--replica-lazy-flush yes # Flush replica dataset in background thread
--activedefrag yes # Reclaim fragmented memory without restart
--hz 15 # Run background tasks 15×/s (default 10) for faster key expiry
)
if [ -n "$REDIS_HOST_PASSWORD" ]; then
REDIS_ARGS+=(--requirepass "$REDIS_HOST_PASSWORD")
fi
# Run redis with a password if provided
echo "Redis has started"
exec redis-server "${REDIS_ARGS[@]}"

View File

@@ -4,7 +4,7 @@ FROM eturnal/eturnal:1.12.2-alpine AS eturnal
FROM strukturag/nextcloud-spreed-signaling:2.1.1 AS signaling
FROM alpine:3.23.4 AS janus
ARG JANUS_VERSION=v1.4.0
ARG JANUS_VERSION=v1.4.1
WORKDIR /src
RUN set -ex; \
apk upgrade --no-cache -a; \
@@ -82,7 +82,9 @@ RUN set -ex; \
touch \
/etc/nats.conf \
/etc/eturnal.yml; \
echo "listen: 127.0.0.1:4222" | tee /etc/nats.conf; \
# write_deadline: "10s" — without a write deadline, a lagging subscriber can stall the broker indefinitely, blocking all other signaling messages.
# max_payload: 8MB — the default is 1 MB; signaling payloads in large meetings (many participants, ICE candidates) can exceed this, causing dropped messages.
printf 'listen: 127.0.0.1:4222\nwrite_deadline: "10s"\nmax_payload: 8MB\n' | tee /etc/nats.conf; \
mkdir -p \
/var/tmp \
/conf \

View File

@@ -5,3 +5,6 @@ nc -z 127.0.0.1 8188 || exit 1
nc -z 127.0.0.1 4222 || exit 1
nc -z 127.0.0.1 "$TALK_PORT" || exit 1
eturnalctl status || exit 1
# Verify that the signaling server is actually serving requests, not just
# listening on the TCP port (which nc -z above only tests for open port).
wget -q -O /dev/null http://127.0.0.1:8081/api/v1/stats || exit 1

View File

@@ -91,10 +91,12 @@ if [ -z "$TALK_MAX_SCREEN_BITRATE" ]; then
TALK_MAX_SCREEN_BITRATE=2097152
fi
# Signling
# Signaling
cat << SIGNALING_CONF > "/conf/signaling.conf"
[http]
listen = 0.0.0.0:8081
readtimeout = 15
writetimeout = 30
[app]
debug = false
@@ -110,7 +112,9 @@ internalsecret = ${INTERNAL_SECRET}
backends = backend-1
allowall = false
timeout = 10
connectionsperhost = 8
# connectionsperhost: This is the HTTP keep-alive connection pool size from the signaling server to the Nextcloud backend.
# Under load (many concurrent calls joining/leaving simultaneously) a pool of 8 creates a queue bottleneck for backend authentication and session lookups, thus increasing to 32.
connectionsperhost = 32
skipverify = ${SKIP_CERT_VERIFY}
[backend-1]

View File

@@ -7,19 +7,23 @@ logfile_maxbytes=50MB
logfile_backups=10
loglevel=error
[program:eturnal]
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
command=eturnalctl foreground
[program:nats-server]
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
command=nats-server -c /etc/nats.conf
# Start first: signaling depends on NATS being available
priority=10
[program:eturnal]
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
command=eturnalctl foreground
# Start alongside Janus; independent of signaling
priority=20
[program:janus]
stdout_logfile=/dev/stdout
@@ -28,6 +32,8 @@ stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
# debug-level 3 means warning
command=janus --config=/conf/janus.jcfg --disable-colors --log-stdout --full-trickle --debug-level 3
# Start alongside eturnal; signaling connects to Janus via WebSocket
priority=20
[program:signaling]
stdout_logfile=/dev/stdout
@@ -35,3 +41,5 @@ stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
command=nextcloud-spreed-signaling -config /conf/signaling.conf
# Start last: depends on NATS (priority=10) and Janus (priority=20) being up
priority=30

View File

@@ -34,6 +34,9 @@
"enable_nvidia_gpu": true,
"backup_volumes": [
"nextcloud_aio_jellyfin"
],
"depends_on": [
"nextcloud-aio-lldap"
]
}
]

View File

@@ -444,6 +444,9 @@ services:
- http.port=9200
- xpack.license.self_generated.type=basic
- xpack.security.enabled=false
- indices.fielddata.cache.size=20%
- indices.memory.index_buffer_size=20%
- thread_pool.write.queue_size=1000
- FULLTEXTSEARCH_PASSWORD
volumes:
- nextcloud_aio_elasticsearch:/usr/share/elasticsearch/data:rw

20
php/composer.lock generated
View File

@@ -2520,16 +2520,16 @@
},
{
"name": "amphp/socket",
"version": "v2.3.1",
"version": "v2.4.0",
"source": {
"type": "git",
"url": "https://github.com/amphp/socket.git",
"reference": "58e0422221825b79681b72c50c47a930be7bf1e1"
"reference": "dadb63c5d3179fd83803e29dfeac27350e619314"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/amphp/socket/zipball/58e0422221825b79681b72c50c47a930be7bf1e1",
"reference": "58e0422221825b79681b72c50c47a930be7bf1e1",
"url": "https://api.github.com/repos/amphp/socket/zipball/dadb63c5d3179fd83803e29dfeac27350e619314",
"reference": "dadb63c5d3179fd83803e29dfeac27350e619314",
"shasum": ""
},
"require": {
@@ -2538,17 +2538,17 @@
"amphp/dns": "^2",
"ext-openssl": "*",
"kelunik/certificate": "^1.1",
"league/uri": "^6.5 | ^7",
"league/uri-interfaces": "^2.3 | ^7",
"league/uri": "^7",
"league/uri-interfaces": "^7",
"php": ">=8.1",
"revolt/event-loop": "^1 || ^0.2"
"revolt/event-loop": "^1"
},
"require-dev": {
"amphp/php-cs-fixer-config": "^2",
"amphp/phpunit-util": "^3",
"amphp/process": "^2",
"phpunit/phpunit": "^9",
"psalm/phar": "5.20"
"psalm/phar": "6.16.1"
},
"type": "library",
"autoload": {
@@ -2592,7 +2592,7 @@
],
"support": {
"issues": "https://github.com/amphp/socket/issues",
"source": "https://github.com/amphp/socket/tree/v2.3.1"
"source": "https://github.com/amphp/socket/tree/v2.4.0"
},
"funding": [
{
@@ -2600,7 +2600,7 @@
"type": "github"
}
],
"time": "2024-04-21T14:33:03+00:00"
"time": "2026-04-19T15:09:56+00:00"
},
{
"name": "amphp/sync",

View File

@@ -267,6 +267,7 @@
],
"stop_grace_period": 600,
"restart": "unless-stopped",
"shm_size": 134217728,
"devices": [
"/dev/dri"
],
@@ -813,6 +814,9 @@
"http.port=9200",
"xpack.license.self_generated.type=basic",
"xpack.security.enabled=false",
"indices.fielddata.cache.size=20%",
"indices.memory.index_buffer_size=20%",
"thread_pool.write.queue_size=1000",
"FULLTEXTSEARCH_PASSWORD=%FULLTEXTSEARCH_PASSWORD%"
],
"volumes": [

View File

@@ -9,8 +9,8 @@ document.addEventListener("DOMContentLoaded", () => {
document.querySelectorAll('input[data-input-show-password]').forEach((element) => {
element.addEventListener('input', (element) => {
let passwordField = element
element.addEventListener('input', (event) => {
let passwordField = event.target;
if (passwordField.type === "password" && passwordField.value !== "") {
passwordField.type = "text";
} else if (passwordField.type === "text" && passwordField.value === "") {

View File

@@ -1,5 +0,0 @@
document.addEventListener("DOMContentLoaded", function(event) {
// Clamav
let clamav = document.getElementById("clamav");
clamav.disabled = true;
});

View File

@@ -1,5 +0,0 @@
document.addEventListener("DOMContentLoaded", function(event) {
// Collabora
const collabora = document.getElementById("office-collabora");
collabora.disabled = true;
});

View File

@@ -0,0 +1,44 @@
document.addEventListener("DOMContentLoaded", function(event) {
// Clamav
let clamav = document.getElementById("clamav");
clamav.disabled = true;
// Docker socket proxy
let dockerSocketProxy = document.getElementById("docker-socket-proxy");
if (dockerSocketProxy) {
dockerSocketProxy.disabled = true;
}
// HaRP
let harp = document.getElementById("harp");
if (harp) {
harp.disabled = true;
}
// Talk
let talk = document.getElementById("talk");
talk.disabled = true;
// Collabora
const collabora = document.getElementById("office-collabora");
collabora.disabled = true;
// OnlyOffice
const onlyoffice = document.getElementById("office-onlyoffice");
onlyoffice.disabled = true;
// Imaginary
let imaginary = document.getElementById("imaginary");
imaginary.disabled = true;
// Fulltextsearch
let fulltextsearch = document.getElementById("fulltextsearch");
fulltextsearch.disabled = true;
// Talk-recording
document.getElementById("talk-recording").disabled = true;
// Whiteboard
let whiteboard = document.getElementById("whiteboard");
whiteboard.disabled = true;
});

View File

@@ -1,7 +0,0 @@
document.addEventListener("DOMContentLoaded", function(event) {
// Docker socket proxy
let dockerSocketProxy = document.getElementById("docker-socket-proxy");
if (dockerSocketProxy) {
dockerSocketProxy.disabled = true;
}
});

View File

@@ -1,5 +0,0 @@
document.addEventListener("DOMContentLoaded", function(event) {
// Fulltextsearch
let fulltextsearch = document.getElementById("fulltextsearch");
fulltextsearch.disabled = true;
});

View File

@@ -1,7 +0,0 @@
document.addEventListener("DOMContentLoaded", function(event) {
// HaRP
let harp = document.getElementById("harp");
if (harp) {
harp.disabled = true;
}
});

View File

@@ -1,5 +0,0 @@
document.addEventListener("DOMContentLoaded", function(event) {
// Imaginary
let imaginary = document.getElementById("imaginary");
imaginary.disabled = true;
});

View File

@@ -1,5 +0,0 @@
document.addEventListener("DOMContentLoaded", function(event) {
// OnlyOffice
const onlyoffice = document.getElementById("office-onlyoffice");
onlyoffice.disabled = true;
});

View File

@@ -1,4 +0,0 @@
document.addEventListener("DOMContentLoaded", function(event) {
// Talk-recording
document.getElementById("talk-recording").disabled = true;
});

View File

@@ -1,5 +0,0 @@
document.addEventListener("DOMContentLoaded", function(event) {
// Talk
let talk = document.getElementById("talk");
talk.disabled = true;
});

View File

@@ -1,5 +0,0 @@
document.addEventListener("DOMContentLoaded", function(event) {
// Whiteboard
let whiteboard = document.getElementById("whiteboard");
whiteboard.disabled = true;
});

View File

@@ -10,6 +10,9 @@ ini_set('max_execution_time', '7200');
// Log whole log messages
ini_set('log_errors_max_len', '0');
// Path for the Twig compiled-template cache (created at container startup by start.sh)
const TWIG_CACHE_PATH = '/tmp/twig-cache';
use DI\Container;
use DI\NotFoundException;
use Slim\Csrf\Guard;
@@ -37,22 +40,40 @@ $container->set(Guard::class, function () use ($responseFactory) {
});
// Register Middleware To Be Executed On All Routes
// Migrate from the old PHPSESSID cookie to the new __Host-Http-PHPSESSID cookie.
// This is needed because the session cookie was renamed in a previous release. Without this,
// users that were logged in before the update would be logged out after the container restarts.
$wasAuthenticated = false;
if (!isset($_COOKIE['__Host-Http-PHPSESSID']) && isset($_COOKIE['PHPSESSID'])) {
session_name('PHPSESSID');
if (session_start(['save_path' => $dataConst->GetSessionDirectory(), 'use_strict_mode' => true])) {
$wasAuthenticated = isset($_SESSION[\AIO\Auth\AuthManager::SESSION_KEY]) && $_SESSION[\AIO\Auth\AuthManager::SESSION_KEY] === true;
session_unset();
session_destroy();
}
}
session_start([
"name" => "__Host-Http-PHPSESSID", // Set cookie prefix to prevent other pages from overwriting this cookie. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie#cookie_prefixes
"save_path" => $dataConst->GetSessionDirectory(), // Where to save the session files
"cookie_lifetime" => 0, // Delete the session cookie whenever the browser is closed. See https://www.php.net/manual/en/session.configuration.php#ini.session.cookie-lifetime
"gc_maxlifetime" => 86400, // Delete sessions after 24 hours. See https://www.php.net/manual/en/session.configuration.php#ini.session.gc-maxlifetime
"gc_probability" => 1, // Probability that the session cleanup starts. See https://www.php.net/manual/en/session.configuration.php#ini.session.gc-probability
"gc_divisor" => 1, // gc_probability/gc_divisor = 1/1 = 100%, meaning that *all* outdated sessions get deleted when the cleanup job runs. See https://www.php.net/manual/en/session.configuration.php#ini.session.gc-divisor
"gc_probability" => 0, // Probability that the session cleanup starts. The sessions are cleaned up by a cron job instead, see /cron.sh. See https://www.php.net/manual/en/session.configuration.php#ini.session.gc-probability
"gc_divisor" => 100, // gc_probability/gc_divisor = 0/100 = 0%, meaning that PHP will never run session GC itself (cron.sh handles cleanup instead). See https://www.php.net/manual/en/session.configuration.php#ini.session.gc-divisor
"use_strict_mode" => true, // Only allow initialized session IDs. See https://www.php.net/manual/en/session.configuration.php#ini.session.use-strict-mode
"cookie_secure" => true, // Only send cookies over https (not http). See https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie#secure
"cookie_httponly" => true, // Block the cookie from being read with js in the browser, will still be send for fetch request triggered by js. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie#httponly
"cookie_samesite" => "Strict", // Only send the cookie with requests triggered by AIO itself. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie#samesitesamesite-value
]);
if ($wasAuthenticated) {
$container->get(\AIO\Auth\AuthManager::class)->SetAuthState(true);
}
$app->add(Guard::class);
// Create Twig
$twig = Twig::create(__DIR__ . '/../templates/', ['cache' => false]);
$twig = Twig::create(__DIR__ . '/../templates/', ['cache' => TWIG_CACHE_PATH]);
$app->add(TwigMiddleware::create($app, $twig));
$twig->addExtension(new \AIO\Twig\CsrfExtension($container->get(Guard::class)));
@@ -70,6 +91,7 @@ $app->post('/api/docker/backup-check-repair', AIO\Controller\DockerController::c
$app->post('/api/docker/backup-test', AIO\Controller\DockerController::class . ':StartBackupContainerTest');
$app->post('/api/docker/restore', AIO\Controller\DockerController::class . ':StartBackupContainerRestore');
$app->post('/api/docker/stop', AIO\Controller\DockerController::class . ':StopContainer');
$app->post('/api/docker/prune', AIO\Controller\DockerController::class . ':SystemPrune');
$app->get('/api/docker/logs', AIO\Controller\DockerController::class . ':GetLogs');
$app->post('/api/auth/login', AIO\Controller\LoginController::class . ':TryLogin');
$app->get('/api/auth/getlogin', AIO\Controller\LoginController::class . ':GetTryLogin');

View File

@@ -8,7 +8,7 @@ use AIO\Data\DataConst;
use \DateTime;
readonly class AuthManager {
private const string SESSION_KEY = 'aio_authenticated';
public const string SESSION_KEY = 'aio_authenticated';
public function __construct(
private ConfigurationManager $configurationManager

View File

@@ -39,7 +39,17 @@ readonly class ContainerDefinitionFetcher {
*/
private function GetDefinition(): array
{
$data = json_decode((string)file_get_contents(DataConst::GetContainersDefinitionPath()), true, 512, JSON_THROW_ON_ERROR);
$containersDefinitionPath = DataConst::GetContainersDefinitionPath();
$cacheKey = 'containers-json-' . $containersDefinitionPath;
$cachedJson = apcu_fetch($cacheKey);
if (!is_string($cachedJson)) {
$cachedJson = (string)file_get_contents($containersDefinitionPath);
apcu_add($cacheKey, $cachedJson);
}
$data = json_decode($cachedJson, true, 512, JSON_THROW_ON_ERROR);
// We store this information for later because we need to use it to distinct between community containers and default containers.
$standardContainerNames = array_column($data['aio_services_v1'], 'container_name');
$additionalContainerNames = [];
foreach ($this->configurationManager->aioCommunityContainers as $communityContainer) {
@@ -212,6 +222,15 @@ readonly class ContainerDefinitionFetcher {
if (!$this->configurationManager->isWhiteboardEnabled) {
continue;
}
} else {
// Skip dependencies on community containers that are not currently enabled.
// Only apply this when the current entry is itself a community container,
// and the dependency is not an enabled community container or a standard built-in container.
if (in_array($entry['container_name'], $additionalContainerNames, true)
&& !in_array($value, $additionalContainerNames, true)
&& !in_array($value, $standardContainerNames, true)) {
continue;
}
}
$dependsOn[] = $value;
}

View File

@@ -328,6 +328,22 @@ readonly class DockerController {
return $nonbufResp;
}
public function SystemPrune(Request $request, Response $response, array $args) : Response {
// Get streaming response start and closure
$nonbufResp = $this->startStreamingResponse($response);
$body = $nonbufResp->getBody();
$addToStreamingResponseBody = function (string $message) use ($body) : void {
$body->write("<div>$message</div>");
};
$this->dockerActionManager->SystemPrune($addToStreamingResponseBody);
// End streaming response
$this->finalizeStreamingResponse($nonbufResp);
return $nonbufResp;
}
public function stopTopContainer() : void {
$id = self::TOP_CONTAINER;
$this->PerformRecursiveContainerStop($id);

View File

@@ -14,6 +14,10 @@ class ConfigurationManager
private bool $noWrite = false;
private string $dailyBackupFileCache = '';
private int $dailyBackupFileMtime = 0;
public string $aioToken {
get => $this->get('AIO_TOKEN', '');
set { $this->set('AIO_TOKEN', $value); }
@@ -760,23 +764,47 @@ class ConfigurationManager
$time .= PHP_EOL;
}
file_put_contents(DataConst::GetDailyBackupTimeFile(), $time);
$this->dailyBackupFileCache = '';
$this->dailyBackupFileMtime = 0;
}
private function getDailyBackupFileContent() : string {
$file = DataConst::GetDailyBackupTimeFile();
if (!file_exists($file)) {
$this->dailyBackupFileCache = '';
$this->dailyBackupFileMtime = 0;
return '';
}
$mtime = filemtime($file);
if ($mtime !== false && $this->dailyBackupFileMtime === $mtime && $this->dailyBackupFileCache !== '') {
return $this->dailyBackupFileCache;
}
$content = file_get_contents($file);
if ($content === false || $content === '') {
return '';
}
if ($mtime !== false) {
$this->dailyBackupFileCache = $content;
$this->dailyBackupFileMtime = $mtime;
}
return $content;
}
public function getDailyBackupTime() : string {
if (!file_exists(DataConst::GetDailyBackupTimeFile())) {
$content = $this->getDailyBackupFileContent();
if ($content === '') {
return '';
}
$dailyBackupFile = (string)file_get_contents(DataConst::GetDailyBackupTimeFile());
$dailyBackupFileArray = explode("\n", $dailyBackupFile);
$dailyBackupFileArray = explode("\n", $content);
return $dailyBackupFileArray[0];
}
public function areAutomaticUpdatesEnabled() : bool {
if (!file_exists(DataConst::GetDailyBackupTimeFile())) {
$content = $this->getDailyBackupFileContent();
if ($content === '') {
return false;
}
$dailyBackupFile = (string)file_get_contents(DataConst::GetDailyBackupTimeFile());
$dailyBackupFileArray = explode("\n", $dailyBackupFile);
$dailyBackupFileArray = explode("\n", $content);
if (isset($dailyBackupFileArray[1]) && $dailyBackupFileArray[1] === 'automaticUpdatesAreNotEnabled') {
return false;
} else {
@@ -788,11 +816,10 @@ class ConfigurationManager
if (file_exists(DataConst::GetDailyBackupTimeFile())) {
unlink(DataConst::GetDailyBackupTimeFile());
}
$this->dailyBackupFileCache = '';
$this->dailyBackupFileMtime = 0;
}
/**
* @throws InvalidSettingConfigurationException
*/
public function setAdditionalBackupDirectories(string $additionalBackupDirectories) : void {
$additionalBackupDirectoriesArray = explode("\n", $additionalBackupDirectories);
$validDirectories = '';

View File

@@ -425,6 +425,13 @@ readonly class DockerActionManager {
// $mounts[] = ["Type" => "bind", "Source" => $volume->name, "Target" => $volume->mountPoint, "ReadOnly" => !$volume->isWritable, "BindOptions" => [ "Propagation" => "rshared"]];
// }
// Special things for the jellyfin community container
} elseif ($container->identifier === 'nextcloud-aio-jellyfin') {
$lldapIp = gethostbyname('nextcloud-aio-lldap');
if ($lldapIp !== 'nextcloud-aio-lldap') {
$requestBody['HostConfig']['ExtraHosts'] = ['nextcloud-aio-lldap:' . $lldapIp];
}
// Special things for the caddy community container
} elseif ($container->identifier === 'nextcloud-aio-caddy') {
$requestBody['HostConfig']['ExtraHosts'] = ['host.docker.internal:host-gateway'];
@@ -997,4 +1004,71 @@ readonly class DockerActionManager {
return $this->dockerHubManager->GetLatestDigestOfTag($imageName, $tag);
}
}
public function SystemPrune(?\Closure $addToStreamingResponseBody = null): void {
$endpoints = [
// Remove stopped containers
'containers/prune',
// Remove unused images
'images/prune',
// Remove unused volumes
'volumes/prune',
// Remove unused networks
'networks/prune',
// Prune build cache
'build/prune',
];
foreach ($endpoints as $endpoint) {
// Special-case images prune to include the dangling filter as requested
if ($endpoint === 'images/prune') {
$filters = json_encode(['dangling' => ['false']]);
$url = $this->BuildApiUrl($endpoint . '?filters=' . urlencode((string) $filters));
} else {
$url = $this->BuildApiUrl($endpoint);
}
if ($addToStreamingResponseBody !== null) {
$addToStreamingResponseBody("Running $endpoint...");
}
try {
$response = $this->guzzleClient->post($url);
if ($addToStreamingResponseBody !== null) {
$data = json_decode((string)$response->getBody(), true);
$deleted = 0;
foreach (['ContainersDeleted', 'ImagesDeleted', 'VolumesDeleted', 'NetworksDeleted', 'CachesDeleted'] as $key) {
if (isset($data[$key]) && is_array($data[$key])) {
$deleted += count($data[$key]);
}
}
$reclaimed = $data['SpaceReclaimed'] ?? 0;
$parts = [];
if ($deleted > 0) {
$parts[] = "$deleted item(s) deleted";
}
if ($reclaimed > 0) {
$i = (int)floor(log($reclaimed, 1024));
$parts[] = 'Space reclaimed: ' . (string)round($reclaimed / (1024 ** $i), 2) . ' ' . ['B','KB','MB','GB'][$i];
}
$addToStreamingResponseBody(!empty($parts) ? implode('. ', $parts) . '.' : 'Nothing to prune.');
}
} catch (RequestException $e) {
error_log(sprintf('Docker prune (%s) failed: %s', $endpoint, $e->getMessage()));
if ($addToStreamingResponseBody !== null) {
$addToStreamingResponseBody('Error: ' . $e->getMessage());
}
// continue with next prune step
}
}
if ($addToStreamingResponseBody !== null) {
$addToStreamingResponseBody("Docker system prune completed.");
sleep(1);
// We automatically reload after 10s so that the output can be read or copied if necessary
$addToStreamingResponseBody("Automatically reloading the page after 10s.");
sleep(10);
}
}
}

View File

@@ -582,13 +582,25 @@
{% if is_backup_container_running == false %}
{% if isApacheStarting == false %}
{% if isAnyRunning == true %}
<h2>Docker System Prune</h2>
<details>
<summary>Click here to reveal a button to prune the docker system.</summary>
<p>By clicking the button below you can run "docker system prune -a". This will remove unused images, containers, networks, volumes and build cache. It will not delete data of running containers.</p>
<form method="POST" action="api/docker/prune" target="overlay-log">
<input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}">
<input type="hidden" name="{{csrf.keys.value}}" value="{{csrf.value}}">
<input type="submit" value="Prune docker system" data-confirm="Run docker system prune -a? This will remove unused images, containers, networks, volumes and build cache. It will not delete data of running containers. Continue?" />
</form>
</details>
{% endif %}
<h2>AIO passphrase change</h2>
<details>
<summary>Click here to change your AIO passphrase</summary>
<p>You can change your AIO passphrase below:</p>
<form method="POST" action="api/configuration" class="xhr">
<input type="password" autocomplete="current-password" name="current-master-password" placeholder="Your current AIO passphrase" id="current-master-password" data-input-show-password="showPassword('current-master-password')">
<input type="password" autocomplete="new-password" name="new-master-password" placeholder="Your new AIO passphrase" id="new-master-password" data-input-show-password="showPassword('new-master-password')">
<input type="password" autocomplete="current-password" name="current-master-password" placeholder="Your current AIO passphrase" id="current-master-password" data-input-show-password>
<input type="password" autocomplete="new-password" name="new-master-password" placeholder="Your new AIO passphrase" id="new-master-password" data-input-show-password>
<input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}">
<input type="hidden" name="{{csrf.keys.value}}" value="{{csrf.value}}">
<input type="submit" value="Submit passphrase change" />

View File

@@ -224,16 +224,7 @@
</form>
<p><strong>Minimal system requirements:</strong> When any optional container is enabled, at least 2GB RAM, a dual-core CPU and 40GB system storage are required. When enabling ClamAV, Nextcloud Talk Recording-server or Fulltextsearch, at least 3GB RAM are required. For Talk Recording-server additional 2 vCPUs are required. When enabling everything, at least 5GB RAM and a quad-core CPU are required. Recommended are at least 1GB more RAM than the minimal requirement. For further advice and recommendations see <strong><a target="_blank" href="https://github.com/nextcloud/all-in-one/discussions/1335">this documentation</a></strong></p>
{% if isAnyRunning == true %}
<script type="text/javascript" src="disable-clamav.js"></script>
<script type="text/javascript" src="disable-docker-socket-proxy.js"></script>
<script type="text/javascript" src="disable-harp.js"></script>
<script type="text/javascript" src="disable-talk.js"></script>
<script type="text/javascript" src="disable-collabora.js?v2"></script>
<script type="text/javascript" src="disable-onlyoffice.js?v2"></script>
<script type="text/javascript" src="disable-imaginary.js"></script>
<script type="text/javascript" src="disable-fulltextsearch.js"></script>
<script type="text/javascript" src="disable-talk-recording.js"></script>
<script type="text/javascript" src="disable-whiteboard.js"></script>
<script type="text/javascript" src="disable-containers.js"></script>
{% endif %}
{% if is_collabora_enabled == true and isAnyRunning == false and was_start_button_clicked == true %}

View File

@@ -6,7 +6,7 @@
<link rel="icon" href="img/favicon.png">
<script type="text/javascript" src="forms.js?v2"></script>
<script type="text/javascript" src="toggle-dark-mode.js?v1"></script>
<script type="text/javascript" src="click-handlers.js?v1"></script>
<script type="text/javascript" src="click-handlers.js?v2"></script>
</head>
<body>

View File

@@ -11,7 +11,7 @@
{% if is_login_allowed == true %}
<p>Log in using your Nextcloud AIO passphrase:</p>
<form method="POST" action="api/auth/login" class="xhr">
<input type="password" autocomplete="current-password" name="password" placeholder="Password" id="master-password" data-input-show-password="showPassword('master-password')">
<input type="password" autocomplete="current-password" name="password" placeholder="Password" id="master-password" data-input-show-password>
<input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}">
<input type="hidden" name="{{csrf.keys.value}}" value="{{csrf.value}}">
<input type="submit" class="button" value="Log in" />