Sign inSign up

junkerderprovinz/opencloud

By junkerderprovinz

•Updated 1 day ago

One-click OpenCloud for Unraid: auto-init, permission heal, PUID/PGID, production and rolling tags.

Image
0

10K+

junkerderprovinz/opencloud repository overview

OpenCloud

Build  Lint  Docker Pulls  Image Size  Arch  OpenCloud  Unraid  License: AGPL-3.0


A plug-and-play Docker image that turns the official OpenCloud server into a genuine one-click Unraid app: it runs the required first-boot init for you, heals the appdata permissions and honours Unraid's PUID/PGID. No console, no chown, no config-file editing required.

A one-knight job: I build it, keep it running, work through the issues and add what people ask for, until nothing is missing. It is free, with no accounts, no telemetry, no ads and no paid tier. No asterisk anywhere. Nothing readable ever leaves your own walls. Forged on evenings and weekends, with heart and stubbornness.

If it has earned a place on your server or computer, toss a coin to your knight: it helps cover the costs and keeps the project alive. It also makes this knight's heart beat a little faster. Three ways below, whichever suits you.


Buy me a coffee   PayPal   Donate with crypto


⁠Table of Contents

  1. Overview⁠
  2. Quick Start⁠
  3. Configuration⁠
  4. Production vs Rolling⁠
  5. How the Wrapper Works⁠
  6. Reverse Proxy⁠
  7. Branding⁠
  8. Building Locally⁠
  9. Updating⁠
  10. Troubleshooting⁠
  11. Architecture⁠
  12. Contributing / License⁠
  13. License⁠
  14. How AI is used here⁠
  15. Support this project⁠

⁠1. Overview

OpenCloud⁠ is a modern, self-hosted file sync-and-share platform (an actively developed member of the ownCloud/Infinite-Scale family). The official opencloudeu/opencloud⁠ image is excellent, but it is not built for a one-click NAS install:

  • it runs its binary as a fixed UID with no PUID/PGID support, so on a fresh Unraid box the root-owned bind mounts make the very first boot fail with "permission denied" writing /etc/opencloud/opencloud.yaml and /var/lib/opencloud/nats;
  • it requires a one-time opencloud init to be run by hand before opencloud server will start.

This image is a thin wrapper around the official one that fixes those two things and adds a few extras:

  • Auto-init: runs opencloud init once on first boot (idempotent on later boots).
  • Permission heal: creates the config/data dirs and hands them to your PUID:PGID, and repairs a previously root-owned tree once (sentinel-guarded, so it never recursively re-chowns your whole data set on every start).
  • PUID / PGID: drops privileges to Unraid's nobody:users (99:100) by default via a static gosu.
  • Two channels: :rolling (newest builds, the template default) and :latest (OpenCloud's fully QA'd production line), from the same wrapper.
  • Multi-arch: amd64 and arm64.
  • Branding app (optional, off by default): set the name, logos, favicon and login background from the web UI, see §7⁠.

The wrapper does not fork, patch or repackage OpenCloud itself. It layers a tiny entrypoint and the optional branding app on top of the unmodified upstream image, so you always run real, current OpenCloud.

OpenCloud web UI showing the file browser
The OpenCloud web UI: your personal space with files, folders and spaces.

OpenCloud sign-in page
Sign in as admin with the password you set in the Unraid template.


⁠2. Quick Start

⁠Step 1: Install the template

On Unraid: Apps → search for OpenCloud → Install. The Community Applications template is published from the unraid-apps⁠ feed.

To load it by hand:

mkdir -p /boot/config/plugins/dockerMan/templates-user && \
curl -fsSL -o /boot/config/plugins/dockerMan/templates-user/my-OpenCloud.xml \
  https://raw.githubusercontent.com/junkerderprovinz/unraid-apps/main/opencloud/opencloud.xml
⁠Step 2: Set the admin password and paths

In the template, the only field you must set is Admin Password (IDM_ADMIN_PASSWORD). It becomes the password for the built-in admin user on first start. The two volumes default to /mnt/user/appdata/opencloud/{config,data}; adjust the data path to a share with room to grow.

⁠Step 3: Start and wait for the banner

Hit Apply. The first start takes a moment while the container generates its config and a self-signed certificate. Watch the container log for:

  OpenCloud
  OPENCLOUD IS READY
⁠Step 4: Open the WebUI

Open https://<unraid-ip>:9200/ and accept the self-signed certificate once. Log in as admin with the password you set.

Plain Docker (no Unraid)
docker run -d \
  --name opencloud \
  --restart unless-stopped \
  -p 9200:9200 \
  -e PUID=99 -e PGID=100 \
  -e IDM_ADMIN_PASSWORD='change-me-please' \
  -e OC_URL='https://192.168.1.10:9200' \
  -e OC_INSECURE=true \
  -v /mnt/user/appdata/opencloud/config:/etc/opencloud \
  -v /mnt/user/appdata/opencloud/data:/var/lib/opencloud \
  junkerderprovinz/opencloud:rolling

Set OC_URL to how clients reach the server (its IP:port, or your proxied hostname).


⁠3. Configuration

VariableDefaultDescription
IDM_ADMIN_PASSWORD(required)Password for the built-in admin user, applied on first init. Set this.
OC_URLhttps://192.168.1.10:9200Required. Public URL clients use to reach OpenCloud, and the OIDC login issuer. Must be https. Set your server's real LAN IP:9200, or your external hostname behind a reverse proxy. Unraid does not auto-fill this.
OC_INSECUREtrueAccept the container's self-signed cert. Set false when a proxy provides a valid cert.
OC_LOG_LEVELinfoLog verbosity: info, warn, error, debug.
IDM_CREATE_DEMO_USERSfalseSeed demo users (test only, unsafe for real use).
PROXY_TLStrueOpenCloud terminates TLS itself on 9200. Set false behind a TLS-terminating proxy (see §6⁠).
PROXY_ENABLE_APP_AUTHfalseLet WebDAV clients sign in with a username and an app token. Needed by rclone and by phone sync apps, which cannot do the browser sign-in. Off by default; see §10⁠.
BRANDING_APPfalseAdd a Branding app where admins set the instance name, slogan, logos, favicon and login background. See §7⁠.
PUID99User ID OpenCloud runs as, Unraid's nobody.
PGID100Group ID, Unraid's users.
PortPurposeVolumePurpose
9200HTTPS WebUI / API (self-signed by default)/etc/opencloudConfig (opencloud.yaml + secrets)
/var/lib/opencloudData: user files, index, nats bus

No database. OpenCloud is not Nextcloud. It has no MySQL/Postgres and needs none. State lives in the local storage tree on the /var/lib/opencloud volume plus an embedded NATS bus. Don't add a database container; there's nothing to point it at.

Files show up but are greyed out / won't open? The storage driver is wrong. Keep STORAGE_USERS_DRIVER=posix (the default): never leave it blank and never use local; both leave files visible but unreadable. Also make sure the Data volume is on a filesystem with extended-attribute support (the Unraid array and cache/pool disks have it). The driver is fixed at first init. To change it, start with a fresh Data folder.


⁠S3 object storage (optional)

OpenCloud can keep file blobs in any S3-compatible bucket while the metadata stays local. Set the storage driver to decomposeds3 and add the connection variables. Keep the driver on posix (the default) for normal local storage. Do not leave it blank.

For self-hosted, two genuine, actively-maintained S3-compatible stores work well here: SeaweedFS⁠ (recommended for a single node) and Garage⁠ (built for geo-distributed multi-node clusters, but runs single-node here too). AWS S3, Backblaze B2 and Wasabi work the same way against their own endpoints.

VariableExampleDescription
STORAGE_USERS_DRIVERdecomposeds3Set to decomposeds3 for S3 blob storage. Default is posix (local): never leave it blank or use local, both grey out files.
STORAGE_USERS_DECOMPOSEDS3_ENDPOINThttp://192.168.1.10:8333S3 endpoint. Internal http:// URL for self-hosted SeaweedFS/Garage; the provider's https:// endpoint for AWS/B2/Wasabi.
STORAGE_USERS_DECOMPOSEDS3_REGIONdefaultdefault for SeaweedFS, garage for Garage (its default region), or the provider region (us-east-1, …) otherwise.
STORAGE_USERS_DECOMPOSEDS3_ACCESS_KEY…Access key ID.
STORAGE_USERS_DECOMPOSEDS3_SECRET_KEY…Secret access key.
STORAGE_USERS_DECOMPOSEDS3_BUCKETopencloudBucket name: create it first, the container does not.

Where those values come from:

  • SeaweedFS (self-hosted). In that template, set an Access Key and Secret Key (see its README's Security note) and optionally a Pre-create Bucket name. Those become your access key, secret key and bucket directly, no separate service-account step. Point the endpoint at its S3 port, http://<seaweedfs-ip>:8333, with region default.
  • Garage (self-hosted). In that template, set an Access Key and Secret Key (and optionally a Bucket). They are pre-seeded on first boot, no separate CLI step. Point the endpoint at its S3 API port, http://<garage-ip>:3900, with region garage.
  • AWS S3 / Backblaze B2 / Wasabi. Create a bucket in the provider console, then create an access key (AWS: an IAM access key; B2/Wasabi: an application/API key). Use the provider's https:// endpoint and the bucket's region.

The metadata always stays local. decomposeds3 puts only the blob bytes in S3; the file tree, xattrs and the blob→object mapping live on /var/lib/opencloud. That volume is therefore required and must be backed up even with S3. Losing it orphans your S3 objects (they are opaque IDs with no folder structure). There is no all-on-S3 mode. OpenCloud's system/metadata store (STORAGE_SYSTEM_DRIVER) stays decomposed (local) and needs no change.

If uploads fail with a checksum error on a non-AWS endpoint, add STORAGE_USERS_DECOMPOSEDS3_PUT_OBJECT_DISABLE_CONTENT_SHA256=true. Both the SeaweedFS and Garage paths use the same generic decomposeds3 driver this wrapper's S3 support was originally built and verified against. SeaweedFS has since been re-verified live against a real OpenCloud instance after the switch away from MinIO (connectivity, boot health and unauthenticated bucket reachability all confirmed; a fully authenticated file-read round-trip is the one check still outstanding). Garage has not yet been separately re-verified end-to-end.


⁠Web office (optional)

OpenCloud can edit documents in the browser, but it ships no office engine: it speaks the WOPI protocol to a separate document-server container. This wrapper wires that up from three template fields (all advanced, default off).

  1. Run a document server (its own container):
    • Euro Office (euro-office): the sovereign OnlyOffice fork, image ghcr.io/euro-office/documentserver, port 80. Set WOPI_ENABLED=true. There is a one-click Unraid template for it in the junkerderprovinz feed⁠ (search Euro Office in Community Applications). OpenCloud itself makes Euro Office the default editor for MS formats (docx/xlsx/pptx).
    • Collabora Online (CODE) (collabora): image collabora/code, port 9980. A maintained community CA template exists (search Collabora in Community Applications); set its WOPI host allowlist (aliasgroup1 / domain) to your OpenCloud URL. Best for ODF (odt/ods/odp).
    • OnlyOffice Document Server (onlyoffice): image onlyoffice/documentserver, port 80; set WOPI_ENABLED=true. A community CA template exists.
  2. Point OpenCloud at it: set Web office suite to euro-office, collabora or onlyoffice, Office document server URL to the server's browser-reachable URL, and an Office WOPI secret. For OnlyOffice and Euro Office that secret must equal the document server's JWT secret (JWT_SECRET / EURO_OFFICE_JWT_SECRET); for Collabora it is not required.
  3. Reverse proxy: forward /wopi and /collaboration to OpenCloud on port 9200, and make sure OpenCloud and the document server can reach each other over the network.

Under the hood the wrapper turns on OpenCloud's built-in collaboration service (OC_ADD_RUN_SERVICES=collaboration), sets the COLLABORATION_* variables, registers it as the secure-view/edit handler and exposes the secure-view role. It also writes a small csp.yaml adding the document server's origin to OpenCloud's Content-Security-Policy frame-src/img-src and points PROXY_CSP_CONFIG_FILE_LOCATION at it, so the editor iframe isn't CSP-blocked by the browser, a step OpenCloud's own reference deployment requires wiring by hand. Already set PROXY_CSP_CONFIG_FILE_LOCATION yourself? The wrapper leaves it alone; add the document server's origin to your own file's frame-src/img-src. Leave Web office suite on off (the default) if you do not need document editing.

⁠Full-text search (Apache Tika, optional)

OpenCloud already has a built-in search: out of the box it matches file and folder names and metadata (tags, media type, …). It does not look inside file contents on its own. Apache Tika is not a second search engine, it is a text-extractor that OpenCloud's search service uses to read the text out of documents (PDF, Word, Excel, PowerPoint, ODF, …) so a search word inside a file is found too. (The TIKA=:tika.yml / TIKA_IMAGE lines you may have seen belong to OpenCloud's official docker-compose deployment. This Unraid wrapper has no .env; the two template fields below do the wiring instead.)

  1. Run Apache Tika (its own container). Ready-made Tika templates exist in Community Applications (search Tika). Install one (image apache/tika, port 9998; a -full tag additionally does OCR of scanned images). Note its network-reachable address, e.g. http://<TIKA_IP>:9998.
  2. Turn it on: set Full-text search (Tika) to true and Tika server URL to that address, then Apply. The wrapper points OpenCloud's search extractor at Tika and switches full-text search on for you.

Under the hood the wrapper sets SEARCH_EXTRACTOR_TYPE=tika, SEARCH_EXTRACTOR_TIKA_TIKA_URL and FRONTEND_FULL_TEXT_SEARCH_ENABLED=true (plus SEARCH_EXTRACTOR_CS3SOURCE_INSECURE=true for the internal LAN cert). Only files uploaded or changed after this are content-indexed; existing files are not re-indexed automatically, so re-upload or edit a file to test. See the OpenCloud search docs⁠.


⁠4. Production vs Rolling

Two channels are built from this wrapper, differing only in the upstream base image:

TagBase imageFor
junkerderprovinz/opencloud:rollingopencloudeu/opencloud-rolling:latestDefault. Newest OpenCloud releases (currently 8.x), published about every three weeks.
junkerderprovinz/opencloud:latestopencloudeu/opencloud:latestOpenCloud's production line (currently the 7.2.x train), fully QA'd and cut about every six months. :production is kept as an alias, same image.

Which channel? Rolling is the default because the production line still carries two problems that bite on Unraid. As of 7.2.x it lacks the incremental-fsync fix (reva#720) for the large-folder sync abort on slow storage (issue #3027), which shipped in 7.3.0. More seriously, it treats a failed postprocessing event publish as fatal and ends the whole server process, so a single transient nats: timeout can take the container down; that was fixed in 7.5.0 (#3347⁠). Slow storage is precisely what produces those timeouts. Since production is cut roughly twice a year, the stable line will not carry the fix for months.

Pick :latest instead if you would rather have OpenCloud's fully QA'd line and your data volume already sits on a fast SSD/NVMe pool, which avoids the stall on its own. Switch by changing the Repository tag in the Unraid template. Back up your appdata before switching channels. Both channels track OpenCloud's own upstream :latest tag directly, and the weekly rebuild picks it up automatically alongside Alpine security patches, with no waiting on a version-bump PR to get merged.


⁠5. How the Wrapper Works

The entrypoint runs as root only long enough to prepare the volumes, then drops to your user:

  1. Permission heal. Creates /etc/opencloud + /var/lib/opencloud if missing and chowns them to PUID:PGID. The config dir is small and always fully healed; the data dir is only chown -R'd once (or after a PUID/PGID change), tracked by a .uid-heal sentinel, so a large data set is never recursively re-owned on every boot. The nats bus dir is always re-asserted (small, must stay writable).
  2. Branding app. With BRANDING_APP=true the entrypoint copies the web extension into the data volume, writes the managed proxy.yaml (unless you have your own, see §7⁠) and starts brandingd as PUID:PGID on 127.0.0.1:9299. It also points IDP_ASSET_PATH at the image's copy of OpenCloud's login page, unless you set that variable yourself. The copy adds one script, which loads the saved branding from brandingd. With false it removes the extension and the managed proxy.yaml. Whenever a saved branding exists, it also runs brandingd -regenerate once, app on or off, so the branding follows the base theme of the current image.
  3. Search index check. A bleve search index that OpenCloud cannot open would make the search service fail five times, and then the whole server stops. searchindex reads each index the way bleve loads it, and moves one that would fail to <name>.broken, so the service starts with a new, empty index. When a new index appears on data that already had one (after that move, or when an image update changes the index schema), the entrypoint indexes all spaces again in the background once the server is up. See §10⁠.
  4. Init. Runs opencloud init as the target user (writes opencloud.yaml, consuming IDM_ADMIN_PASSWORD). It is idempotent and harmlessly errors once the config exists.
  5. Hand-off. Prints the ready banner, then execs opencloud server dropped to PUID:PGID via a static gosu (copied from the upstream tianon/gosu image, so the base needs no package manager).

⁠6. Reverse Proxy

By default OpenCloud serves HTTPS itself on 9200 with a self-signed certificate, ideal for a direct LAN install. To put it behind a reverse proxy that terminates TLS (Traefik, NGINX Proxy Manager, SWAG, …):

  • set PROXY_TLS=false (OpenCloud then serves plain HTTP for the proxy to wrap),
  • set OC_URL to your external URL, e.g. https://cloud.example.com,
  • set OC_INSECURE=false (your proxy presents a valid certificate),
  • point the proxy upstream at the container's port 9200.

Desktop or mobile client login returns 403 Forbidden while the browser works? The native clients sign in through a loopback OIDC redirect (redirect_uri=http://127.0.0.1:<port>, per RFC 8252). Many reverse-proxy "block exploits" filters reject a literal http:// inside a query string. In NGINX Proxy Manager this is the "Block Common Exploits" toggle: its block-exploits.conf contains `if

Tag summary

Content type

Image

Digest

sha256:54e9e412c…

Size

73.2 MB

Last updated

1 day ago

docker pull junkerderprovinz/opencloud