Setting up an Office / Collaboration Space under Docker

Setting up an Office / Collaboration Space under Docker
Plainshawk Office and Collaboration Desk

Installing, Securing and Updating Nextcloud + Collabora

Mac Mini · Docker · Nginx Proxy Manager · MariaDB · Redis · Collabora

This guide describes the recommended deployment of Nextcloud with MariaDB and Redis, fronted by Nginx Proxy Manager (NPM), with Collabora Online providing browser-based document editing.

The design deliberately keeps HTTPS termination at Nginx Proxy Manager. Nextcloud and Collabora communicate over Docker's internal network using HTTP, while users and external clients always connect over HTTPS.


Architecture at a Glance

Only Nginx Proxy Manager is exposed to the LAN/Internet. MariaDB and Redis have no published host ports.


Key Lessons Incorporated From Your Setup

1. Never skip Nextcloud major versions

Nextcloud must be upgraded one major version at a time.

For example:

33 → 34 → 35 → 36

Do not attempt:

33 → 36

Before moving to the next major version, first bring the current major version up to its latest maintenance release.

This is also the official Nextcloud upgrade procedure. (Nextcloud)


2. Do not leave the installation-only admin variables in Compose

Do not permanently set:

NEXTCLOUD_ADMIN_USER
NEXTCLOUD_ADMIN_PASSWORD

They are intended for automatic first-run installation. The Docker image uses them when the instance is not yet installed. (Docker Hub)

For this installation we deliberately create the administrator through the web installer.

This also avoids confusing the initial installation process with the normal operation of the container.


3. The configuration directory must remain writable by www-data

Nextcloud's Apache container runs the application as UID 33 (www-data).

On macOS, Finder may display files owned by UID 33 as _appstore. This is normal.

If config.php cannot be read or written by UID 33, Nextcloud can enter an installation/login loop or produce errors such as:

Console has to be executed with the user that owns the file config/config.php

The Docker project's own troubleshooting examples show this exact ownership issue. (GitHub)


4. Redis and APCu should be explicitly configured

For a single-server installation, Nextcloud recommends:

APCu       → local cache
Redis      → distributed cache
Redis      → transactional file locking

This is the configuration used here. (Nextcloud)


5. MariaDB must be allowed enough time to initialise

Docker Desktop on macOS can take longer to initialise MariaDB than a native Linux Docker host.

The database healthcheck therefore has:

start_period: 30s

Nextcloud waits for the database and Redis healthchecks before starting.


6. Never use --lower-case-table-names=1

Do not add:

--lower-case-table-names=1

to MariaDB.

This can cause serious problems with existing Nextcloud databases and should not be introduced into this installation.


7. Collabora is behind Nginx Proxy Manager

NPM handles:

HTTPS
Let's Encrypt
TLS termination
WebSockets

Collabora therefore listens internally using HTTP.

The NPM proxy host must use:

Scheme: http
Forward port: 9980

not HTTPS.


8. Back up before every Nextcloud upgrade

Nextcloud does not support normal downgrades. If an upgrade fails, the supported recovery mechanism is restoration from backup rather than simply installing the previous version over the top. (Nextcloud)

Therefore:

Backup first. Upgrade second.

Prerequisites

Before beginning, confirm:

  • Docker Desktop is running.
  • proxy-net already exists.
  • Nginx Proxy Manager is installed.
  • Ports 80 and 443 are forwarded by the router.
  • Cloudflare A records exist for:
    • cloud.plainshawk.co.uk
    • office.plainshawk.co.uk
  • Both Cloudflare records are DNS only / grey cloud.
  • Your existing NPM container is connected to proxy-net.
  • Vaultwarden is available for storing passwords and recovery information.

Part 1 — Nextcloud

Stage 1 — Create the Directory Structure

Create the Docker configuration and persistent data directories:

mkdir -p ~/docker/nextcloud
mkdir -p /Volumes/Media-Home/nextcloud-data

cd ~/docker/nextcloud

The resulting structure will be:

~/docker/nextcloud/
├── docker-compose.yml
├── config/
├── apps/
├── themes/
└── db/

/Volumes/Media-Home/nextcloud-data/
└── Nextcloud user files

The database lives under:

~/docker/nextcloud/db

while the large user-data directory lives on the external SSD:

/Volumes/Media-Home/nextcloud-data

Stage 2 — Create the Compose File

nano docker-compose.yml

Use:

services:

  nextcloud-db:
    image: mariadb:11
    container_name: nextcloud-db
    restart: unless-stopped

    command:
      - --transaction-isolation=READ-COMMITTED
      - --log-bin=binlog
      - --binlog-format=ROW

    environment:
      - MYSQL_ROOT_PASSWORD=choose_a_root_password
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud
      - MYSQL_PASSWORD=choose_a_db_password

    volumes:
      - ./db:/var/lib/mysql

    healthcheck:
      test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
      interval: 10s
      timeout: 5s
      retries: 10
      start_period: 30s


  nextcloud-redis:
    image: redis:alpine
    container_name: nextcloud-redis
    restart: unless-stopped

    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 10s


  nextcloud:
    image: nextcloud:latest
    container_name: nextcloud
    restart: unless-stopped

    depends_on:
      nextcloud-db:
        condition: service_healthy
      nextcloud-redis:
        condition: service_healthy

    environment:

      # MariaDB
      - MYSQL_HOST=nextcloud-db
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud
      - MYSQL_PASSWORD=choose_a_db_password

      # Redis
      - REDIS_HOST=nextcloud-redis
      - REDIS_HOST_PORT=6379

      # Nextcloud
      - NEXTCLOUD_TRUSTED_DOMAINS=cloud.plainshawk.co.uk
      - TZ=Europe/London

      # Reverse proxy
      - APACHE_DISABLE_REWRITE_IP=1
      - TRUSTED_PROXIES=172.16.0.0/12
      - OVERWRITEHOST=cloud.plainshawk.co.uk
      - OVERWRITEPROTOCOL=https
      - OVERWRITECLIURL=https://cloud.plainshawk.co.uk

    volumes:
      - ./config:/var/www/html/config
      - ./apps:/var/www/html/custom_apps
      - ./themes:/var/www/html/themes
      - /Volumes/Media-Home/nextcloud-data:/var/www/html/data

    networks:
      - default
      - proxy-net


networks:

  default:

  proxy-net:
    external: true

Important

There are deliberately no:

NEXTCLOUD_ADMIN_USER=
NEXTCLOUD_ADMIN_PASSWORD=

entries.

The database variables are sufficient to pre-populate the database section of the installation wizard. The administrator will be created through the browser.

The official Docker image supports these database environment variables as part of its automatic configuration process. (Docker Hub)


Stage 3 — Start Nextcloud

Start the stack:

docker compose up -d

Watch the logs:

docker compose logs -f nextcloud

The first startup can take several minutes.

Press Ctrl+C once the container is running normally.

Then check the complete stack:

docker compose ps

You should see:

nextcloud-db       Up (healthy)
nextcloud-redis    Up (healthy)
nextcloud          Up

Stage 4 — Complete the First-Run Installation

Navigate to:

https://cloud.plainshawk.co.uk

The Nextcloud setup wizard should appear.

Create your administrator account.

Use a strong password and store it in Vaultwarden.

The database section should already be populated from the Compose environment:

Database:
MySQL/MariaDB

Database user:
nextcloud

Database password:
your database password

Database name:
nextcloud

Database host:
nextcloud-db

Click:

Install

Allow the installation to complete.

Do not interrupt the browser while the initial database is being created.


Stage 5 — Fix and Verify Permissions

Nextcloud's configuration and application directories must be accessible to UID 33.

Run:

sudo chown -R 33:33 ~/docker/nextcloud/config/
sudo chown -R 33:33 ~/docker/nextcloud/apps/
sudo chown -R 33:33 ~/docker/nextcloud/themes/
sudo chown -R 33:33 /Volumes/Media-Home/nextcloud-data/

Verify:

ls -ln ~/docker/nextcloud/config/config.php

The owner should be:

33

Do not be concerned if Finder displays this as _appstore.


Stage 6 — Configure Redis and APCu

Redis being present as a container is not enough. Nextcloud needs to be explicitly configured to use it.

Run:

docker exec -u www-data nextcloud php occ \
  config:system:set redis host \
  --value="nextcloud-redis"

docker exec -u www-data nextcloud php occ \
  config:system:set redis port \
  --value=6379 \
  --type=integer

docker exec -u www-data nextcloud php occ \
  config:system:set redis timeout \
  --value=0 \
  --type=integer

Configure the cache:

docker exec -u www-data nextcloud php occ \
  config:system:set memcache.local \
  --value="\OC\Memcache\APCu"

docker exec -u www-data nextcloud php occ \
  config:system:set memcache.distributed \
  --value="\OC\Memcache\Redis"

docker exec -u www-data nextcloud php occ \
  config:system:set memcache.locking \
  --value="\OC\Memcache\Redis"

Verify:

docker exec -u www-data nextcloud php occ \
  config:system:get memcache.local

docker exec -u www-data nextcloud php occ \
  config:system:get memcache.distributed

docker exec -u www-data nextcloud php occ \
  config:system:get memcache.locking

The expected values are:

\OC\Memcache\APCu
\OC\Memcache\Redis
\OC\Memcache\Redis

This follows Nextcloud's recommended APCu + Redis configuration for a single-server deployment. (Nextcloud)


Stage 7 — Post-Installation Optimisation

Run:

docker exec -u www-data nextcloud php occ \
  db:add-missing-indices

Update the MIME database:

docker exec -u www-data nextcloud php occ \
  maintenance:mimetype:update-db

docker exec -u www-data nextcloud php occ \
  maintenance:mimetype:update-js

Run the repair routines:

docker exec -u www-data nextcloud php occ \
  maintenance:repair --include-expensive

Set the maintenance window:

docker exec -u www-data nextcloud php occ \
  config:system:set maintenance_window_start \
  --value=2 \
  --type=integer

Stage 8 — Configure Background Jobs

This is an important addition to the original installation.

Nextcloud recommends cron rather than AJAX for reliable background processing. (Nextcloud)

The official Docker project provides a cron.sh mechanism and Compose examples using a second Nextcloud container for this purpose. (GitHub)

Add this service to docker-compose.yml:

  nextcloud-cron:
    image: nextcloud:latest
    container_name: nextcloud-cron
    restart: unless-stopped

    entrypoint: /cron.sh

    depends_on:
      nextcloud-db:
        condition: service_healthy
      nextcloud-redis:
        condition: service_healthy

    environment:
      - MYSQL_HOST=nextcloud-db
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud
      - MYSQL_PASSWORD=choose_a_db_password
      - REDIS_HOST=nextcloud-redis
      - REDIS_HOST_PORT=6379
      - TZ=Europe/London

    volumes:
      - ./config:/var/www/html/config
      - ./apps:/var/www/html/custom_apps
      - ./themes:/var/www/html/themes
      - /Volumes/Media-Home/nextcloud-data:/var/www/html/data

    networks:
      - default

The application and cron containers must have matching Nextcloud volumes; the official Docker examples specifically call this out. (GitHub)

Recreate the stack:

docker compose up -d

Then set Nextcloud's background-job mode to cron:

docker exec -u www-data nextcloud php occ background:cron

Verify:

docker exec -u www-data nextcloud php occ background:cron

Nextcloud should report:

Set mode for background jobs to 'cron'

Check the cron container:

docker logs nextcloud-cron

Stage 9 — Add the Nextcloud Proxy Host in NPM

Log into Nginx Proxy Manager.

Create a new Proxy Host.

Details

Field Value
Domain cloud.plainshawk.co.uk
Scheme http
Forward Hostname nextcloud
Forward Port 80
Cache Assets Off
Block Common Exploits On
Websockets Support On

SSL

Select:

  • Request a new Let's Encrypt certificate
  • Force SSL: On
  • HTTP/2 Support: On

Advanced

client_max_body_size 10240M;

proxy_request_buffering off;

proxy_read_timeout 600s;
proxy_send_timeout 600s;
send_timeout 600s;

# Block common WordPress scanners
# Do NOT block PHP — Nextcloud uses PHP.

location ~ /(wp-admin|wp-login|wp-content|wp-includes|xmlrpc\.php) {
    return 444;
}

location ~ /\.(env|git|svn|aws|ssh|npmrc) {
    return 444;
}

location ~ /(phpmyadmin|pma|adminer|mysql)/ {
    return 444;
}

# CalDAV / CardDAV discovery
location /.well-known/carddav {
    return 301 $scheme://$host/remote.php/dav;
}

location /.well-known/caldav {
    return 301 $scheme://$host/remote.php/dav;
}

Save the proxy host.


Stage 10 — Verify Nextcloud

Open:

https://cloud.plainshawk.co.uk

Log in.

Then go to:

Administration Settings → Overview

Check for:

  • Database warnings
  • Redis warnings
  • Background-job warnings
  • Trusted-domain warnings
  • HTTPS warnings
  • Missing PHP modules
  • Security warnings

Fix any critical warnings before proceeding.


Part 2 — Collabora Online

Stage 11 — Create the Collabora Directory

mkdir -p ~/docker/collabora
cd ~/docker/collabora

Stage 12 — Create the Collabora Compose File

nano docker-compose.yml

Use:

services:

  collabora:
    image: collabora/code:latest
    container_name: collabora
    restart: unless-stopped

    cap_add:
      - MKNOD

    environment:
      - aliasgroup1=https://cloud.plainshawk.co.uk:443
      - DONT_GEN_SSL_CERT=YES
      - server_name=office.plainshawk.co.uk
      - dictionaries=en_GB en_US
      - TZ=Europe/London
      - username=admin
      - password=choose_a_collabora_admin_password
      - SSL_ENABLE=false
      - SSL_TERMINATION=true
      - extra_params=--o:ssl.enable=false --o:ssl.termination=true

    networks:
      - proxy-net


networks:

  proxy-net:
    external: true

Why the SSL settings are duplicated

Nginx Proxy Manager is responsible for HTTPS.

The traffic path is:

Browser
   │
   │ HTTPS
   ▼
NPM
   │
   │ HTTP
   ▼
Collabora :9980

The explicit:

extra_params=--o:ssl.enable=false --o:ssl.termination=true

is retained because recent Collabora CODE releases have continued to show certificate-startup problems in reverse-proxy deployments when SSL is disabled only through the environment configuration. (GitHub)


Stage 13 — Start Collabora

docker compose up -d

Watch the logs:

docker compose logs -f collabora

Look for a successful startup and a message indicating that the service is ready.

Press:

Ctrl+C

once startup is complete.

Check:

docker compose ps

Stage 14 — Add the Collabora Proxy Host

In NPM:

Proxy Hosts → Add Proxy Host

Details

Field Value
Domain office.plainshawk.co.uk
Scheme http
Forward Hostname collabora
Forward Port 9980
Cache Assets Off
Block Common Exploits Off
Websockets Support On

The important point is:

Do not select HTTPS as the upstream scheme.

NPM is terminating TLS.

SSL

  • Request Let's Encrypt certificate
  • Force SSL: On
  • HTTP/2 Support: On

Advanced

proxy_buffering off;

proxy_read_timeout 36000s;
proxy_send_timeout 36000s;
proxy_connect_timeout 36000s;
send_timeout 36000s;

client_max_body_size 0;

proxy_http_version 1.1;

proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;


location ^~ /browser {
    proxy_pass http://collabora:9980;
}

location ^~ /hosting/discovery {
    proxy_pass http://collabora:9980;
}

location ^~ /hosting/capabilities {
    proxy_pass http://collabora:9980;
}

location ~ ^/cool/(.*)/ws$ {
    proxy_pass http://collabora:9980;

    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
}

location ~ ^/(c|l)ool {
    proxy_pass http://collabora:9980;
}

location ^~ /cool/adminws {
    proxy_pass http://collabora:9980;
}


# Scanner blocking

location ~ \.(php|php\d|phtml|asp|aspx|cgi)$ {
    return 444;
}

location ~ /(wp-admin|wp-login|wp-content|wp-includes|xmlrpc\.php) {
    return 444;
}

location ~ /\.(env|git|svn|aws|ssh|npmrc) {
    return 444;
}

Save the proxy host.


Stage 15 — Install Nextcloud Office

In Nextcloud:

Apps → Office & text

Find:

Nextcloud Office

Install and enable it.

Then go to:

Administration Settings → Nextcloud Office

Select:

Use your own server

Enter:

https://office.plainshawk.co.uk

Save.

You should receive confirmation that the Collabora Online server is reachable.

If your version exposes the WOPI allow-list setting, restrict it to your internal Docker network:

172.16.0.0/12

Stage 16 — Test Collabora

Go to:

Files → New

Create:

  • New document
  • New spreadsheet
  • New presentation

Each should open in the browser using Collabora.

If documents open but the editor is blank or repeatedly reconnects, check the NPM WebSocket configuration first.


Stage 17 — Configure Nextcloud Background Jobs

Return to:

Administration Settings → System

Confirm that Background jobs are configured for:

Cron

You can verify from the command line:

docker exec -u www-data nextcloud php occ background:cron

The cron container should then execute /cron.sh automatically.

Reliable background jobs are particularly important for activities such as notifications, file cleanup, previews and other scheduled maintenance. Nextcloud recommends cron over the default AJAX scheduler. (Nextcloud)


Stage 18 — Backup Nextcloud Properly

The original backup procedure backed up the database but not the complete Nextcloud installation.

For a recoverable backup, you should preserve:

MariaDB database
Nextcloud config
Nextcloud application data
Custom apps
Themes
User data

The Docker image documentation recommends persistent storage for both the database and Nextcloud data so that they can be backed up independently. (Docker Hub)

Database backup

Store the database password somewhere appropriate rather than hard-coding it into a public script.

For example, with your existing local setup:

docker exec nextcloud-db mariadb-dump \
  -u nextcloud \
  -p'YOUR_DATABASE_PASSWORD' \
  nextcloud \
  > "$BACKUP_DIR/nextcloud_$DATE.sql"

Nextcloud files

Add:

# Nextcloud configuration
cp -r ~/docker/nextcloud/config \
  "$BACKUP_DIR/nextcloud_config_$DATE"

# Custom applications
cp -r ~/docker/nextcloud/apps \
  "$BACKUP_DIR/nextcloud_apps_$DATE"

# Themes
cp -r ~/docker/nextcloud/themes \
  "$BACKUP_DIR/nextcloud_themes_$DATE"

# User data
cp -r /Volumes/Media-Home/nextcloud-data \
  "$BACKUP_DIR/nextcloud_data_$DATE"

echo "Nextcloud backup complete"

Important

A raw copy of the MariaDB database directory while MariaDB is running is not a substitute for a logical database dump.

The SQL dump should therefore be part of the normal backup.


Stage 19 — Create a Nextcloud Pre-Upgrade Backup Function

Add this to:

nano ~/.zshrc
nextcloud-backup() {

    DATE=$(date +%Y%m%d_%H%M%S)

    BACKUP_DIR="/Users/gavin/docker/backups"

    mkdir -p "$BACKUP_DIR"

    echo "Backing up Nextcloud database..."

    docker exec nextcloud-db mariadb-dump \
        -u nextcloud \
        -p'YOUR_DATABASE_PASSWORD' \
        nextcloud \
        > "$BACKUP_DIR/nextcloud_db_preop_$DATE.sql"

    echo "Backing up Nextcloud configuration..."

    cp -r ~/docker/nextcloud/config \
        "$BACKUP_DIR/nextcloud_config_preop_$DATE"

    echo "Backing up Nextcloud data..."

    cp -r /Volumes/Media-Home/nextcloud-data \
        "$BACKUP_DIR/nextcloud_data_preop_$DATE"

    echo "Nextcloud backup complete."

}

Reload:

source ~/.zshrc

Before an upgrade:

nextcloud-backup

Stage 20 — Add Nextcloud and Collabora to the Startup Script

Edit:

nano ~/docker/start-all.sh

Make sure these appear before Nginx Proxy Manager:

echo "Starting Nextcloud..."
cd /Users/gavin/docker/nextcloud && docker compose up -d

echo "Starting Collabora..."
cd /Users/gavin/docker/collabora && docker compose up -d

Because the Nextcloud Compose file contains health-based dependencies, MariaDB and Redis will be brought up before Nextcloud itself.

NPM should still be started last so that its upstream containers are already present on proxy-net.


Stage 21 — Add Both Services to Uptime Kuma

Create these monitors.

Nextcloud

Field Value
Name Nextcloud
Type HTTP(s)
URL https://cloud.plainshawk.co.uk/status.php
Interval 60 seconds
Retries 3

Collabora

Field Value
Name Collabora
Type HTTP(s)
URL http://collabora:9980/hosting/discovery
Interval 60 seconds
Retries 3

The Collabora monitor deliberately uses the internal Docker address.

This tests the actual service rather than introducing NPM, DNS and Let's Encrypt as additional failure points.


Updating Nextcloud

The Golden Rule

Never upgrade Nextcloud without a fresh backup.

Nextcloud does not support normal downgrades. (Nextcloud)

The official procedure is to upgrade sequentially through each major release and first bring the existing major version up to its latest maintenance release. (Nextcloud)


Minor / Maintenance Update

For example:

34.0.1 → 34.0.2

First determine the installed version:

docker exec -u www-data nextcloud php occ status

Then:

cd ~/docker/nextcloud

# Back up everything important
nextcloud-backup

# Back up configuration separately
cp -r ./config \
  "./config-backup-$(date +%Y%m%d_%H%M%S)"

# Enable maintenance mode
docker exec -u www-data nextcloud php occ \
  maintenance:mode --on

# Pull the new image
docker compose pull nextcloud

# Recreate only Nextcloud
docker compose up -d --force-recreate nextcloud

# Watch startup
docker compose logs -f nextcloud

Then run:

docker exec -u www-data nextcloud php occ upgrade

Follow with:

docker exec -u www-data nextcloud php occ \
  db:add-missing-indices

Then:

docker exec -u www-data nextcloud php occ \
  maintenance:repair --include-expensive

Finally:

docker exec -u www-data nextcloud php occ \
  maintenance:mode --off

Check:

docker exec -u www-data nextcloud php occ status

Major Version Update

Suppose the current version is:

34.x

and you want:

35.x

First make sure you are on the latest 34.x maintenance release.

Then:

cd ~/docker/nextcloud

nextcloud-backup

cp -r ./config \
  "./config-backup-$(date +%Y%m%d_%H%M%S)"

Enable maintenance mode:

docker exec -u www-data nextcloud php occ \
  maintenance:mode --on

Change the image from:

image: nextcloud:latest

to the target major:

image: nextcloud:35

This gives you deliberate control over the major version rather than allowing an unexpected major upgrade.

Pull and recreate:

docker compose pull nextcloud
docker compose up -d --force-recreate nextcloud

Watch carefully:

docker compose logs -f nextcloud

Run:

docker exec -u www-data nextcloud php occ upgrade

Then:

docker exec -u www-data nextcloud php occ \
  db:add-missing-indices

docker exec -u www-data nextcloud php occ \
  maintenance:mimetype:update-db

docker exec -u www-data nextcloud php occ \
  maintenance:mimetype:update-js

docker exec -u www-data nextcloud php occ \
  maintenance:repair --include-expensive

Disable maintenance mode:

docker exec -u www-data nextcloud php occ \
  maintenance:mode --off

Verify:

docker exec -u www-data nextcloud php occ status

Check the web interface and Uptime Kuma before proceeding.


Repeat for Every Major Version

For example:

34 → 35
35 → 36
36 → 37

Do not jump directly from:

34 → 37

Nextcloud explicitly requires sequential major upgrades. (Nextcloud)

Also allow background migrations to complete before immediately performing another major upgrade. (Nextcloud)


Updating Collabora

Collabora has no Nextcloud database migration to perform, so its updates are considerably simpler.

cd ~/docker/collabora

docker compose pull

docker compose up -d --force-recreate

docker compose logs -f collabora

Look for the normal ready/startup messages.

Then test:

https://cloud.plainshawk.co.uk

Create or edit a document.

Important

Collabora's recent Docker releases have had compatibility issues around SSL termination and WebSockets, so do not assume that every latest update is harmless.

After a Collabora update always test:

  1. Open a document.
  2. Edit it.
  3. Save it.
  4. Reopen it.
  5. Test a spreadsheet.
  6. Test a presentation.

If the editor suddenly fails after an update, check the Collabora logs before changing the NPM configuration.


Quick Reference

URL Purpose
https://cloud.plainshawk.co.uk Nextcloud
https://cloud.plainshawk.co.uk/status.php Nextcloud health check
https://office.plainshawk.co.uk Collabora Online endpoint
https://office.plainshawk.co.uk/browser/dist/admin/admin.html Collabora administration

Troubleshooting

Nextcloud enters an installation/login loop

Check that these are not present in the Compose file:

NEXTCLOUD_ADMIN_USER
NEXTCLOUD_ADMIN_PASSWORD

Then check:

cat ~/docker/nextcloud/config/config.php | grep installed

You should see:

'installed' => true,

If permissions are wrong:

sudo chown -R 33:33 ~/docker/nextcloud/config/

Restart:

docker compose up -d

maintenance:install or similar installation errors appear

Check the configuration:

docker exec -u www-data nextcloud php occ status

If occ cannot read config.php, check its ownership:

ls -ln ~/docker/nextcloud/config/config.php

It should be owned by UID 33.


Redis isn't being used

Check:

docker exec -u www-data nextcloud php occ \
  config:system:get memcache.locking

Expected:

\OC\Memcache\Redis

Then:

docker exec -u www-data nextcloud php occ \
  config:system:get redis host

Expected:

nextcloud-redis

Nextcloud reports background-job warnings

Check:

docker ps | grep nextcloud-cron

Then:

docker logs nextcloud-cron

Confirm the scheduler:

docker exec -u www-data nextcloud php occ background:cron

Nextcloud recommends cron mode for reliable production background processing. (Nextcloud)


Collabora crashes with a CA certificate error

Check:

docker logs collabora

If you see an error referring to:

ca-chain.cert.pem

verify that the Compose file contains:

- DONT_GEN_SSL_CERT=YES
- SSL_ENABLE=false
- SSL_TERMINATION=true
- extra_params=--o:ssl.enable=false --o:ssl.termination=true

Then:

docker compose up -d --force-recreate

Recent Collabora releases have demonstrated this exact failure mode when SSL termination is handled by an external reverse proxy. (GitHub)


"Collabora Online server is not reachable"

Check that:

aliasgroup1=https://cloud.plainshawk.co.uk:443

exactly matches the Nextcloud URL.

Check:

server_name=office.plainshawk.co.uk

Then test the internal service:

docker exec nextcloud \
  curl -s http://collabora:9980/hosting/discovery

You should receive XML describing the Collabora capabilities.

Also check that NPM uses:

Scheme: http
Host: collabora
Port: 9980

Collabora documents open but the editor is blank

The most likely cause is WebSocket handling.

In NPM confirm:

Websockets Support: On

and that the Advanced configuration contains:

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";

502 Bad Gateway from NPM

Check:

docker ps

Then:

docker network inspect proxy-net

Confirm both:

nextcloud
collabora
nginx-proxy-manager

are connected to the network.

Test Nextcloud directly from NPM:

docker exec nginx-proxy-manager \
  curl -I http://nextcloud:80/status.php

Test Collabora:

docker exec nginx-proxy-manager \
  curl -I http://collabora:9980/hosting/discovery

MariaDB connection lost after reboot

Check:

docker compose ps

MariaDB should report:

healthy

before Nextcloud starts.

Check:

docker logs nextcloud-db

The:

start_period: 30s

healthcheck delay is deliberately present because MariaDB can take longer to initialise on Docker Desktop/macOS.


Nextcloud reports a database warning after an upgrade

Run:

docker exec -u www-data nextcloud php occ \
  db:add-missing-indices

Then:

docker exec -u www-data nextcloud php occ \
  maintenance:repair --include-expensive

Check the Nextcloud Administration → Overview page again.


Nextcloud shows a 502 immediately after a major upgrade

Do not immediately roll back the container.

Check:

docker compose logs -f nextcloud

Then:

docker exec -u www-data nextcloud php occ status

Database migrations can take time.

If the upgrade has genuinely failed, restore the database, configuration and data from the pre-upgrade backup rather than attempting to downgrade the installation in place. Nextcloud does not support normal downgrades. (Nextcloud)


Operational Checklist

Daily

  • Uptime Kuma monitors Nextcloud.
  • Uptime Kuma monitors Collabora.
  • Nightly backup completes successfully.

Weekly

  • Check Nextcloud Administration → Overview.
  • Check for available maintenance/security updates.
  • Review Docker logs for recurring errors.
  • Verify backup storage.

Before every Nextcloud update

☐ Fresh database backup
☐ Fresh configuration backup
☐ Fresh user-data backup
☐ Check current Nextcloud version
☐ Read release/critical-change notes
☐ Check third-party app compatibility
☐ Enable maintenance mode
☐ Upgrade only one major version
☐ Run occ upgrade
☐ Run repair/index maintenance
☐ Disable maintenance mode
☐ Test web interface
☐ Test file upload/download
☐ Test mobile/client sync
☐ Check Uptime Kuma

Before every Collabora update

☐ Check Collabora release notes
☐ Pull new image
☐ Recreate container
☐ Check logs
☐ Open a document
☐ Edit and save
☐ Test spreadsheet
☐ Test presentation
☐ Check WebSockets

Final Architecture

The resulting design deliberately separates the major components:

                         INTERNET
                             │
                        HTTPS :443
                             │
                             ▼
                    ┌─────────────────┐
                    │ Cloudflare DNS  │
                    │  DNS only       │
                    └────────┬────────┘
                             │
                             ▼
                    ┌─────────────────┐
                    │ Home Router     │
                    │ 80 / 443        │
                    └────────┬────────┘
                             │
                             ▼
                  ┌──────────────────────┐
                  │ Nginx Proxy Manager  │
                  │                      │
                  │ TLS termination      │
                  │ Let's Encrypt        │
                  │ WebSockets           │
                  └──────────┬───────────┘
                             │
                       proxy-net
                             │
               ┌─────────────┴──────────────┐
               │                            │
               ▼                            ▼
       ┌───────────────┐            ┌────────────────┐
       │   Nextcloud   │◄──────────►│   Collabora    │
       │      :80      │            │     :9980      │
       └───────┬───────┘            └────────────────┘
               │
        ┌──────┴───────┐
        │              │
        ▼              ▼
  ┌───────────┐   ┌───────────┐
  │  MariaDB  │   │   Redis   │
  │    :3306  │   │   :6379   │
  └───────────┘   └───────────┘
        │
        ▼
  /Users/gavin/docker/
  /Volumes/Media-Home/

        ▲
        │
  ┌───────────────┐
  │    Backups    │
  │ DB + config + │
  │ apps + data   │
  └───────────────┘

The key security principle is that MariaDB and Redis never need to be exposed outside Docker, while NPM is the sole public-facing gateway. Nextcloud and Collabora communicate internally, and all external access is protected by HTTPS at the proxy layer.