NextCloud notify_push with Redis, systemd, and NGINX (WebSockets)

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):

Solution overview (how the pieces fit)

Architecture:

  • NextCloud publishes events to Redis
  • notify_push subscribes 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_push as the same OS user as your web stack (often www-data) to simplify permissions.
  • Use the NextCloud config.php path in ExecStart so 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 (not notify_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:

  1. The notify_push binary (system service)
  2. 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-test complains 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 Upgrade and Connection headers are set
  • Ensure no additional proxy layer strips these headers (CDN, WAF, second NGINX/HAProxy)

Missing trusted proxies entries

Symptoms:

  • notify_push:setup fails
  • self-test fails on trusted proxy checks

Fix:

  • Add the proxy IPs (and if used: 127.0.0.1 and ::1) to NextCloud trusted_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 (or BIND=:: with PORT)

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_push metrics 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

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.