Skip to Content

Nextcloud Docker Setup: Security Hardening, Automated Backups, and Disaster Recovery That Actually Work

Learn how to harden your Nextcloud server against attack, automate encrypted offsite backups, configure audit logging, enforce two-factor authentication, and build a tested disaster recovery process your team can execute under pressure.
Nextcloud setup guide

Nextcloud Docker Setup: Security Hardening, Automated Backups, and Disaster Recovery That Actually Work

Deploying Nextcloud and tuning it for performance covers the 80%. The other 20% — the part that matters when something goes wrong — is security hardening that stops attacks before they succeed, backups that are encrypted, automated, and actually tested, and a disaster recovery process your team can execute in under an hour rather than spending a weekend recovering from a breach or disk failure. This guide covers the security and resilience side of Nextcloud Docker setup that most guides skip entirely.

This guide assumes a running Nextcloud instance with PostgreSQL and Redis. If you're starting fresh, begin with our basic Nextcloud deployment guide. For performance tuning and LDAP integration, see our advanced configuration guide. This guide completes the trilogy with security and resilience.


Prerequisites

  • A running Nextcloud instance with HTTPS — see our deployment guide
  • Admin access to the Nextcloud web interface and shell (occ CLI)
  • Access to an S3-compatible bucket or remote server for offsite backups
  • At least 2GB free disk space on the host for staging backup archives
  • sudo or root access on the host for fail2ban and system-level hardening

Check the current security posture before making changes:

# Run Nextcloud's built-in security scan
docker exec -u www-data nextcloud php occ security:scan

# Check for admin warnings
docker exec -u www-data nextcloud php occ check
docker exec -u www-data nextcloud php occ setupchecks

# External security scan (run from another machine):
# https://scan.nextcloud.com/ — paste your Nextcloud URL
# Aim for an A+ rating before considering this guide complete

Security Hardening: Locking Down the Surface

HTTP Security Headers

Security headers are your first line of defense against XSS, clickjacking, and content injection. Nextcloud's recommended headers are more restrictive than most defaults. Add them to your Nginx server block:

# Add to the server block in /etc/nginx/sites-available/nextcloud
# (or your Nginx configuration file)

server {
    listen 443 ssl http2;
    server_name cloud.yourdomain.com;

    # HSTS — tells browsers to always use HTTPS for 2 years
    # preload: submit to browser preload lists (irreversible — be sure before enabling)
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

    # Prevent MIME type sniffing
    add_header X-Content-Type-Options "nosniff" always;

    # Prevent embedding in iframes on other domains
    add_header X-Frame-Options "SAMEORIGIN" always;

    # XSS protection
    add_header X-XSS-Protection "1; mode=block" always;

    # No referrer for cross-origin requests
    add_header Referrer-Policy "no-referrer" always;

    # Restrict what browser features this page can use
    add_header Permissions-Policy "interest-cohort=()" always;

    # Prevent Adobe Flash and PDF cross-domain access
    add_header X-Permitted-Cross-Domain-Policies "none" always;

    # Content Security Policy for Nextcloud
    # Note: Nextcloud sets its own CSP headers — only add if you've tested it won't break things

    # Hide server version
    server_tokens off;

    # ... rest of your proxy config
}

# Test headers after applying:
curl -I https://cloud.yourdomain.com | grep -E '(Strict|X-Content|X-Frame|Referrer|Permissions)'
# Verify each header appears with the expected value

Hardening Nextcloud Configuration

Several security settings live in config.php and aren't configurable through the UI. Set them via the occ CLI:

# Restrict to HTTPS only (HTTP requests return an error)
docker exec -u www-data nextcloud php occ config:system:set \
  overwriteprotocol --value="https"

# Disable update notifications for non-admin users
docker exec -u www-data nextcloud php occ config:system:set \
  updatechecker --value=false --type=boolean

# Disable sharing to public links by default (admins can re-enable per group)
docker exec -u www-data nextcloud php occ config:system:set \
  shareapi_allow_links --value="no"

# Set password policy for public share links
docker exec -u www-data nextcloud php occ config:system:set \
  shareapi_enforce_links_password --value="yes"

# Lock accounts after failed login attempts
docker exec -u www-data nextcloud php occ config:system:set \
  auth.bruteforce.protection.enabled --value=true --type=boolean

# Set session lifetime to 12 hours (default is no expiry)
docker exec -u www-data nextcloud php occ config:system:set \
  session_lifetime --value=43200 --type=integer

# Disable user enumeration (prevent attackers from discovering usernames)
docker exec -u www-data nextcloud php occ config:system:set \
  hide_login_address --value=true --type=boolean

# Force HTTPS for all generated URLs
docker exec -u www-data nextcloud php occ config:system:set \
  forcessl --value=true --type=boolean

# Verify changes
docker exec -u www-data nextcloud php occ config:system:get auth.bruteforce.protection.enabled

Enforcing Two-Factor Authentication

Nextcloud's built-in brute force protection is valuable, but 2FA is the real defense against credential theft. Configure it as mandatory for all users via organization-level policy:

# Install TOTP 2FA app
docker exec -u www-data nextcloud php occ app:install twofactor_totp
docker exec -u www-data nextcloud php occ app:enable twofactor_totp

# Install backup codes app (recovery if TOTP device is lost)
docker exec -u www-data nextcloud php occ app:install twofactor_backupcodes
docker exec -u www-data nextcloud php occ app:enable twofactor_backupcodes

# ENFORCE 2FA for all users (they must set it up on next login)
docker exec -u www-data nextcloud php occ twofactorauth:enforce --on

# Or enforce only for specific groups:
docker exec -u www-data nextcloud php occ twofactorauth:enforce --on --group="Engineering"
docker exec -u www-data nextcloud php occ twofactorauth:enforce --on --group="Management"

# Check 2FA enforcement status:
docker exec -u www-data nextcloud php occ twofactorauth:enforce --status

# Generate backup codes for a user who's locked out:
docker exec -u www-data nextcloud php occ twofactorauth:cleanup username
docker exec -u www-data nextcloud php occ twofactor_backupcodes:generate username

Fail2Ban for Login Rate Limiting

Nextcloud has built-in brute force protection, but fail2ban provides IP-level blocking at the network layer — more effective against distributed attacks. Install it on the host and configure a jail for Nextcloud:

sudo apt install fail2ban -y

# Create Nextcloud filter
sudo tee /etc/fail2ban/filter.d/nextcloud.conf << 'EOF'
[Definition]
_groupsre = (?:(?:,\s*)?\"(?:\w+)\":\s*\"[^\"]*\")*
failregex = ^\{%(_groupsre)s(?:,\s*)?\"remoteAddr\":\"\"(?:,\s*)?%(_groupsre)s(?:,\s*)?\"message\":\"Login failed:
ignoreregex =
EOF

# Create Nextcloud jail
sudo tee /etc/fail2ban/jail.d/nextcloud.conf << 'EOF'
[nextcloud]
enabled  = true
port     = 80,443
protocol = tcp
filter   = nextcloud
maxretry = 10
bantime  = 3600
findtime = 600
logpath  = /var/log/nextcloud.log
EOF

# Enable Nextcloud logging to the host filesystem
# In Nextcloud config.php or via occ:
docker exec -u www-data nextcloud php occ config:system:set log_type --value="file"
docker exec -u www-data nextcloud php occ config:system:set logfile --value="/var/www/html/data/nextcloud.log"
docker exec -u www-data nextcloud php occ config:system:set loglevel --value=2

# Mount the log file from the container to the host:
# Add to docker-compose.yml volumes for the nextcloud service:
# - ./logs/nextcloud.log:/var/www/html/data/nextcloud.log
mkdir -p ./logs && touch ./logs/nextcloud.log

# Update jail logpath to match your mount:
# logpath = /path/to/nextcloud/logs/nextcloud.log

sudo systemctl enable fail2ban
sudo systemctl restart fail2ban
sudo fail2ban-client status nextcloud

Automated Encrypted Backups

What Needs to Be Backed Up

A Nextcloud backup has three independent components — all three are required for a complete restore:

  1. Database (PostgreSQL) — user accounts, file metadata, sharing info, app configurations, calendar/contacts data
  2. Data directory — the actual file contents (./data in your Compose setup)
  3. Config directoryconfig.php containing your instance secret, encryption keys, and all system settings

Losing the database but having files means you can't authenticate or find files. Having the database but losing files means empty directories. Losing config.php means you can't decrypt anything. Back up all three.

Complete Backup Script

#!/bin/bash
# /opt/scripts/backup-nextcloud.sh
# Full Nextcloud backup: database + data + config
# Encrypted with GPG and uploaded to S3

set -euo pipefail

# Configuration
BACKUP_BASE="/opt/backups/nextcloud"
DATE=$(date +%Y-%m-%d-%H%M)
S3_BUCKET="s3://your-backup-bucket/nextcloud"
GPG_KEY_ID="[email protected]"  # Your GPG key fingerprint or email
NEXTCLOUD_CONTAINER="nextcloud"
DB_CONTAINER="nextcloud_db"
RETENTION_DAYS=30

mkdir -p "${BACKUP_BASE}/${DATE}"
echo "[$(date)] Starting Nextcloud backup: ${DATE}"

# Step 1: Enable maintenance mode to ensure consistent backup
echo "[$(date)] Enabling maintenance mode..."
docker exec -u www-data "${NEXTCLOUD_CONTAINER}" php occ maintenance:mode --on

# Trap to ensure maintenance mode is disabled even if backup fails
trap 'docker exec -u www-data ${NEXTCLOUD_CONTAINER} php occ maintenance:mode --off; echo "Maintenance mode disabled (trap)"' EXIT

# Step 2: Database backup (atomic)
echo "[$(date)] Backing up PostgreSQL..."
docker exec "${DB_CONTAINER}" pg_dump \
  -U nextcloud -d nextcloud \
  --format=custom \
  --compress=9 \
  > "${BACKUP_BASE}/${DATE}/database.pgdump"

# Step 3: Config backup (small — always include)
echo "[$(date)] Backing up config..."
docker cp "${NEXTCLOUD_CONTAINER}:/var/www/html/config/config.php" \
  "${BACKUP_BASE}/${DATE}/config.php"

# Step 4: Data directory backup (rsync for speed on incremental runs)
# Or tar for portability
echo "[$(date)] Backing up data directory..."
tar czf "${BACKUP_BASE}/${DATE}/data.tar.gz" \
  -C /path/to/nextcloud ./data \
  --exclude='./data/appdata_*/preview' \
  --exclude='./data/*/thumbnails'
# Excluding previews/thumbnails saves significant space — they're regeneratable

# Step 5: Create single archive of all backup components
echo "[$(date)] Creating combined archive..."
tar czf "${BACKUP_BASE}/nextcloud-${DATE}.tar.gz" \
  -C "${BACKUP_BASE}/${DATE}" .

# Step 6: Encrypt before upload
echo "[$(date)] Encrypting backup..."
gpg --recipient "${GPG_KEY_ID}" \
    --trust-model always \
    --batch \
    --yes \
    --output "${BACKUP_BASE}/nextcloud-${DATE}.tar.gz.gpg" \
    --encrypt "${BACKUP_BASE}/nextcloud-${DATE}.tar.gz"

# Step 7: Upload to S3
echo "[$(date)] Uploading to S3..."
aws s3 cp \
  "${BACKUP_BASE}/nextcloud-${DATE}.tar.gz.gpg" \
  "${S3_BUCKET}/nextcloud-${DATE}.tar.gz.gpg" \
  --storage-class STANDARD_IA

# Step 8: Clean up
rm -rf "${BACKUP_BASE}/${DATE}"
rm -f "${BACKUP_BASE}/nextcloud-${DATE}.tar.gz"
rm -f "${BACKUP_BASE}/nextcloud-${DATE}.tar.gz.gpg"

# Step 9: Remove old backups from S3
aws s3 ls "${S3_BUCKET}/" | awk '{print $4}' | while read obj; do
  DATE_STR=$(echo "$obj" | grep -oE '[0-9]{4}-[0-9]{2}-[0-9]{2}')
  if [ -n "$DATE_STR" ]; then
    AGE=$(( ($(date +%s) - $(date -d "$DATE_STR" +%s)) / 86400 ))
    if [ "$AGE" -gt "$RETENTION_DAYS" ]; then
      aws s3 rm "${S3_BUCKET}/${obj}"
      echo "Deleted old backup: ${obj}"
    fi
  fi
done

# Maintenance mode is disabled by the trap on EXIT
echo "[$(date)] Backup complete: nextcloud-${DATE}.tar.gz.gpg"
# Make executable and schedule
chmod +x /opt/scripts/backup-nextcloud.sh

# Add to crontab: crontab -e
# Run daily at 2am, log output
0 2 * * * /opt/scripts/backup-nextcloud.sh >> /var/log/nextcloud-backup.log 2>&1

# Verify last backup succeeded:
tail -5 /var/log/nextcloud-backup.log
# Look for: Backup complete: nextcloud-YYYY-MM-DD-HHMM.tar.gz.gpg

# Verify backup is in S3:
aws s3 ls s3://your-backup-bucket/nextcloud/ | tail -5

Incremental Backups with Restic

For large data directories where a full tar backup is too slow or expensive, Restic provides efficient incremental backups with deduplication and encryption built in:

# Install restic
sudo apt install restic -y

# Initialize a Restic repository in S3
export AWS_ACCESS_KEY_ID=your-key
export AWS_SECRET_ACCESS_KEY=your-secret
export RESTIC_PASSWORD=a-very-strong-repository-password

restic -r s3:s3.amazonaws.com/your-backup-bucket/nextcloud-restic init

# Run incremental backup (first run is full, subsequent are incremental)
# Put Nextcloud in maintenance mode first
docker exec -u www-data nextcloud php occ maintenance:mode --on

restic -r s3:s3.amazonaws.com/your-backup-bucket/nextcloud-restic backup \
  /path/to/nextcloud/data \
  --tag nextcloud-data \
  --exclude='*/thumbnails' \
  --exclude='*/preview'

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

# Prune old snapshots (keep 7 daily, 4 weekly, 12 monthly)
restic -r s3:s3.amazonaws.com/your-backup-bucket/nextcloud-restic forget \
  --prune \
  --keep-daily 7 \
  --keep-weekly 4 \
  --keep-monthly 12

# Verify repository integrity weekly:
restic -r s3:s3.amazonaws.com/your-backup-bucket/nextcloud-restic check

Audit Logging and Security Monitoring

Enabling Audit Logging

Nextcloud's Audit App logs every user action — file access, sharing, login, administrative changes. For regulated environments or teams handling sensitive data, this is non-optional:

# Install and enable the Audit app
docker exec -u www-data nextcloud php occ app:install admin_audit
docker exec -u www-data nextcloud php occ app:enable admin_audit

# Configure audit log location
docker exec -u www-data nextcloud php occ config:app:set \
  admin_audit logfile --value="/var/www/html/data/audit.log"

# The audit log records:
# - User logins and logouts (with IP addresses)
# - File operations (create, read, update, delete, share, unshare)
# - Admin actions (user creation, deletion, permission changes)
# - Public link access

# Monitor for suspicious patterns:
# Watch for unusual access volumes from a single user:
tail -f /path/to/nextcloud/data/audit.log | grep -E '"event":"(fileRead|fileAccessed)"' | \
  awk -F'"user":"' '{print $2}' | awk -F'"' '{print $1}' | \
  sort | uniq -c | sort -rn | head -20

# Watch for failed logins:
tail -100 /path/to/nextcloud/data/nextcloud.log | jq 'select(.message | contains("Login failed"))'
# Shows: {"level":2, "time":"...", "remoteAddr":"...", "message":"Login failed: ..."}

File Access Notifications

For sensitive directories, configure Nextcloud to notify file owners when their files are accessed by others or shared externally:

# Enable the Activity app for per-user notifications
docker exec -u www-data nextcloud php occ app:enable activity

# Configure default notification settings for all users:
docker exec -u www-data nextcloud php occ config:app:set activity \
  notify_email_shared_file_by_link --value=1
docker exec -u www-data nextcloud php occ config:app:set activity \
  notify_email_shared_with_by_user --value=1

# Admin: set SMTP for notifications (if not already set)
docker exec -u www-data nextcloud php occ config:system:set mail_smtphost --value="smtp.yourdomain.com"
docker exec -u www-data nextcloud php occ config:system:set mail_smtpport --value=587
docker exec -u www-data nextcloud php occ config:system:set mail_from_address --value="nextcloud"
docker exec -u www-data nextcloud php occ config:system:set mail_domain --value="yourdomain.com"
docker exec -u www-data nextcloud php occ config:system:set mail_smtpauth --value=1 --type=boolean
docker exec -u www-data nextcloud php occ config:system:set mail_smtpname --value="[email protected]"
docker exec -u www-data nextcloud php occ config:system:set mail_smtppassword --value="your-smtp-password"

# Test email delivery:
docker exec -u www-data nextcloud php occ mail:test [email protected]

Disaster Recovery: Tested Restore Procedures

The difference between a backup and a recovery capability is testing. A backup you've never restored from is an untested hypothesis. These procedures should be run on a test server at least quarterly.

Full Restore Procedure

#!/bin/bash
# restore-nextcloud.sh
# Restore Nextcloud from an encrypted backup archive
# Usage: ./restore-nextcloud.sh nextcloud-2026-04-07-0200.tar.gz.gpg

set -euo pipefail

BACKUP_FILE="${1:-}"
if [ -z "$BACKUP_FILE" ]; then
  echo "Usage: $0 "
  exit 1
fi

RESTORE_DIR="/tmp/nextcloud-restore-$(date +%s)"
mkdir -p "$RESTORE_DIR"

echo "[1/7] Downloading backup from S3..."
aws s3 cp "s3://your-backup-bucket/nextcloud/${BACKUP_FILE}" \
  "${RESTORE_DIR}/${BACKUP_FILE}"

echo "[2/7] Decrypting backup..."
gpg --batch --yes \
    --output "${RESTORE_DIR}/backup.tar.gz" \
    --decrypt "${RESTORE_DIR}/${BACKUP_FILE}"

echo "[3/7] Extracting backup..."
tar xzf "${RESTORE_DIR}/backup.tar.gz" -C "${RESTORE_DIR}/"

echo "[4/7] Enabling maintenance mode..."
docker exec -u www-data nextcloud php occ maintenance:mode --on

echo "[5/7] Restoring PostgreSQL database..."
# Drop and recreate the database
docker exec nextcloud_db psql -U postgres \
  -c "DROP DATABASE IF EXISTS nextcloud;"
docker exec nextcloud_db psql -U postgres \
  -c "CREATE DATABASE nextcloud OWNER nextcloud;"

# Restore from pg_dump custom format
cat "${RESTORE_DIR}/database.pgdump" | \
  docker exec -i nextcloud_db pg_restore \
  -U nextcloud -d nextcloud --no-owner --no-privileges

echo "[6/7] Restoring config.php..."
docker cp "${RESTORE_DIR}/config.php" \
  nextcloud:/var/www/html/config/config.php
docker exec nextcloud chown www-data:www-data /var/www/html/config/config.php

echo "[7/7] Restoring data directory..."
# Clear existing data first
rm -rf /path/to/nextcloud/data/*
tar xzf "${RESTORE_DIR}/data.tar.gz" -C /path/to/nextcloud/

# Fix permissions
docker exec nextcloud chown -R www-data:www-data /var/www/html/data

# Re-scan files to rebuild filecache
docker exec -u www-data nextcloud php occ files:scan --all

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

# Clean up
rm -rf "$RESTORE_DIR"

echo "Restore complete. Verify at https://cloud.yourdomain.com"
echo "Run: docker exec -u www-data nextcloud php occ status"

Partial Recovery: Database Only

If only the database is corrupted but the data directory is intact, you can restore just the database without touching files — faster and less disruptive:

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

# Restore database only
docker exec nextcloud_db psql -U postgres -c "DROP DATABASE IF EXISTS nextcloud;"
docker exec nextcloud_db psql -U postgres -c "CREATE DATABASE nextcloud OWNER nextcloud;"

cat /path/to/database.pgdump | \
  docker exec -i nextcloud_db pg_restore \
  -U nextcloud -d nextcloud --no-owner --no-privileges

# Re-run migrations in case the restored DB version differs from the app version
docker exec -u www-data nextcloud php occ upgrade
docker exec -u www-data nextcloud php occ db:add-missing-indices

# Rebuild file cache (database knows about files; filecache may be stale)
docker exec -u www-data nextcloud php occ files:scan --all -q

docker exec -u www-data nextcloud php occ maintenance:mode --off
echo "Database restore complete"

Tips, Gotchas, and Troubleshooting

Backup Script Fails with Maintenance Mode Left On

# The backup script uses a trap to disable maintenance mode on exit
# If the trap doesn't fire (e.g., server restart during backup),
# users will see "Nextcloud is in maintenance mode"

# Check maintenance mode status:
docker exec -u www-data nextcloud php occ status | grep maintenance

# Disable manually:
docker exec -u www-data nextcloud php occ maintenance:mode --off

# Verify it's off:
curl -I https://cloud.yourdomain.com
# Should return 200, not the maintenance page

Brute Force Protection Locking Out Legitimate Users

# Check if an IP is throttled:
docker exec -u www-data nextcloud php occ security:bruteforce:attempts YOUR_IP_ADDRESS

# Reset brute force protection for a specific IP:
docker exec -u www-data nextcloud php occ security:bruteforce:reset YOUR_IP_ADDRESS

# Whitelist trusted IPs (your office static IP, VPN exit nodes):
docker exec -u www-data nextcloud php occ config:system:set \
  trusted_proxies 0 --value="10.0.0.0/8"
docker exec -u www-data nextcloud php occ config:system:set \
  auth.bruteforce.protection.whitelist 0 --value="YOUR_OFFICE_IP"

GPG Backup Encryption: Key Management

# Generate a dedicated backup GPG key:
gpg --batch --gen-key << 'EOF'
%no-protection
Key-Type: RSA
Key-Length: 4096
Name-Real: Nextcloud Backup
Name-Email: [email protected]
Expire-Date: 0
%commit
EOF

# Export and store securely (offline, encrypted storage, password manager)
gpg --armor --export [email protected] > nextcloud-backup-pubkey.asc
gpg --armor --export-secret-keys [email protected] > nextcloud-backup-privkey.asc

# CRITICAL: Store the private key somewhere separate from the server
# If you lose the private key, you cannot decrypt your backups
# Good options: password manager (Vaultwarden), hardware security key, encrypted USB in a safe

# Import the key on a restore machine:
gpg --import nextcloud-backup-privkey.asc

Pro Tips

  • Test your restore quarterly — on a real test server — not just "checking that backup files exist." Run the full restore procedure, log in as a user, verify files are accessible, and time how long it takes. Document the time so you can give accurate RTO estimates during an incident.
  • Store config.php separately from other backups — it contains your instance secret, which is used to encrypt certain data. Keep a copy of it in your password manager (Vaultwarden) as well as in automated backups, so you can recover even if S3 is unavailable.
  • Set Nextcloud to log to a named Docker volume, not a bind mount — log file paths can change when containers are rebuilt. A named volume for logs survives container recreation and makes fail2ban's log reading reliable.
  • Patch promptly — Nextcloud releases security advisories regularly — subscribe to the Nextcloud security advisories RSS feed and update within a week of any critical patch. The occ upgrade path is reliable; the risk of delaying patches is not.
  • Use Nextcloud's built-in security scan before and after changesscan.nextcloud.com checks your public-facing instance for common misconfigurations, missing security headers, and known vulnerable versions. Run it after every significant configuration change.

Wrapping Up

Security and backup are where self-hosted infrastructure either earns or loses trust. A Nextcloud instance without hardened security headers, enforced 2FA, fail2ban protection, and tested encrypted backups is a liability waiting to materialize. The configuration in this guide takes an afternoon to implement and provides a security posture that most SaaS cloud storage vendors don't offer — because you control every layer.

Together, the three guides in this series cover the full lifecycle: deployment and initial setup, performance tuning, external storage, and LDAP, and this guide's security hardening and disaster recovery. Implement all three and you have a Nextcloud deployment that's genuinely production-ready — not just running, but resilient.


Need Nextcloud Secured and Backed Up Properly for Your Organization?

Implementing security hardening, encrypted backup automation, audit logging, and a tested disaster recovery process for a production Nextcloud deployment takes experience and time your team may not have. The sysbrix team handles Nextcloud security audits, backup infrastructure, and ongoing maintenance for organizations that need their self-hosted storage to be as reliable as the cloud services they left behind.

Talk to Us →
Nextcloud Docker Setup: Performance Tuning, External Storage, LDAP, and Scaling for Teams
Go beyond basic deployment — learn how to optimize Nextcloud with PHP-FPM and Redis caching, mount external S3 storage, configure LDAP user sync, and run Nextcloud Office for a complete self-hosted collaboration platform.