Files
nextcloud-docker/README.md
2026-04-17 10:33:46 +05:30

242 lines
7.4 KiB
Markdown

# 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` on app startup)
- 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`
## 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
```
## Enable web updater (one-time verification)
Web updater is now enabled by design for this deployment.
Check values:
```bash
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`
If you need to enforce immediately without restart, run:
```bash
./scripts/update-nextcloud-hub.sh
```
This script also rewrites `config/upgrade-disable-web.config.php` inside the app container volume.
## 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
- ensures web updater is enabled and release channel is configured
- ensures maintenance mode is off before `occ upgrade` (fixes prior deadlock)
- runs `occ upgrade`
- runs `occ app:update --all` (unless disabled)
- runs schema and repair commands (`db:add-missing-*`, `maintenance:repair`)
- runs `occ status`, `occ setupchecks`, and `scripts/test.sh`
> Recommendation: take a filesystem/database backup before major Hub upgrades.
## Automate Hub updates (cron)
Install/update a weekly cron job (default: Sunday 02:30):
```bash
./scripts/setup-hub-update-cron.sh
```
Optional custom schedule:
```bash
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 are tracked in `.hub-update.env.example`:
- `RUN_APP_UPDATES=1`
- `STRICT_TLS=0`
- `ENABLE_WEB_UPDATER=1`
- `UPDATER_RELEASE_CHANNEL=stable`
## 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 ensures writable `/var/cache/fontconfig`
- sets Apache `ServerName nxt.bhatfamily.in`
- sets `XDG_CACHE_HOME=/tmp/.cache`
- forces `config/upgrade-disable-web.config.php` to `upgrade.disable-web=false`
- `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`
- `updater.release.channel=stable`
- `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`, `.hub-update.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.