Running opencloud – great nextcloud replacement

I’ve been running owncloud, then nextcloud for years. While it worked I was never very happy with it. Performance was always lackluster even for 1-2 users, features added was never something I cared about (groupware, AI assistant, talk features – don’t use, need or care).

All I want is a reliable lean google drive or onedrive alternative, clean files-only minimal interface, some apps on ios and android, ability to share and edit files. I’ve been on the lookout for alternatives for years but there was nothing really good out there until now. Already has android and apple apps.

Enter opencloud. I’ve been running it for 2 months now and can recommend switching. Here’s how I run it using docker compose. Note that this is unofficial docker-compose.yml. Official docker-compose.yml is over here, but myself and many other people find it too complicated. Examples also focus on traefik while people might be using other reverse proxies.

Note: This is tutorial is only to illustrate how I personally run my personal opencloud service. This post is provided for informational and educational purposes only. It does not constitute security advice, and no statement is made regarding the suitability or fitness of these configurations for production or secure deployment.

Opencloud screenshots

Sharing screenshots so people can see how it looks like. People that are looking to switch are probably interested in this the most.

What I love about it – interface is clean, uncluttered, and to the point.

opencloud 3.6 screenshot - ui
opencloud 3.6 screenshot - ui
opencloud 3.6 screenshot - video player
opencloud 3.6 screenshot – video player
opencloud 3.6 screenshot - collabora
opencloud 3.6 screenshot – collabora
opencloud 3.6 screenshot - images
opencloud 3.6 screenshot – images
opencloud 3.6 screenshot - markdown
opencloud 3.6 screenshot – markdown
opencloud 3.6 screenshot - user preferences
opencloud 3.6 screenshot – user preferences
opencloud 3.6 screenshot - word documents collabora
opencloud 3.6 screenshot – word documents collabora
opencloud 3.6 screenshot - appstore. Not many apps there yet.
opencloud 3.6 screenshot – appstore

Various settings pages:

How I run opencloud in docker compose with nginx proxy manager as a reverse proxy

Opencloud docker setup could be a bit convoluted if you want collabora (collabora is a selfhosted service for editing documents) or calendar (you need to add radicale container). If you don’t use collabora for editing microsoft word or open documents – you can just run opencloud as a single container (fine for people that dont care about docs) – you can still do text and markdown file editing with a single container opencloud setup.

If you want collabora – you need 3 containers: collabora, wopi and opencloud. I’m running with collabora and these examples are for 3 container setup with collabora.

I run opencloud containers straight on my NAS server running ubuntu LTS, I then expose container ports on tailscale only, and then I route it via nginx proxy manager through my public VPS via tailscale.

This avoids cloudflare as I dont like using cloudflare, I’m sure you can set this up with cloudflare as well.

You don’t necessarily need tailscale and nginx proxy manager unless you want to expose opencloud via public VPS server just like I do, or just want to use private openlcoud on tailscale VPN (good idea).

I use these 3 domains (Using example.com is just an example). Why? Because I own example.com, I then have a VPS that i identify as toolski17.example.com, I then run 3 services on that VPS.
These are just example urls, my setup uses different DNS names:

Why such complex dns names? That’s just for additional security – nginx proxy manager is configured to 444 if user does not have domain. This literally eliminates bots and probes entirely for me (No, this is not security by obscurity, this is defence in depth). “obscurity is not a security” types are going to love this paragraph – look guys – Just use tailscale without exposing services to the internet if you don’t like this. This is how I do it and it works great.

Edit: To clarify to my dear frieds from “obscurity is not a security” crowd that immediately attacked me. I should have clarified – I use wildcard ssl and wildcard dns: *.toolski17.example.com. *.toolski17.example.com is the only thing letsencrypt and registrar sees. For attacker to even begin trying to exploit your application they would have to guess a subdomain first, everything else get’s a 444 from nginx. And after that – you still have regular opencloud authentication and authorization to deal with.

Edit 2: added bolder disclaimer as I dont have time to go back and fourth on secure subdomain topic. Works great for me. Ymmv

Opencloud – docker-compose.yml

networks:
  default:
    external: true
    name: infranet

# Example from here: https://www.reddit.com/r/selfhosted/comments/1n5kgvi/looking_for_a_reliable_nextcloud_alternative/  

services:
  opencloud-app:
    restart: always
    image: opencloudeu/opencloud-rolling:latest
    hostname: opencloud-app
    container_name: opencloud-app
    user: "1000:1000"
    logging:
      driver: "json-file"
      options:
        max-file: "5"
        max-size: "10m"
    environment:
      # enable services that are not started automatically
      OC_ADD_RUN_SERVICES: "notifications"
      OC_URL: "https://fence-red5-opencloud.toolski17.example.com"
      OC_LOG_LEVEL: "info"
      OC_LOG_COLOR: "false"
      OC_LOG_PRETTY: "false"
      PROXY_TLS: "false"
      OC_INSECURE: "true"
      ### demo users ###
      IDM_CREATE_DEMO_USERS: "false"
      PROXY_HTTP_ADDR: 0.0.0.0:9200

      FRONTEND_ARCHIVER_MAX_SIZE: 1099511627776   # 1 TiB
      # (optional) if you also need more files per archive:
      FRONTEND_ARCHIVER_MAX_NUM_FILES: 500000

      ### Notifications Settings ###
      NOTIFICATIONS_SMTP_HOST: blah.mxrouting.net
      NOTIFICATIONS_SMTP_PORT: 587
      NOTIFICATIONS_SMTP_SENDER: "OpenCloud <donotreply@example.com>"
      NOTIFICATIONS_SMTP_USERNAME: donotreply@example.com
      NOTIFICATIONS_SMTP_PASSWORD: FILL_IN_YOUR_PASS
      NOTIFICATIONS_SMTP_INSECURE: false
      NOTIFICATIONS_SMTP_AUTHENTICATION: plain
      NOTIFICATIONS_SMTP_ENCRYPTION: starttls

      STORAGE_USERS_DRIVER: posix
      STORAGE_USERS_ID_CACHE_STORE: nats-js-kv

      ### Wopi Server Settings ###
      WOPISERVER_DOMAIN: "fence-red5-wopi.toolski17.example.com"
      
      # Bullshit needed for collabora
      PROXY_CSP_CONFIG_FILE_LOCATION: "/etc/opencloud/csp.yaml"

      FRONTEND_APP_HANDLER_SECURE_VIEW_APP_ADDR: "eu.opencloud.api.collaboration.CollaboraOnline"

      ### NATS Settings - by default it listens on 127.0.0.1 ###
      NATS_NATS_HOST: 0.0.0.0
      NATS_NATS_PORT: 9233

      ### GRPC Settings ###
      GATEWAY_GRPC_ADDR: "0.0.0.0:9142"

      ### Collabora Settings ###
      COLLABORA_DOMAIN: "fence-red5-collabora.toolski17.example.com"
      COLLABORA_ADMIN_USER: "admin"
      COLLABORA_ADMIN_PASSWORD: "FILL_IN_YOUR_PASS"
      COLLABORA_SSL_ENABLE: false
      COLLABORA_SSL_VERIFICATION: false
    volumes:
      - ${FS_DIR}/data:/var/lib/opencloud
      - ${FS_DIR}/config:/etc/opencloud
    #exposing port to internal Tailscale network only
    ports:
      - ${TAILSCALE_IP}:${OPENCLOUD_PORT}:${OPENCLOUD_PORT}




  opencloud-collaboration:
    image: opencloudeu/opencloud-rolling:latest
    container_name: opencloud-collaboration
    depends_on:
      opencloud-app:
        condition: service_started
      opencloud-collabora:
        condition: service_healthy
    restart: always
    entrypoint:
      - /bin/sh
    command: ['-c', 'opencloud collaboration server']
    environment:
      OC_LOG_LEVEL: info
      COLLABORATION_GRPC_ADDR: 0.0.0.0:9301
      COLLABORATION_HTTP_ADDR: 0.0.0.0:9300
      MICRO_REGISTRY: "nats-js-kv"
      MICRO_REGISTRY_ADDRESS: "opencloud-app:9233"
      COLLABORATION_WOPI_SRC: "https://fence-red5-wopi.toolski17.example.com"
      COLLABORATION_APP_NAME: "CollaboraOnline"
      COLLABORATION_APP_PRODUCT: "Collabora"
      COLLABORATION_APP_ADDR: "https://fence-red5-collabora.toolski17.example.com"
      COLLABORATION_APP_ICON: "https://fence-red5-collabora.toolski17.example.com/favicon.ico"
      COLLABORATION_APP_INSECURE: true
      COLLABORATION_CS3API_DATAGATEWAY_INSECURE: true
      COLLABORATION_LOG_LEVEL: info
      OC_URL: "https://fence-red5-opencloud.toolski17.example.com"
    volumes:
      - ${FS_DIR}/config:/etc/opencloud
    ports:
      #exposing port to internal Tailscale network only
      # - 9300:9300
      - ${TAILSCALE_IP}:${COLLABORATION_PORT}:${COLLABORATION_PORT}

  opencloud-collabora:
    image: collabora/code:latest
    container_name: opencloud-collabora
    cap_add:
      - MKNOD
    environment:
      aliasgroup1: https://fence-red5-wopi.toolski17.example.com:443
      DONT_GEN_SSL_CERT: "YES"
      extra_params: |
        --o:ssl.enable=false \
        --o:ssl.ssl_verification=false \
        --o:ssl.termination=true \
        --o:welcome.enable=false \
        --o:net.frame_ancestors=fence-red5-opencloud.toolski17.example.com
      username: admin
      password: 'FILL_IN_YOUR_PASS'
      domain: "fence-red5-collabora\\.toolski17\\.aurora\\.dev|fence-red5-opencloud\\.toolski17\\.aurora\\.dev|fence-red5-wopi\\.toolski17\\.aurora\\.dev|opencloud-app|opencloud-collaboration"
    restart: always
    entrypoint: ['/bin/bash', '-c']
    command: ['coolconfig generate-proof-key && /start-collabora-online.sh']
    healthcheck:
      test: ["CMD", "bash", "-c", "exec 3<>/dev/tcp/127.0.0.1/9980 && echo -e 'GET /hosting/discovery HTTP/1.1\r\nHost: localhost:9980\r\n\r\n' >&3 && head -n 1 <&3 | grep '200 OK'"]
    ports:
      #exposing port to internal Tailscale network only
      - ${TAILSCALE_IP}:${WOPI_PORT}:${WOPI_PORT}

Opencloud – docker compose .env file

#General settings
COMPOSE_PROJECT_NAME=opencloud

# Where data is stored for opencloud on my nas
FS_DIR=/mnt/nas-pool-furyroad/dockervolumes/opencloud

#ports
OPENCLOUD_PORT=9200
COLLABORATION_PORT=9300
WOPI_PORT=9980

#Nas Host Tailscale network ip
TAILSCALE_IP=100.93.99.20

Opencloud – csp.yaml. yes this is needed

I had to set it up manually. Various UI operations will fail if you dont have it.

This file is used by main opencloud service:


# Bullshit needed for collabora
PROXY_CSP_CONFIG_FILE_LOCATION: "/etc/opencloud/csp.yaml"

directives:
  child-src:
    - "'self'"
  connect-src:
    - "'self'"
    - 'blob:'
    - 'https://ence-red5-wopi.shedsky1.example.com/'
    - 'wss://ence-red5-wopi.shedsky1.example.com/'
    - 'https://raw.githubusercontent.com/opencloud-eu/awesome-apps/'
    - 'https://ence-red5-opencloud.shedsky1.example.com/'
  default-src:
    - "'none'"
  font-src:
    - "'self'"
  frame-ancestors:
    - "'self'"
  frame-src:
    - "'self'"
    - 'blob:'
    - 'https://embed.diagrams.net/'
    - 'https://ence-red5-collabora.shedsky1.example.com/'
    - 'https://docs.opencloud.eu'
  img-src:
    - "'self'"
    - 'data:'
    - 'blob:'
    - 'https://raw.githubusercontent.com/opencloud-eu/awesome-apps/'
    - 'https://ence-red5-collabora.shedsky1.example.com/'
  manifest-src:
    - "'self'"
  media-src:
    - "'self'"
  object-src:
    - "'self'"
    - 'blob:'
  script-src:
    - "'self'"
    - "'unsafe-inline'"
    - "'unsafe-eval'"         # <-- allow eval()/new Function()
  style-src:
    - "'self'"
    - "'unsafe-inline'"

Opencloud reverse proxy – Nginx proxy manager on a public VPS

I prefer this route when exposing a service from my local network. Basically if I want service to be publicly available, – I route it from my home network via tailscale and expose via remote public VPS I own and some domain name I own using nginx proxy manager reverse proxy.

Here is an example: just point domains to the tailscale DNS names.

Again – I use tailscale purely to connect from public VPS server to my homenetwork.

a. I add 3 proxies in nginx proxy manager:

opencloud nginx proxy manager
opencloud nginx proxy manager

b. Nginx proxy config looks something like this – note how I point to my NAS tailscale dns:

c. Just make sure to increase nginx timeouts. (advanced tab in nginx proxy manager), otherwise transferring a lot of files could fail.

proxy_connect_timeout 60s;
proxy_send_timeout  4h;      # or 14400s
proxy_read_timeout  4h;      # or 14400s
send_timeout        4h;      # or 14400s


# these help WebDAV COPY/MOVE and uploads
# disabled as im not sure this is needed
# proxy_request_buffering off;
# proxy_buffering       off;

Join opencloud chat (matrix)

They have good active matrix channel (that’s a chat app): https://matrix.to/#/#opencloud:matrix.org

If you need help and support community seems active and growing.

What I would say to Nextcloud

You need to refactor your backend. No – you cannot build application like nextcloud on share-nothing per request PHP backend. What you have now is a buggy, unscalable mess of a product that can only survive on top of redis caching and being entirely free. You need to convert nextcloud to a long running in-memory always-on application with heavy use of async/coroutines.

@nextcloud team – If you want to refactor quickly – use swoole on top PHP, use hyperf framework. This way you can keep PHP, but shift entirely how it works under the hood.

Switch to long-running paradigm, parallelize database calls (improves performance), use coroutines, use swoole built-in side processes, use built-in cronjobs abilities, offload async tasks to nonblocking TaskExecutor using side nonblocking task workers.

Look at the benchmark I’ve done to illustrate the differene – it barely scratches the surface of what is possible with swoole.

Websockets, socket.io, tcp server, grpc server -> these are a killer features most PHP apps simply incapable of. You need them for nextcloud. – I dont have time to list it all. Just take a look at this list https://hyperf.wiki/3.1/#/en/ and if you’ve been in a PHP field for a while the difference of possibilities will be self-evident. it’s a night and day.

In conclusion

Switch to opencloud today, it’s fast, clean, lean and overall amazing. And maybe nextcloud will get their act together (it should better be soon).

Leave a Comment