Home Lab Part 3: OpenCloud — Replacing Google Drive and Nextcloud
After Immich for photos, the next thing to self-host was file sync and office editing. I started with Nextcloud but quickly switched to OpenCloud — a Go-based alternative that’s lighter, faster, and doesn’t need a database.
Why OpenCloud over Nextcloud
| OpenCloud | Nextcloud | |
|---|---|---|
| Language | Go | PHP |
| RAM idle | ~100 MB | 1–2 GB |
| Database | None (metadata in filesystem) | PostgreSQL/MySQL required |
| Backup | ZFS snapshot = full atomic backup | DB dump + files separately |
| Collabora latency | Lower (Go WOPI) | Higher |
The biggest win: no database. All metadata lives in the filesystem alongside the files. A ZFS snapshot gives you a complete, consistent backup — no need to coordinate DB dumps with file backups.

Architecture
Four containers work together:
| Container | Port | Purpose |
|---|---|---|
opencloud | 9200 | Main server (files, sharing, auth) |
opencloud_collaboration | 9300 | WOPI server (Collabora bridge) |
collabora | 9980 | Collabora Online (document editing) |
radicale | 5232 | CalDAV/CardDAV (calendar & contacts) |
Docker Compose
The key config decisions:
services:
opencloud:
image: owncloud/opencloud-rolling:latest
entrypoint: ["/bin/sh"]
command: ["-c", "opencloud init || true; opencloud server"]
user: "1000:1000"
volumes:
- /mnt/nextcloud:/var/lib/opencloud
- ./oc-config:/etc/opencloud
environment:
PROXY_TLS: "false"
PROXY_ENABLE_BASIC_AUTH: "true"
networks:
- proxy
- internal
labels:
- "traefik.enable=true"
- "traefik.http.routers.opencloud.rule=Host(`cloud.johannes-kling.de`)"
- "traefik.http.services.opencloud.loadbalancer.server.port=9200"
- "traefik.http.middlewares.opencloud-headers.headers.customrequestheaders.X-Forwarded-Proto=https"
A few things to note:
The || true in the entrypoint is critical. opencloud init creates the initial config but fails on subsequent starts because the config already exists. Without || true, the container crashes on every restart.
PROXY_ENABLE_BASIC_AUTH: "true" is required for WebDAV clients (like desktop sync or mobile apps) to authenticate. Without it, only browser-based OAuth login works.
X-Forwarded-Proto=https middleware — since Traefik serves HTTP internally and Cloudflare handles TLS, OpenCloud thinks it’s running on HTTP and creates redirect loops. This header tells it the client connection is actually HTTPS.
Run as non-root with user: "1000:1000". All data directories must be owned by this uid/gid.
Collabora Online

Collabora gives you in-browser editing of documents, spreadsheets, and presentations — like Google Docs but self-hosted. OpenCloud connects to it via a WOPI server.
The critical environment variables that aren’t obvious:
environment:
NATS_NATS_HOST: 0.0.0.0
GATEWAY_GRPC_ADDR: 0.0.0.0:9142
Without binding NATS and gRPC to 0.0.0.0, Collabora can’t communicate with OpenCloud across Docker containers (they default to localhost).
CalDAV/CardDAV with Radicale
Radicale handles calendar and contacts sync. OpenCloud proxies the requests internally — no separate subdomain needed.
# config/opencloud/proxy.yaml — routes DAV traffic to Radicale
Auth is handled via http_x_remote_user — OpenCloud passes the logged-in user to Radicale via header, so there are no separate credentials to manage.
To connect a phone: point DAVx5 (Android, free on F-Droid) or iOS Calendar/Contacts at https://cloud.johannes-kling.de. It auto-discovers the CalDAV/CardDAV endpoints.
Config Files
OpenCloud needs a few config files from the opencloud-compose repo:
csp.yaml— Content Security Policy headersbanned-password-list.txt— password blacklistproxy.yaml— routes/caldav/and/carddav/to Radicale
Gotchas
All data directories must be owned by uid/gid 1000:1000. If permissions are wrong, OpenCloud starts but silently fails to write files.
Collabora needs NATS + gRPC bound to 0.0.0.0. The default localhost binding doesn’t work across Docker containers.
The X-Forwarded-Proto header is non-negotiable. Without it, you get an infinite redirect loop between HTTP and HTTPS.
Backup
This is where OpenCloud really shines over Nextcloud. Since there’s no database — all state lives in the filesystem — a single ZFS snapshot is a complete, atomic backup:
zfs snapshot hdd-01/data/nextcloud@backup-$(date +%F)
No DB dumps, no coordination, no risk of inconsistency.
Result
OpenCloud is live at cloud.johannes-kling.de — file sync, document editing, and calendar/contacts all in one place. It uses a fraction of the resources Nextcloud needed and backup is trivially simple.
Next up: MCP Servers — giving AI assistants access to a shared knowledge base.