Files
nextcloud-docker/README.md
Raghav c79a25e34d Deploy Talk HPB/client push and document rollout steps
Add compose/nginx/env changes for Talk HPB + notify_push integration, and document deployment + hardening commands in README.

Co-Authored-By: Oz <oz-agent@warp.dev>
2026-04-17 12:47:01 +05:30

10 KiB

Nextcloud on Ubuntu via Docker for nxt.bhatfamily.in

This repository deploys Nextcloud behind Nginx using Docker Compose.

Exposed ports:

  • HTTP: 8082
  • HTTPS: 8446

Target hostname:

  • nxt.bhatfamily.in

What changed

The stack now includes:

  • Fixed Nginx mount path (nginx/nginx.conf mapped correctly)
  • Fixed MariaDB command (mariadbd)
  • Nginx reverse proxy mode for nextcloud:apache (no FastCGI mismatch)
  • Production TLS provisioning using Let's Encrypt DNS-01 with Cloudflare
  • Automated TLS renewal job support (cron)
  • Nextcloud app startup fixes for Apache ServerName and writable Fontconfig cache
  • Persistent web-updater enablement (upgrade-disable-web.config.php forced to false)
  • Reverse-proxy trust configuration in Nextcloud (trusted_proxies, forwarded_for_headers)
  • Nginx hardening (server_tokens off, stronger HSTS, hide X-Powered-By, TLS session hardening)
  • Brute-force protection explicitly enabled and maintenance window configured
  • Scripted Nextcloud Hub upgrade workflow: scripts/update-nextcloud-hub.sh
  • Automated Hub update scheduler: scripts/setup-hub-update-cron.sh
  • Redis service for transactional file locking and distributed cache
  • Floating app image tag enabled: nextcloud:apache (major upgrades supported with staged path)
  • Setup warning remediation integrated (missing indices, mimetype migrations, log-noise cleanup)
  • Nextcloud Talk high-performance backend service (talk-hpb) with signaling endpoint
  • Nextcloud desktop client push via notify_push app and notify-push service

Current baseline (Apr 2026)

  • App image: nextcloud:apache
  • DB image: mariadb:11.4
  • Cache/locking: redis:7-alpine
  • Nextcloud version at last validation: 33.0.2
  • Talk HPB: ghcr.io/nextcloud-releases/aio-talk:latest via /standalone-signaling/
  • Client push: notify_push app + nextcloud-notify-push service via /push/

Prerequisites

  • Ubuntu host with Docker + Docker Compose plugin (or docker-compose)
  • Domain nxt.bhatfamily.in in Cloudflare DNS
  • DNS A record for nxt pointing to your server public IP (DNS-only)
  • Router/firewall forwarding for ports 8082 and 8446

Initial setup

  1. Create runtime env file:
cp .env.example .env
  1. Edit .env with strong values.
  2. Start stack with bootstrap TLS:
./scripts/install.sh
  1. Validate:
./scripts/test.sh

Enable web updater (one-time verification)

Web updater is enabled by design for this deployment.

Check values:

docker exec --user www-data nextcloud-app php occ config:list system | grep -E "upgrade.disable-web|updater.release.channel"

Expected:

  • upgrade.disable-web: false
  • updater.release.channel: stable

Update Nextcloud Hub (scripted)

Use the upgrade helper script to pull images, apply upgrades, run repairs, and validate endpoints.

Run update:

./scripts/update-nextcloud-hub.sh

Optional flags:

  • Skip app marketplace updates:
RUN_APP_UPDATES=0 ./scripts/update-nextcloud-hub.sh
  • Skip expensive repairs/mimetype migration pass:
RUN_EXPENSIVE_REPAIR=0 ./scripts/update-nextcloud-hub.sh
  • Require strict TLS validation during smoke tests (no -k):
STRICT_TLS=1 ./scripts/update-nextcloud-hub.sh

What the script does:

  • pulls latest db, app, and web images
  • recreates services and restarts web to refresh upstream resolution
  • ensures web updater is enabled and release channel is configured
  • ensures maintenance mode is off before occ upgrade
  • runs occ upgrade
  • runs occ app:update --all (unless disabled)
  • runs schema and repair commands (db:add-missing-*, maintenance:repair)
  • optionally runs maintenance:repair --include-expensive
  • runs occ status, occ setupchecks, and scripts/test.sh

Important: major upgrades are one-at-a-time

Nextcloud only supports upgrading one major version at a time.

If the floating nextcloud:apache tag is more than one major ahead of your installed version, do staged upgrades first, for example:

  1. nextcloud:31-apache → run updater script
  2. nextcloud:32-apache → run updater script
  3. nextcloud:33-apache → run updater script
  4. switch back to nextcloud:apache

Automate Hub updates (cron)

Install/update a weekly cron job (default: Sunday 02:30):

./scripts/setup-hub-update-cron.sh

Optional custom schedule:

HUB_UPDATE_CRON_SCHEDULE="15 2 * * 6" ./scripts/setup-hub-update-cron.sh

This setup script will:

  • create/update .hub-update.env (local only, not committed)
  • install/refresh cron entry for scripts/update-nextcloud-hub.sh
  • write logs to logs/hub-update.log

Defaults tracked in .hub-update.env.example:

  • RUN_APP_UPDATES=1
  • RUN_EXPENSIVE_REPAIR=1
  • STRICT_TLS=0
  • ENABLE_WEB_UPDATER=1
  • UPDATER_RELEASE_CHANNEL=stable

Setup warning remediation notes

The following warning-focused fixes are now part of the deployed configuration:

  • Transactional File Locking: Redis-backed locking enabled (memcache.locking=Redis)
  • Mimetype migrations: addressed via maintenance:repair --include-expensive
  • Missing optional indices: addressed via occ db:add-missing-indices
  • AppAPI deploy daemon errors: app_api disabled (not used in this deployment)
  • Internet connectivity check log spam: has_internet_connection=false set intentionally for this environment

If you want AppAPI external apps later:

  • re-enable app: docker exec --user www-data nextcloud-app php occ app:enable app_api
  • configure a reachable deploy daemon from Settings > AppAPI

Deploy Talk HPB and client push

This deployment includes Talk HPB and desktop client push support in Docker Compose.

Required secrets in .env (already templated in .env.example):

  • TALK_TURN_SECRET
  • TALK_SIGNALING_SECRET
  • TALK_INTERNAL_SECRET
  1. Start/update services:
docker compose up -d app talk-hpb notify-push web
  1. Configure Talk signaling/STUN/TURN in Nextcloud:
set -a; source .env; set +a
docker exec --user www-data nextcloud-app php occ talk:signaling:add --verify https://nxt.bhatfamily.in:8446/standalone-signaling "$TALK_SIGNALING_SECRET"
docker exec --user www-data nextcloud-app php occ talk:stun:add nxt.bhatfamily.in:3478
docker exec --user www-data nextcloud-app php occ talk:turn:add --secret="$TALK_TURN_SECRET" turn nxt.bhatfamily.in:3478 udp,tcp
  1. Configure Client Push (notify_push):
docker exec --user www-data nextcloud-app php occ app:install notify_push
docker exec --user www-data nextcloud-app php occ notify_push:setup https://nxt.bhatfamily.in:8446/push
docker exec --user www-data nextcloud-app php occ notify_push:self-test

If notify_push is already installed, skip app:install and run notify_push:setup + self-test.

  1. Verify endpoints and setup checks:
curl -k https://nxt.bhatfamily.in:8446/standalone-signaling/api/v1/welcome
docker exec --user www-data nextcloud-app php occ setupchecks

One-time setup/security hardening commands

These commands were used to clear remaining setup/security notices in this deployment:

docker exec --user www-data nextcloud-app php occ twofactorauth:enforce --on
docker exec --user www-data nextcloud-app php occ config:system:set default_phone_region --value=IN
docker exec --user www-data nextcloud-app php occ config:system:set serverid --type=integer --value=1
docker exec --user www-data nextcloud-app php occ config:system:set mail_smtpmode --value=null

Adjust default_phone_region to your country code as needed.

Move Nextcloud data directory to external storage

Use the migration helper to move existing data to a host path and switch the app to a bind mount.

Default target:

  • /media/rbhat/DATA/nextcloud/NextCloudData

Run migration:

./scripts/migrate-data-directory.sh /media/rbhat/DATA/nextcloud/NextCloudData

What the script does:

  • enables maintenance mode
  • copies current /var/www/html/data content to target directory
  • applies owner/group and permissions for Nextcloud (www-data)
  • updates docker-compose.yml app volume with ...:/var/www/html/data
  • recreates app and web services
  • disables maintenance mode and verifies mount

Rollback (if needed):

  1. Remove the :/var/www/html/data bind mount line from app volumes in docker-compose.yml.
  2. docker compose up -d app web
  3. Confirm status:
docker exec --user www-data nextcloud-app php occ status

Production TLS (Let's Encrypt + Cloudflare DNS-01)

  1. Export credentials in shell:
export CF_DNS_API_TOKEN={{CF_DNS_API_TOKEN}}
export LETSENCRYPT_EMAIL={{LETSENCRYPT_EMAIL}}
  1. Issue/renew and install production cert:
./scripts/provision-production-tls.sh
  1. Reload Nginx container:
docker compose restart web
  1. Verify cert:
echo | openssl s_client -connect nxt.bhatfamily.in:8446 -servername nxt.bhatfamily.in 2>/dev/null | openssl x509 -noout -subject -issuer -dates

Automated renewal job (cron)

  1. Ensure your Cloudflare token export script exists (default path used by renewal wrapper):
  • ~/bin/cloudflare-api-usertoken.sh
  1. Install/update renewal cron entry:
./scripts/setup-renewal-cron.sh

This script will:

  • create/update .tls-renewal.env (local only, not committed)
  • install a daily cron job (03:17 by default)
  • write logs to logs/tls-renew.log
  1. Manual renewal run (same path cron uses):
./scripts/renew-production-tls.sh

Admin password reset

List existing users:

docker exec --user www-data nextcloud-app php occ user:list

Reset password using helper script (interactive prompt):

./scripts/reset-admin-password.sh admin

Reset password non-interactively (for automation):

NEW_NEXTCLOUD_PASSWORD={{NEW_NEXTCLOUD_PASSWORD}} ./scripts/reset-admin-password.sh admin

You can target a different username by passing it as the first argument.

Useful commands

Start/update containers:

docker compose up -d

Restart all services:

docker compose restart

Restart web only:

docker compose restart web

Stop and remove containers/volumes:

./scripts/uninstall.sh

Security notes

  • .env, .tls-renewal.env, .hub-update.env, and runtime cert material under nginx/ssl are intentionally ignored by Git.
  • backups/ is ignored and used for local database/log snapshots before risky changes.
  • Keep .env mode restricted (chmod 600 .env).
  • If secrets were ever committed earlier, rotate them.