NextCloud Desktop clients traditionally detect server-side changes by polling. With newer client versions, server-to-client push notifications via notify_push have become important to get timely sync updates without aggressive polling.
This post shows a pragmatic setup of the NextCloud notify_push backend behind NGINX, managed by systemd, using Redis as the message bus. It also covers updates, self-tests, and common reverse proxy pitfalls (WebSockets, trusted proxies, IPv6 bind).
All configuration files and scripts for this post are available here: https://git.spacewars.ch/blog/public
Context and requirements
Assumptions:
- You run a working NextCloud instance (PHP-FPM/Apache or PHP-FPM/NGINX).
- You have shell access to the NextCloud host and to the reverse proxy.
- Your reverse proxy terminates TLS and forwards to the NextCloud backend.
- You can install a NextCloud app via
occ.
Prerequisites:
- Redis reachable from NextCloud (TCP or Unix socket)
- NextCloud configured to use Redis (for file locking and/or as configured by the app)
- systemd on the host running the push daemon
- A reverse proxy that supports WebSockets (NGINX example in this article)
References (minimal):
notify_pushreleases: https://github.com/nextcloud/notify_push/releasesnotify_pushproject/documentation (README): https://github.com/nextcloud/notify_push
Solution overview (how the pieces fit)
Architecture:
- NextCloud publishes events to Redis
notify_pushsubscribes to Redis and exposes a WebSocket endpoint- Clients connect to
https://<cloud-host>/push/(through the reverse proxy) - NGINX forwards
/push/to the local push daemon and upgrades the connection to WebSockets
Key decisions:
- Run
notify_pushas the same OS user as your web stack (oftenwww-data) to simplify permissions. - Use the NextCloud
config.phppath inExecStartso you do not have to encode everything into environment variables. - Explicitly handle IPv6 bind if you want dual-stack connectivity.
Step-by-step implementation
1) Configure Redis for NextCloud
You need Redis configured and reachable. The exact Redis configuration depends on your distribution, but the key point is: NextCloud must be able to connect either via:
- TCP:
127.0.0.1:6379(or an internal VLAN), or - Unix socket:
/var/run/redis/redis-server.sock
Verify connectivity from the NextCloud host (TCP example):
redis-cli -h 127.0.0.1 -p 6379 ping
Expected output:
PONG
If you use a socket, adjust redis-cli -s ....
2) Download the notify_push binary
Download the correct binary from the release page:
https://github.com/nextcloud/notify_push/releases
Place it somewhere stable that your systemd unit can reference, for example:
/usr/local/nextcloud/notify_push/usr/local/nextcloud/test_client(optional)
Make the binary executable and owned by the service user. Example (adjust version and architecture):
mkdir -p /usr/local/nextcloud
wget -O /usr/local/nextcloud/notify_push \
'https://github.com/nextcloud/notify_push/releases/download/v1.3.0/notify_push-x86_64-unknown-linux-musl'
chmod 0755 /usr/local/nextcloud/notify_push
chown www-data:www-data /usr/local/nextcloud/notify_push
Notes:
- Avoid typos in the binary name. It must be
notify_push(notnotify_psuh). - Keep binaries outside the web root.
3) Create a systemd unit
Create /etc/systemd/system/notify_push.service:
[Unit]
Description=Push daemon for NextCloud clients (notify_push)
Documentation=https://github.com/nextcloud/notify_push
After=network-online.target
Wants=network-online.target
[Service]
# Change if you already have something running on this port
Environment=PORT=7867
# Explicit bind for dual-stack. If you only want IPv4, use 127.0.0.1:7867
Environment=BIND=::
# Point notify_push to the NextCloud config.php to read settings
ExecStart=/usr/local/nextcloud/notify_push /var/www/cloud.example.com/nextcloud/config/config.php
# requires notify_push build with the systemd feature (enabled by default in releases)
Type=notify
User=www-data
Group=www-data
Restart=always
RestartSec=60
[Install]
WantedBy=multi-user.target
Enable and start:
systemctl daemon-reload
systemctl enable --now notify_push.service
systemctl status notify_push.service
Operational note: binding to :: means the daemon listens on IPv6 and (depending on your kernel settings) may also accept IPv4-mapped connections. If you want to guarantee it is only reachable locally, bind to 127.0.0.1 and/or a private address and firewall it.
4) Reverse proxy (NGINX) with WebSockets
On the reverse proxy, forward /push/ to the local daemon. Example snippet:
location ^~ /push/ {
proxy_pass http://127.0.0.1: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;
}
If your environment drops or hides upgrade headers, you might additionally need:
proxy_pass_header Upgrade;
Verification:
- Reload NGINX and watch error logs while you connect a client.
- If WebSockets are not upgraded correctly, clients will often connect and then immediately disconnect or never receive events.
5) Install and configure the NextCloud app
Install and enable the NextCloud app:
sudo -u www-data php occ app:install notify_push
sudo -u www-data php occ app:enable notify_push
Run setup (replace URL with your own push endpoint):
sudo -u www-data php occ notify_push:setup https://cloud.example.com/push
Important: add the push daemon / reverse proxy addresses to trusted proxies in config.php (both IPv4 and IPv6 if applicable). Otherwise, notify_push setup and operation can fail because NextCloud refuses to trust forwarded headers.
Operations
Self-test (first thing to run)
If your setup is correct:
sudo -u www-data php occ notify_push:self-test
Expected output looks like:
✓ 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
If you see errors about Redis messages not arriving, the issue is often either:
- Redis connectivity/config on the NextCloud side, or
- Reverse proxy WebSocket upgrade/header handling, or
- Trusted proxy configuration (Forwarded headers rejected)
Metrics
You can view metrics via occ:
sudo -u www-data php occ notify_push:metrics
Example output:
Active connection count: 1
Active user count: 1
Total connection count: 9
Total database query count: 8
Events received: 354
Messages sent: 14
Messages sent (file): 12
Messages sent (notification): 0
Messages sent (activity): 2
Messages sent (custom): 0
If you want Prometheus-style scraping, notify_push can expose metrics over TCP. Do this only on localhost or behind authentication, and firewall it appropriately.
Dump effective config
Useful when you suspect the daemon reads a different NextCloud config than expected:
/usr/local/nextcloud/notify_push --dump-config /var/www/cloud.example.com/nextcloud/config/config.php
Updates (binary and app compatibility)
You typically update two parts:
- The notify_push binary (system service)
- The notify_push NextCloud app
Keep them compatible. After a NextCloud update, re-run:
occ app:update notify_push(or update all apps)- and update the binary if the self-test indicates a version mismatch
Binary update workflow (example):
systemctl stop notify_push.service
wget -O /usr/local/nextcloud/notify_push \
'https://github.com/nextcloud/notify_push/releases/download/v1.3.0/notify_push-x86_64-unknown-linux-musl'
chown www-data:www-data /usr/local/nextcloud/notify_push
chmod 0755 /usr/local/nextcloud/notify_push
systemctl start notify_push.service
systemctl status notify_push.service
Optional: update the bundled test_client the same way.
Common pitfalls and troubleshooting
WebSockets not upgrading through the proxy
Symptoms:
self-testcomplains about Redis messages not reaching clients- Desktop clients never get push updates
- NGINX logs show
400/426/ unexpected connection closes
Fixes:
- Ensure
proxy_http_version 1.1 - Ensure
UpgradeandConnectionheaders are set - Ensure no additional proxy layer strips these headers (CDN, WAF, second NGINX/HAProxy)
Missing trusted proxies entries
Symptoms:
notify_push:setupfailsself-testfails on trusted proxy checks
Fix:
- Add the proxy IPs (and if used:
127.0.0.1and::1) to NextCloudtrusted_proxies - Ensure forwarded headers are consistent with your proxy design
IPv6 bind and connectivity surprises
If you only bind to IPv4 but clients/proxy prefer IPv6 (or vice versa), you can see intermittent failures. Decide explicitly:
- Local-only:
BIND=127.0.0.1:7867 - Dual-stack:
BIND=:::7867(orBIND=::withPORT)
Test client for low-level debugging
Download test_client from the same release page and run:
LOG=DEBUG /usr/local/nextcloud/test_client https://cloud.example.com/ user-name user-password
This is useful to see whether authentication works and whether the endpoint behaves as expected. If it fails with capability/credential errors, verify:
- correct username/password (or app password if required)
- correct base URL (watch out for double slashes)
- server reachable over IPv4/IPv6 as expected
Conclusion
notify_push restores fast, reliable server-to-client change propagation for NextCloud clients and can reduce the need for frequent polling. The main operational risks are not in the daemon itself, but at the edges: Redis connectivity, trusted proxy configuration, and correct WebSocket handling in the reverse proxy.
Further work / related topics:
- Expose
notify_pushmetrics safely and scrape with Prometheus - Harden reverse proxy rules (rate limits, IP allowlists for metrics endpoints)
- Automate binary updates with checksum verification and staged rollout
- Compare polling vs push behaviour in mixed IPv4/IPv6 environments
