I think I got Nextcloud Client Push (notify_push) working persistently with the current Cloudron Nextcloud package.
Tested with:
Cloudron Nextcloud package 5.8.5
Nextcloud 34.0.2
notify_push 1.3.5
This uses only /app/data; it does not modify read-only /app/code or /etc. I verified it across a full Cloudron app restart.
1. Install Client Push
Install Client Push from the Nextcloud Apps page, or run this in the Nextcloud app terminal:
sudo -u www-data php /app/code/occ app:install notify_push || true
sudo -u www-data php /app/code/occ app:enable notify_push
2. Create the persistent daemon runner
Run in the Nextcloud app terminal:
mkdir -p /app/data/notify_push
cat > /app/data/notify_push/runner.sh <<'EOF'
#!/bin/bash
set -u
binary="/app/data/apps/notify_push/bin/x86_64/notify_push"
config="/app/data/config/config.php"
if [[ ! -x "${binary}" ]]; then
echo "notify_push binary is missing: ${binary}" >&2
exit 1
fi
# Apache supplies a pipe on stdin. Preserve and drain it so requests cannot block.
exec 3<&0
cat <&3 >/dev/null &
reader_pid=$!
"${binary}" \
--bind 127.0.0.1 \
--port 7867 \
--nextcloud-url http://127.0.0.1 \
"${config}" &
push_pid=$!
cleanup() {
kill -TERM "${push_pid}" "${reader_pid}" 2>/dev/null || true
wait "${push_pid}" "${reader_pid}" 2>/dev/null || true
}
trap cleanup EXIT
trap 'exit 0' HUP INT TERM
wait "${push_pid}"
EOF
chown www-data:www-data /app/data/notify_push/runner.sh
chmod 0755 /app/data/notify_push/runner.sh
The daemon binds only to loopback. Port 7867 is not exposed publicly.
3. Add the internal callback configuration
The daemon must contact Nextcloud directly instead of looping back through Cloudron’s public proxy:
cat > /app/data/config/notify_push.config.php <<'EOF'
<?php
$CONFIG = [
'trusted_domains' => [
getenv('CLOUDRON_APP_DOMAIN'),
'127.0.0.1',
],
'trusted_proxies' => [
getenv('CLOUDRON_PROXY_IP'),
'127.0.0.1',
],
];
EOF
chown www-data:www-data /app/data/config/notify_push.config.php
chmod 0640 /app/data/config/notify_push.config.php
Do not add --glob-config to the daemon command. The Rust configuration parser cannot evaluate the getenv() calls in this extra PHP config file.
4. Add the persistent Apache proxy
First save the original configuration:
cp -n /app/data/apache/mpm_prefork.conf \
/app/data/apache/mpm_prefork.conf.before-notify-push
Then append the integration once:
if ! grep -Fq "BEGIN CLOUDRON NOTIFY_PUSH" /app/data/apache/mpm_prefork.conf; then
cat >> /app/data/apache/mpm_prefork.conf <<'EOF'
# BEGIN CLOUDRON NOTIFY_PUSH
<IfModule !proxy_module>
LoadModule proxy_module /usr/lib/apache2/modules/mod_proxy.so
</IfModule>
<IfModule !proxy_http_module>
LoadModule proxy_http_module /usr/lib/apache2/modules/mod_proxy_http.so
</IfModule>
<IfModule !proxy_wstunnel_module>
LoadModule proxy_wstunnel_module /usr/lib/apache2/modules/mod_proxy_wstunnel.so
</IfModule>
ProxyPass "/push/ws" "ws://127.0.0.1:7867/ws"
ProxyPass "/push/" "http://127.0.0.1:7867/"
ProxyPassReverse "/push/" "http://127.0.0.1:7867/"
GlobalLog "|/usr/local/bin/gosu www-data:www-data /app/data/notify_push/runner.sh" combined
# END CLOUDRON NOTIFY_PUSH
EOF
fi
chown www-data:www-data /app/data/apache/mpm_prefork.conf
chmod 0644 /app/data/apache/mpm_prefork.conf
GlobalLog gives Apache ownership of the daemon lifecycle. Apache starts the runner whenever the app starts and respawns it if it exits.
5. Validate and activate Apache
apache2ctl configtest
Only continue if it reports Syntax OK:
supervisorctl restart apache2
Check that the daemon is running:
ps -ef | grep '[n]otify_push'
curl -i "https://${CLOUDRON_APP_DOMAIN}/push/test/cookie"
The unauthenticated curl request should return HTTP 400 with Missing request header "token". That is expected and confirms the public proxy reaches the daemon.
6. Configure Nextcloud
sudo -u www-data php /app/code/occ \
notify_push:setup "https://${CLOUDRON_APP_DOMAIN}/push"
All checks should pass:
✓ redis is configured
✓ push server is receiving redis messages
✓ push server can load mount info from database
✓ push server can connect to the Nextcloud server
✓ push server is a trusted proxy
✓ push server is running the same version as the app
configuration saved
Run the self-test:
sudo -u www-data php /app/code/occ notify_push:self-test
7. Verify persistence
Restart Nextcloud from the Cloudron dashboard. Once it is healthy again, reopen the terminal and run:
sudo -u www-data php /app/code/occ notify_push:self-test
ps -ef | grep '[n]otify_push'
The saved endpoints should be:
https://your-nextcloud-domain.example/push
wss://your-nextcloud-domain.example/push/ws
I also confirmed that both, my Hermes and the official Nextcloud sync client connected and authenticated to the WebSocket after the restart.
Notes
This is an instance-level workaround because the current Cloudron package has no custom supervisor hook. It follows the official notify_push architecture: a background daemon plus an Apache reverse proxy.
Everything persistent is stored in:
/app/data/apache/mpm_prefork.conf
/app/data/notify_push/runner.sh
/app/data/config/notify_push.config.php
The original Apache configuration is retained at:
/app/data/apache/mpm_prefork.conf.before-notify-push
I would rerun notify_push:self-test after future Nextcloud or Cloudron package updates.