Nextcloud: Speed Optimization That Actually Works

Been running Nextcloud for 2 years as my personal Dropbox. First 6 months were painful - file sync took forever, web UI was sluggish, and mobile apps timed out constantly. After some serious optimization, it now feels faster than Dropbox.

Problem

Syncing a folder with 50k+ files (photos). Initial sync took 3 days, and incremental syncs took 30+ minutes for just a few new files. CPU usage was at 100%, network was fine, but transfers crawled at ~100KB/s.

Error: The sync is taking longer than expected (desktop client message)

What I Tried

Attempt 1: Increased PHP memory_limit and max_execution_time - no noticeable improvement.
Attempt 2: Switched from SQLite to PostgreSQL - slight improvement but still slow.
Attempt 3: Adjusted client sync settings - made it worse by skipping files.

Actual Fix

The issue was that Nextcloud was scanning every file on every sync cycle. The solution involved: 1) Enabling filesystem caching with Redis, 2) Configuring proper chunked uploads, 3) Adjusting the scan interval, and 4) Using the 'occ files:scan' command for initial scans.

// config/config.php - Performance optimizations

// Enable Redis for file locking and caching
'memcache.local' => '\\OC\\Memcache\\Redis',
'memcache.distributed' => '\\OC\\Memcache\\Redis',
'redis' => [
  'host' => '127.0.0.1',
  'port' => 6379,
],

// File chunking for large uploads
'max_chunk_size' => '16' * 1024 * 1024, // 16MB chunks

// Reduce filesystem scan frequency
'filesystem_check_changes' => 0, // Disable periodic scans

// Enable transactional file locking
'filelocking.enabled' => true,

// Increase upload limits
'max_upload_size' => '16G',

// Enable previews for better UX
'enable_previews' => true,
'preview_max_x' => 2048,
'preview_max_y' => 2048,
# Initial scan optimization (run once)
docker exec -u www-data nextcloud php occ files:scan --all

# For resyncing specific user
docker exec -u www-data nextcloud php occ files:scan --path="/user/files"

# Set up regular cron jobs instead of AJAX
crontab -e
# Add: */5 * * * * docker exec -u www-data nextcloud php cron.php

# Enable caching in PHP-FPM
# /etc/php/7.4/fpm/pool.d/www.conf
php_admin_value[opcache.enable]=1
php_admin_value[opcache.memory_consumption]=128
php_admin_value[opcache.interned_strings_buffer]=8
php_admin_value[opcache.max_accelerated_files]=10000
php_admin_value[opcache.revalidate_freq]=2

Problem

Enabled OPcache in php.ini, restarted PHP-FPM. Nextcloud admin overview showed "OPcache not properly configured". Page loads were still 2-3 seconds, and PHP was recompiling scripts on every request.

What I Tried

Attempt 1: Modified /etc/php/7.4/fpm/php.ini - no change.
Attempt 2: Restarted both PHP-FPM and Nginx - still showed as not configured.
Attempt 3: Checked php -i | grep opcache - showed it was enabled in CLI but not web.

Actual Fix

The issue was that PHP-FPM uses a different php.ini file than CLI. Also, OPcache settings need to be in the pool.d/www.conf file, not just php.ini. Additionally, Docker containers may have different paths - need to check which PHP binary is actually being used.

# For Docker deployments, find the correct PHP ini
docker exec nextcloud php --ini

# Then edit the correct file
# For CLI: /usr/local/etc/php/conf.d/zzz-opcache.ini
# For FPM: /usr/local/etc/php-fpm.d/www.conf

# Add to Docker volume or use docker exec
docker exec nextcloud sh -c 'echo "opcache.enable=1" >> /usr/local/etc/php/conf.d/zzz-opcache.ini'
docker exec nextcloud sh -c 'echo "opcache.memory_consumption=128" >> /usr/local/etc/php/conf.d/zzz-opcache.ini'
docker exec nextcloud sh -c 'echo "opcache.interned_strings_buffer=8" >> /usr/local/etc/php/conf.d/zzz-opcache.ini'
docker exec nextcloud sh -c 'echo "opcache.max_accelerated_files=10000" >> /usr/local/etc/php/conf.d/zzz-opcache.ini'
docker exec nextcloud sh -c 'echo "opcache.revalidate_freq=2" >> /usr/local/etc/php/conf.d/zzz-opcache.ini'

# Restart PHP-FPM
docker restart nextcloud

# Verify OPcache is working
docker exec nextcloud php -i | grep opcache

# In Nextcloud admin overview, should now show:
# "OPcache is properly configured"

Problem

After 6 months of use, Nextcloud became sluggish. Loading the files page took 5+ seconds. PostgreSQL CPU usage was at 80-100% constantly. Database had about 500k rows in oc_filecache.

What I Tried

Attempt 1: Ran VACUUM ANALYZE on PostgreSQL - temporary fix, degradation returned.
Attempt 2: Increased shared_buffers - ran out of RAM on the server.
Attempt 3: Deleted old file versions - helped slightly but queries were still slow.

Actual Fix

The oc_filecache table needed proper indexing and cleanup. Also, Nextcloud stores deleted files in the trash bin indefinitely - need to configure auto-cleanup. Database connection pooling was also misconfigured.

What I Learned

  • Lesson 1: Nextcloud needs Redis caching - without it, every request hits the filesystem and database.
  • Lesson 2: OPcache configuration in Docker requires editing the correct ini files - CLI and web use different configs.
  • Lesson 3: Database maintenance is critical - trash bin and versions accumulate indefinitely without cleanup.
  • Overall: Nextcloud out of the box is slow. It needs aggressive caching, database tuning, and regular maintenance to perform well. But once optimized, it's genuinely faster than commercial alternatives.

Production Setup

Optimized Docker Compose setup for Nextcloud.

# docker-compose.yml - Optimized Nextcloud
version: '3.8'

services:
  nextcloud-db:
    image: postgres:14-alpine
    container_name: nextcloud-db
    environment:
      POSTGRES_DB: nextcloud
      POSTGRES_USER: nextcloud
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - ./data/postgres:/var/lib/postgresql/data
    networks:
      - nextcloud-network
    restart: always
    command: >
      postgres
      -c shared_buffers=256MB
      -c effective_cache_size=1GB
      -c work_mem=16MB
      -c maintenance_work_mem=128MB

  nextcloud-redis:
    image: redis:alpine
    container_name: nextcloud-redis
    networks:
      - nextcloud-network
    restart: always

  nextcloud:
    image: nextcloud:latest
    container_name: nextcloud
    ports:
      - "127.0.0.1:8080:80"
    environment:
      - POSTGRES_HOST=nextcloud-db
      - POSTGRES_DB=nextcloud
      - POSTGRES_USER=nextcloud
      - POSTGRES_PASSWORD=${DB_PASSWORD}
      - REDIS_HOST=nextcloud-redis
    volumes:
      - ./data/nextcloud:/var/www/html
      - ./data/nextcloud-config:/var/www/html/config
    depends_on:
      - nextcloud-db
      - nextcloud-redis
    networks:
      - nextcloud-network
    restart: always

networks:
  nextcloud-network:
# config/config.php - Place after initial setup
 'oc...',
  'passwordsalt' => '...',
  'datadirectory' => '/var/www/html/data',

  // Database
  'dbtype' => 'pgsql',
  'dbname' => 'nextcloud',
  'dbuser' => 'nextcloud',
  'dbpassword' => getenv('DB_PASSWORD'),
  'dbhost' => 'nextcloud-db',

  // Redis caching
  'memcache.local' => '\\OC\\Memcache\\Redis',
  'memcache.distributed' => '\\OC\\Memcache\\Redis',
  'redis' =>
  array (
    'host' => 'nextcloud-redis',
    'port' => 6379,
  ),

  // Performance
  'filelocking.enabled' => true,
  'memcache.locking' => '\\OC\\Memcache\\Redis',
  'max_chunk_size' => 16 * 1024 * 1024,
  'filesystem_check_changes' => 0,

  // Cleanup
  'trashbin_retention_obligation' => 30,
  'versions_retention_obligation' => 30,

  'overwrite.cli.url' => 'https://cloud.yourdomain.com',
  'overwriteprotocol' => 'https',
);

Monitoring & Debugging

Essential maintenance commands:

Essential Commands

# Check Nextcloud status
docker exec -u www-data nextcloud php occ status

# Update Nextcloud
docker exec -u www-data nextcloud php occ upgrade

# Scan files
docker exec -u www-data nextcloud php occ files:scan --all

# Check for errors
docker exec -u www-data nextcloud php occ check

# Database maintenance
docker exec -u www-data nextcloud php occ db:add-missing-indices
docker exec -u www-data nextcloud php occ db:convert-filecache-bigint

# Monitor logs
docker logs -f nextcloud

# Check Redis connection
docker exec nextcloud-redis redis-cli ping

# Backup
docker exec nextcloud-db pg_dump -U nextcloud nextcloud | gzip > backup.sql.gz

Red Flags to Watch For

  • Page load time >2 seconds - check OPcache and Redis configuration
  • Database CPU >50% - run VACUUM ANALYZE and check indexes
  • Disk growing >100MB/day - configure trash/version cleanup
  • High memory usage - adjust PHP-FPM pool settings

Related Resources