Nextcloud is an open-source content collaboration platform providing on-premises file storage, collaborative editing, calendaring, contacts, and communication.
Installation Overview
Manual LAMP
apt update && apt install -y apache2 mariadb-server php php-{cli,curl,gd,imagick,intl,bcmath,json,mbstring,mysql,xml,zip,apcu,redis,smbclient,ldap}
wget https://download.nextcloud.com/server/releases/latest.tar.bz2
tar -xjf latest.tar.bz2 -C /var/www/ && chown -R www-data:www-data /var/www/nextcloud/
mysql -u root -e "CREATE DATABASE nextcloud CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; GRANT ALL ON nextcloud.* TO 'ncuser'@'localhost' IDENTIFIED BY 'strongpass'; FLUSH PRIVILEGES;"
sudo -u www-data php occ maintenance:install --database=mysql --database-name=nextcloud --database-host=localhost --database-user=ncuser --database-pass=strongpass --admin-user=admin --admin-pass=adminpass --data-dir=/var/ncdataSnap
snap install nextcloud
nextcloud.occ config:system:set trusted_domains 1 --value=example.com
nextcloud.enable-https lets-encryptDocker Image
docker pull nextcloud:apache # Apache-based (self-contained)
docker pull nextcloud:fpm # FPM (needs nginx reverse proxy)
docker pull nextcloud:stable # Latest stable tagDocker Deployment
Docker Compose (PostgreSQL + Redis)
version: '3.8'
services:
db:
image: postgres:16-alpine
restart: unless-stopped
volumes: [db:/var/lib/postgresql/data]
environment:
POSTGRES_DB: nextcloud
POSTGRES_USER: nextcloud
POSTGRES_PASSWORD: db_pass
networks: [nc-net]
redis:
image: redis:7-alpine
restart: unless-stopped
command: redis-server --requirepass redis_pass
networks: [nc-net]
app:
image: nextcloud:apache
restart: unless-stopped
ports: ["8080:80"]
depends_on: [db, redis]
volumes:
- nextcloud_data:/var/www/html
- nextcloud_config:/var/www/html/config
- nextcloud_data_dir:/var/ncdata
environment:
POSTGRES_HOST: db
POSTGRES_DB: nextcloud
POSTGRES_USER: nextcloud
POSTGRES_PASSWORD: db_pass
REDIS_HOST: redis
REDIS_HOST_PASSWORD: redis_pass
NEXTCLOUD_ADMIN_USER: admin
NEXTCLOUD_ADMIN_PASSWORD: admin_pass
NEXTCLOUD_TRUSTED_DOMAINS: nc.example.com
OVERWRITEPROTOCOL: https
OVERWRITECLIURL: https://nc.example.com
PHP_MEMORY_LIMIT: 512M
PHP_UPLOAD_LIMIT: 10G
networks: [nc-net]
volumes: {db:, nextcloud_data:, nextcloud_config:, nextcloud_data_dir:}
networks: {nc-net:}Docker Compose (with Redis + Elasticsearch Fulltextsearch)
version: '3.8'
services:
db: {image: postgres:16-alpine, volumes: [db:/var/lib/postgresql/data], environment: {POSTGRES_DB: nextcloud, POSTGRES_USER: nextcloud, POSTGRES_PASSWORD: db_pass}, networks: [nc-net]}
redis: {image: redis:7-alpine, command: redis-server --requirepass redis_pass, networks: [nc-net]}
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.11.0
environment: [discovery.type=single-node, ES_JAVA_OPTS=-Xms2g -Xmx2g, xpack.security.enabled=false]
volumes: [es_data:/usr/share/elasticsearch/data]
networks: [nc-net]
app:
image: nextcloud:apache
ports: ["8080:80"]
depends_on: [db, redis, elasticsearch]
volumes: [nextcloud_data:/var/www/html, nextcloud_config:/var/www/html/config, nextcloud_data_dir:/var/ncdata]
environment:
POSTGRES_HOST: db
POSTGRES_DB: nextcloud
POSTGRES_USER: nextcloud
POSTGRES_PASSWORD: db_pass
REDIS_HOST: redis
REDIS_HOST_PASSWORD: redis_pass
NEXTCLOUD_ADMIN_USER: admin
NEXTCLOUD_ADMIN_PASSWORD: admin_pass
NEXTCLOUD_TRUSTED_DOMAINS: nc.example.com
OVERWRITEPROTOCOL: https
OVERWRITECLIURL: https://nc.example.com
networks: [nc-net]
volumes: {db:, es_data:, nextcloud_data:, nextcloud_config:, nextcloud_data_dir:}
networks: {nc-net:}Environment Variables Reference
# Admin (first-run only)
NEXTCLOUD_ADMIN_USER=admin NEXTCLOUD_ADMIN_PASSWORD=strongpass
# PostgreSQL
POSTGRES_HOST=db POSTGRES_DB=nextcloud POSTGRES_USER=nextcloud POSTGRES_PASSWORD=***
# MySQL/MariaDB (use instead of POSTGRES_*)
MYSQL_HOST=db MYSQL_DATABASE=nextcloud MYSQL_USER=nextcloud MYSQL_PASSWORD=***
# Redis
REDIS_HOST=redis REDIS_HOST_PORT=6379 REDIS_HOST_PASSWORD=***
# URLs & protocol
NEXTCLOUD_TRUSTED_DOMAINS=nc.example.com
OVERWRITEPROTOCOL=https OVERWRITECLIURL=https://nc.example.com OVERWRITEHOST=nc.example.com
# PHP
PHP_MEMORY_LIMIT=512M PHP_UPLOAD_LIMIT=10G PHP_MAX_FILE_UPLOADS=20
# Mail (optional)
SMTP_HOST=smtp.example.com SMTP_PORT=587 SMTP_SECURE=tls SMTP_NAME=[email protected] SMTP_PASSWORD=***
# Object store (optional)
OBJECTSTORE_S3_BUCKET=nc-data OBJECTSTORE_S3_KEY=access_key OBJECTSTORE_S3_SECRET=***Container Management
docker compose up -d # Start all
docker compose down # Stop all
docker compose exec -u www-data app php occ status
docker compose exec -u www-data app php occ app:list
docker compose exec -u www-data app bash # Interactive shell
docker compose logs -f app # Follow logs
docker compose logs --tail 100 app # Last 100 lines
docker stats nc-app nc-db # Resource usageDocker Update Procedure
docker compose pull app
docker compose down && docker compose up -d
docker compose exec -u www-data app php occ upgrade
docker compose exec -u www-data app php occ maintenance:mode --off
docker compose exec -u www-data app php occ db:add-missing-indices
docker compose exec -u www-data app php occ db:add-missing-columns
docker compose exec -u www-data app php occ maintenance:repairDocker Backup Strategy
# Database dump
docker compose exec db pg_dump -U nextcloud nextcloud > nc-db-$(date +%F).sql
# Volumes backup
docker run --rm -v nc_nextcloud_data:/data -v nc_nextcloud_config:/config -v $(pwd)/backup:/backup alpine tar czf /backup/nc-$(date +%F).tar.gz /data /config
# With restic
docker compose exec -u www-data app php occ maintenance:mode --on
restic -r s3:https://s3.example.com/bucket backup /var/lib/docker/volumes/nc_nextcloud_data/_data /var/lib/docker/volumes/nc_nextcloud_config/_data /var/lib/docker/volumes/nc_db/_data
docker compose exec -u www-data app php occ maintenance:mode --off
restic -r s3:https://s3.example.com/bucket forget --keep-daily 7 --keep-weekly 4 --keep-monthly 3 && restic pruneocc CLI Commands
General
sudo -u www-data php occ list # All commands
sudo -u www-data php occ status # Server status & version
sudo -u www-data php occ check # Environment check
sudo -u www-data php occ config:system:get version
sudo -u www-data php occ config:system:get instanceidConfiguration
# Get/Set system config
sudo -u www-data php occ config:system:get trusted_domains
sudo -u www-data php occ config:system:set trusted_domains 1 --value=nc.example.com
sudo -u www-data php occ config:system:set overwrite.cli.url --value=https://nc.example.com
sudo -u www-data php occ config:system:set default_phone_region --value=DE
sudo -u www-data php occ config:system:delete trusted_domains 1
# App config
sudo -u www-data php occ config:app:get themename
sudo -u www-data php occ config:app:set themename --value=MyTheme
sudo -u www-data php occ config:list # All config valuesUser Management
sudo -u www-data php occ user:add --display-name="John Doe" [email protected] --group=users johndoe
sudo -u www-data php occ user:list # List all
sudo -u www-data php occ user:list --limit=10 --group=admin # Filtered
sudo -u www-data php occ user:info johndoe # User details
sudo -u www-data php occ user:setting johndoe settings email [email protected]
sudo -u www-data php occ user:enable/disable johndoe # Toggle
sudo -u www-data php occ user:resetpassword johndoe
sudo -u www-data php occ user:delete johndoe
sudo -u www-data php occ user:report # User count & storageGroup Management
sudo -u www-data php occ group:add mygroup
sudo -u www-data php occ group:list
sudo -u www-data php occ group:adduser mygroup johndoe
sudo -u www-data php occ group:removeuser mygroup johndoe
sudo -u www-data php occ group:delete mygroupApp Management
sudo -u www-data php occ app:list # All apps
sudo -u www-data php occ app:list --enabled # Enabled only
sudo -u www-data php occ app:enable calendar # Enable app
sudo -u www-data php occ app:disable survey_client # Disable app
sudo -u www-data php occ app:install twofactor_totp # From marketplace
sudo -u www-data php occ app:remove broken-app # Remove app
sudo -u www-data php occ app:update --all # Update all apps
sudo -u www-data php occ app:update calendar # Specific app
sudo -u www-data php occ app:info calendar # App details
sudo -u www-data php occ app:getpath calendar # Install pathFile Operations
sudo -u www-data php occ files:scan --all # Scan all users
sudo -u www-data php occ files:scan johndoe # Single user
sudo -u www-data php occ files:scan --path="/johndoe/files/Documents" # Specific path
sudo -u www-data php occ files:scan --all -q # Quiet mode
sudo -u www-data php occ files:transfer-ownership --path=/Docs --target-node=admin olduser newuser
sudo -u www-data php occ files:transfer-ownership --accept admin olduser newuser
sudo -u www-data php occ trashbin:cleanup --all-users # Purge trash
sudo -u www-data php occ versions:cleanup --all-users # Purge versions
sudo -u www-data php occ files:cleanup # Repair file cacheSecurity
sudo -u www-data php occ encryption:enable/disable/status
sudo -u www-data php occ encryption:encrypt-all
sudo -u www-data php occ security:bruteforce:reset --ip=192.0.2.1
sudo -u www-data php occ security:rate-limiting:reset --ip=192.0.2.1
sudo -u www-data php occ twofactor:enforce johndoe # Require 2FA
sudo -u www-data php occ twofactor:disable johndoe twofactor_totp # Reset 2FA
sudo -u www-data php occ security:information # Security overview
sudo -u www-data php occ security:certificates # Check HTTPS certsDatabase
sudo -u www-data php occ db:add-missing-indices # Add missing indices
sudo -u www-data php occ db:add-missing-columns # Add missing columns
sudo -u www-data php occ db:size # DB size report
sudo -u www-data php occ db:convert-filecache-bigint -n # For >10M files
sudo -u www-data php occ db:convert-type --password=db_pass pgsql postgres@db nextcloud ncuserMaintenance
sudo -u www-data php occ maintenance:mode --on/--off # Toggle maintenance
sudo -u www-data php occ maintenance:repair # Run repair
sudo -u www-data php occ maintenance:repair --include-expensive # Deep repair
sudo -u www-data php occ upgrade # Run upgrade
sudo -u www-data php occ update:check # Check for updates
sudo -u www-data php occ integrity:check-core # Core file integrity
sudo -u www-data php occ integrity:check-app calendar # App file integrityLogging
sudo -u www-data php occ log:manage --level=0 # DEBUG (most verbose)
sudo -u www-data php occ log:manage --level=1 # INFO
sudo -u www-data php occ log:manage --level=3 # ERROR (production)
sudo -u www-data php occ log:manage --file=/var/log/nextcloud.log
sudo -u www-data php occ log:rotate # Rotate log file
sudo -u www-data php occ log:tail --tail 50 # Recent entriesDAV & Sharing
sudo -u www-data php occ dav:sync-system-addressbook
sudo -u www-data php occ dav:remove-orphans
sudo -u www-data php occ dav:list-calendars admin
sudo -u www-data php occ dav:list-addressbooks admin
sudo -u www-data php occ sharing:cleanup-remote-storagesApp-Specific Commands
sudo -u www-data php occ groupfolders:create "Shared Documents"
sudo -u www-data php occ groupfolders:group 1 --add=users --permissions=31
sudo -u www-data php occ groupfolders:quota 1 --value=10GB
sudo -u www-data php occ fulltextsearch:index # Index for search
sudo -u www-data php occ fulltextsearch:reset # Reset index
sudo -u www-data php occ notification:generate johndoe "Title" "Message body"Config.php Important Settings
Basic & URL
<?php
$CONFIG = array(
'trusted_domains' => array(0 => 'localhost', 1 => 'nc.example.com'),
'overwrite.cli.url' => 'https://nc.example.com',
'overwriteprotocol' => 'https',
'overwritehost' => 'nc.example.com',
'datadirectory' => '/var/ncdata',
'maintenance' => false,
'default_language' => 'en',
'default_phone_region' => 'US',
'secret' => 'random-64-char-string',
'instanceid' => 'generated-uuid',
);Database
<?php
$CONFIG = array(
// MySQL/MariaDB
'dbtype' => 'mysql', 'dbname' => 'nextcloud', 'dbhost' => 'localhost:3306',
'dbuser' => 'ncuser', 'dbpassword' => 'db_pass', 'mysql.utf8mb4' => true,
// PostgreSQL
'dbtype' => 'pgsql', 'dbname' => 'nextcloud', 'dbhost' => 'localhost:5432',
'dbuser' => 'ncuser', 'dbpassword' => 'db_pass',
'dbtableprefix' => 'oc_',
);<?php
$CONFIG = array(
'mail_from_address' => 'noreply',
'mail_domain' => 'example.com',
'mail_smtpmode' => 'smtp',
'mail_smtphost' => 'smtp.example.com',
'mail_smtpport' => 587,
'mail_smtpsecure' => 'tls',
'mail_smtpauth' => true,
'mail_smtpname' => '[email protected]',
'mail_smtppassword' => 'smtp_pass',
);Redis Caching
<?php
$CONFIG = array(
'memcache.local' => '\OC\Memcache\APCu',
'memcache.distributed' => '\OC\Memcache\Redis',
'memcache.locking' => '\OC\Memcache\Redis',
'redis' => array('host' => 'localhost', 'port' => 6379, 'password' => 'redis_pass', 'timeout' => 1.5, 'dbindex' => 0),
'filelocking.enabled' => true,
);Object Store (S3/R2) as Primary Storage
<?php
$CONFIG = array(
'objectstore' => array(
'class' => '\\OC\\Files\\ObjectStore\\S3',
'arguments' => array(
'bucket' => 'nc-data', 'key' => 'access_key', 'secret' => 'secret_key',
'region' => 'us-east-1', 'hostname' => 'ams3.digitaloceanspaces.com',
'port' => 443, 'use_ssl' => true, 'use_path_style' => true, 'autocreate' => true,
// For Cloudflare R2, use hostname: <accountid>.r2.cloudflarestorage.com, region: auto
),
),
);Security & Sharing
<?php
$CONFIG = array(
'overwriteprotocol' => 'https',
'strict_login_enforced' => true,
'session_lifetime' => 60 * 60 * 24,
'password_policy.minimum_length' => 12,
'shareapi_allow_links' => true,
'shareapi_default_expire_date' => true,
'shareapi_expire_after_n_days' => 30,
'bruteforce_protection.enabled' => true,
'blacklisted_files' => array('.htaccess', 'Thumbs.db', 'desktop.ini'),
);Storage Management
Local Storage
# Move data directory
sudo -u www-data php occ maintenance:mode --on
sudo mv /var/ncdata /new/path/ncdata
sudo -u www-data php occ config:system:set datadirectory --value=/new/path/ncdata
sudo -u www-data php occ maintenance:mode --off
# Set default quota
sudo -u www-data php occ config:system:set quota.default --value="10 GB"
sudo -u www-data php occ user:setting johndoe files quota --value="50 GB"External Storage (files_external)
sudo -u www-data php occ app:enable files_external
sudo -u www-data php occ files_external:list # List mounts
# Create S3 external mount
sudo -u www-data php occ files_external:create --config bucket=mybucket --config key=access_key --config secret=secret_key --config hostname=s3.amazonaws.com "S3" amazon_s3
# Create SFTP mount
sudo -u www-data php occ files_external:create --config host=sftp.example.com --config user=backup "Remote" sftp
# Apply to users/groups
sudo -u www-data php occ files_external:applicable --add-user=johndoe 1
sudo -u www-data php occ files_external:applicable --add-group=admin 1
# Verify mount
sudo -u www-data php occ files_external:verify 1Performance Tuning
PHP-FPM
; /etc/php/8.3/fpm/pool.d/www.conf
pm = dynamic
pm.max_children = 120
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
pm.max_requests = 1000PHP Opcache
; /etc/php/8.3/cli/conf.d/10-opcache.ini
opcache.enable=1 opcache.memory_consumption=256
opcache.interned_strings_buffer=16 opcache.max_accelerated_files=10000
opcache.revalidate_freq=2 opcache.jit=1255
opcache.jit_buffer_size=256MAPCu
apcu.enabled=1 apcu.shm_size=256M apcu.ttl=7200Nginx Tuning
client_max_body_size 10G;
fastcgi_read_timeout 3600s;
proxy_read_timeout 3600s;
gzip on; gzip_vary on;
gzip_types text/css application/json application/javascript text/xml;
# Static asset caching
location ~* \.(css|js|svg|png|jpg|webp|woff2)$ { expires 7d; add_header Cache-Control "public, immutable"; }Cron
sudo -u www-data php occ background:cron # Switch to system cron
echo "*/5 * * * * /usr/bin/php -f /var/www/nextcloud/cron.php" | crontab -u www-data -
# Verify: sudo -u www-data php occ config:system:get background_modeSecurity Hardening
File Permissions
chown -R www-data:www-data /var/www/nextcloud/
chmod -R 755 /var/www/nextcloud/
chmod 750 /var/www/nextcloud/config/
chmod 750 /var/ncdata/Security Headers
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Content-Type-Options nosniff always;
add_header X-Frame-Options DENY always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer" always;Fail2ban
# /etc/fail2ban/jail.d/nextcloud.conf
[nextcloud]
enabled = true
port = http,https
filter = nextcloud
logpath = /var/ncdata/nextcloud.log
maxretry = 5
bantime = 3600
findtime = 600# Filter: /etc/fail2ban/filter.d/nextcloud.conf
# failregex = ^.*Login failed: '.*' \(Remote IP: '<HOST>'.*$|^.*Trusted domain error.*'<HOST>' .*$Backup & Restore
Full Backup
sudo -u www-data php occ maintenance:mode --on
# DB dump
pg_dump -h localhost -U nextcloud nextcloud > nc-db-$(date +%F).sql
# Data directory
tar czf ncdata-$(date +%F).tar.gz /var/ncdata/
# Config
cp /var/www/nextcloud/config/config.php /backup/
sudo -u www-data php occ maintenance:mode --offFull Restore
# Install same Nextcloud version
cp /backup/config.php /var/www/nextcloud/config/config.php
rsync -aAXv /backup/ncdata/ /var/ncdata/
chown -R www-data:www-data /var/ncdata/ /var/www/nextcloud/
# Drop & recreate DB, then import
mysql -u root -e "DROP DATABASE nextcloud; CREATE DATABASE nextcloud CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;"
mysql -u ncuser -p nextcloud < nc-db-backup.sql
sudo -u www-data php occ maintenance:mode --on
sudo -u www-data php occ upgrade
sudo -u www-data php occ maintenance:repair
sudo -u www-data php occ maintenance:mode --off
sudo -u www-data php occ files:scan --allTroubleshooting
Status Checks
curl -k https://nc.example.com/status.php
# {"installed":true,"maintenance":false,"needsDbUpgrade":false,"version":"28.0.0.12","versionstring":"28.0.0"}
sudo -u www-data php occ statusCommon Issues
# Stuck in maintenance mode
sudo -u www-data php occ maintenance:mode --off
# Or edit config.php directly: sed -i "s/'maintenance' => true/'maintenance' => false/" config.php
# Failed upgrade
sudo -u www-data php occ upgrade --no-interaction
sudo -u www-data php occ maintenance:repair --include-expensive
# Database errors
psql -h localhost -U nextcloud -d nextcloud -c "SELECT 1;" # Test PostgreSQL
mysql -h localhost -u ncuser -p nextcloud -e "SELECT 1;" # Test MySQL
# Permission errors after upgrade
chown -R www-data:www-data /var/www/nextcloud/ /var/ncdata/
# "Data directory is not writable"
chown -R www-data:www-data /var/ncdata/ && chmod 750 /var/ncdata/
# Increase PHP memory limit
echo 'memory_limit = 1024M' > /var/www/nextcloud/.user.ini
# Force disable a broken app
sudo -u www-data php occ app:disable broken-app
# Or add 'apps_paths' => array() to config.php if occ is inaccessible