How to make Calendar + Contacts accessible from WAN without exposing the rest of Nextcloud

Hello everyone,

Is it possible to keep Nextcloud trapped behind a VPN but allow Nextcloud users to sync calendars items from their devices to Nextcloud (and vice versa, if the event was created directly in Nextcloud) outside of the VPN? In particular, is it possible to expose an endpoint that allows 2-way sync directly against Nextcloud’s SabreDAV server?

The endgame is to use Nextcloud’s calendar and contacts as instead of Radicale or something else. Nextcloud is a resource hog so if I can do it here I don’t want a separate service for it… But it’s looking like Nextcloud can’t do it. I’m excited to be proven wrong!

I’m really hoping this is a layer 8 issue as I haven’t seen anything/anyone say this can’t work and I’m fairly certain SabreDAV can do this (though I could be wrong). I could stand up something in the DMZ/cloud but that would be in addition to hosting Nextcloud in the lab (and the installation seems really fragile- I don’t want to mess with that again), and that seems far less optimal than “publish a service endpoint and call it a day”.

Does anyone have guidance they can offer? What keywords should I search for? Is this not a feature of Nextcloud?

Thanks in advance!

I would probably solve this through a reverse proxy. CalDAV syncs through a url e.g. https://example.tld/remote.php/dav/ (all the authentication is done here as well) so simply allowing external access to that url sub tree but limiting access to the rest of the domain to VPN IP ranges.

Something along these lines in nginx (NOTE I haven’t tested this, example idea only)

location / {
    # allow VPN network

    # drop rest of the world
    deny all;
    proxy_pass http://nextcloud_app:8080;
location ^~ /remote.php/dav/ {
    proxy_pass http://nextcloud_app:8080;

*EDIT: You could also use something like Cloudflare tunnels or Tailscale Funnels in order to limit general public exposure.

Thank you for the information! I’ll see what I can do with this.

I tried to go about this WAY differently- adding a port mapping in NC, then pointing the RP at that port based on a host rule match. Layer 8 issue, indeed!