# 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 - 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 - New scripted Nextcloud Hub upgrade workflow: `scripts/update-nextcloud-hub.sh` ## 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: ```bash cp .env.example .env ``` 2. Edit `.env` with strong values. 3. Start stack with bootstrap TLS: ```bash ./scripts/install.sh ``` 4. Validate: ```bash ./scripts/test.sh ``` ## Update Nextcloud Hub (scripted) Use the upgrade helper script to pull new images, apply the upgrade, run post-upgrade repairs, and validate endpoints. Run update: ```bash ./scripts/update-nextcloud-hub.sh ``` Optional flags: - Skip app marketplace updates: ```bash RUN_APP_UPDATES=0 ./scripts/update-nextcloud-hub.sh ``` - Require strict TLS validation during smoke tests (no `-k`): ```bash STRICT_TLS=1 ./scripts/update-nextcloud-hub.sh ``` What the script does: - pulls latest `db`, `app`, and `web` images - recreates services via Compose - enables maintenance mode and runs `occ upgrade` - runs `occ app:update --all` (unless disabled) - runs schema and repair commands (`db:add-missing-*`, `maintenance:repair`) - disables maintenance mode - runs `occ status`, `occ setupchecks`, and `scripts/test.sh` > Recommendation: take a filesystem/database backup before major Hub upgrades. ## 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: ```bash ./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: ```bash docker exec --user www-data nextcloud-app php occ status ``` ## Production TLS (Let's Encrypt + Cloudflare DNS-01) 1. Export credentials in shell: ```bash export CF_DNS_API_TOKEN={{CF_DNS_API_TOKEN}} export LETSENCRYPT_EMAIL={{LETSENCRYPT_EMAIL}} ``` 2. Issue/renew and install production cert: ```bash ./scripts/provision-production-tls.sh ``` 3. Reload Nginx container: ```bash docker compose restart web ``` 4. Verify cert: ```bash 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` 2. Install/update renewal cron entry: ```bash ./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` 3. Manual renewal run (same path cron uses): ```bash ./scripts/renew-production-tls.sh ``` ## Admin password reset List existing users: ```bash docker exec --user www-data nextcloud-app php occ user:list ``` Reset password using helper script (interactive prompt): ```bash ./scripts/reset-admin-password.sh admin ``` Reset password non-interactively (for automation): ```bash 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. ## Operational/security changes applied (Apr 2026) Applied and validated in this deployment: - `docker-compose.yml` (`app` service): - startup command now ensures writable `/var/cache/fontconfig` - sets Apache `ServerName nxt.bhatfamily.in` - sets `XDG_CACHE_HOME=/tmp/.cache` - `nginx/nginx.conf`: - `server_tokens off` - HSTS set to `max-age=63072000; includeSubDomains; preload` - `proxy_hide_header X-Powered-By` - `ssl_session_cache`, `ssl_session_timeout`, `ssl_session_tickets off` - Nextcloud `occ` settings: - `trusted_proxies` configured to Docker network subnet - `forwarded_for_headers` set to `HTTP_X_FORWARDED_FOR` - `auth.bruteforce.protection.enabled=true` - `maintenance_window_start=1` - `weather_status` app disabled to remove repeated PHP warning noise - Host security hygiene: - `.env` permission reduced to `600` ## Useful commands Start/update containers: ```bash docker compose up -d ``` Restart all services: ```bash docker compose restart ``` Restart web only: ```bash docker compose restart web ``` Stop and remove containers/volumes: ```bash ./scripts/uninstall.sh ``` ## Security notes - `.env`, `.tls-renewal.env`, and runtime cert material under `nginx/ssl` are intentionally ignored by Git. - Keep `.env` mode restricted (`chmod 600 .env`). - If secrets were ever committed earlier, rotate them.