Back to Cheatsheets
Cheatsheet

Nextcloud Administration Cheatsheet

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/ncdata

Snap

snap install nextcloud
nextcloud.occ config:system:set trusted_domains 1 --value=example.com
nextcloud.enable-https lets-encrypt

Docker Image

docker pull nextcloud:apache       # Apache-based (self-contained)
docker pull nextcloud:fpm          # FPM (needs nginx reverse proxy)
docker pull nextcloud:stable       # Latest stable tag

Docker 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 usage

Docker 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:repair

Docker 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 prune

occ 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 instanceid

Configuration

# 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 values

User 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 & storage

Group 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 mygroup

App 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 path

File 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 cache

Security

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 certs

Database

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 ncuser

Maintenance

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 integrity

Logging

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 entries

DAV & 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-storages

App-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_',
);

Mail

<?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 1

Performance 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 = 1000

PHP 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=256M

APCu

apcu.enabled=1    apcu.shm_size=256M    apcu.ttl=7200

Nginx 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_mode

Security 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 --off

Full 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 --all

Troubleshooting

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 status

Common 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