Skip to main content
Back to Category

Nextcloud Setup: Self-Hosted Cloud Storage and Collaboration

Nextcloud installation with Docker, Apache/Nginx configuration, database setup, SSL certificate, file synchronization, app store, and performance optimization.

Read time: 16 min Self-Hosting
nextcloudcloud storageself-hostingdockerfile synccollaboration

Table of Contents

Nextcloud Setup: Self-Hosted Cloud Storage and Collaboration

Nextcloud is an open-source, self-hosted alternative to cloud storage services like Google Drive, Dropbox, and OneDrive. Beyond file storage and synchronization, it offers calendar, contacts, video conferencing, document editing, and much more. This guide covers Nextcloud installation with Docker Compose, Nginx reverse proxy configuration, and performance optimization.

What is Nextcloud?

Nextcloud is a collaboration platform that gives you full control over your data:

  • File Storage: Unlimited storage (limited only by your disk capacity)
  • Synchronization: Windows, macOS, Linux, iOS, Android clients
  • App Store: 200+ apps (Nextcloud Talk, Calendar, Contacts, Office)
  • User Management: LDAP/AD integration, group management
  • Security: End-to-end encryption, 2FA, audit log

System Requirements

  • Ubuntu 22.04 or Debian 12
  • Minimum 2 GB RAM (4 GB recommended)
  • Minimum 20 GB disk (additional space for storage)
  • Docker and Docker Compose installed

Installation with Docker Compose

Create Directory Structure

hljs bash
mkdir -p /opt/nextcloud/{data,db,redis}
cd /opt/nextcloud

docker-compose.yml File

hljs yaml
version: '3.8'

services:
  db:
    image: postgres:16-alpine
    container_name: nextcloud-db
    restart: unless-stopped
    environment:
      POSTGRES_DB: nextcloud
      POSTGRES_USER: nextcloud
      POSTGRES_PASSWORD: strong-db-password
    volumes:
      - ./db:/var/lib/postgresql/data
    networks:
      - nextcloud-net

  redis:
    image: redis:7-alpine
    container_name: nextcloud-redis
    restart: unless-stopped
    command: redis-server --requirepass redis-password
    volumes:
      - ./redis:/data
    networks:
      - nextcloud-net

  app:
    image: nextcloud:28-fpm-alpine
    container_name: nextcloud-app
    restart: unless-stopped
    depends_on:
      - db
      - redis
    environment:
      POSTGRES_HOST: db
      POSTGRES_DB: nextcloud
      POSTGRES_USER: nextcloud
      POSTGRES_PASSWORD: strong-db-password
      NEXTCLOUD_ADMIN_USER: admin
      NEXTCLOUD_ADMIN_PASSWORD: admin-password
      NEXTCLOUD_TRUSTED_DOMAINS: cloud.example.com
      REDIS_HOST: redis
      REDIS_HOST_PASSWORD: redis-password
      PHP_MEMORY_LIMIT: 512M
      PHP_UPLOAD_LIMIT: 10G
    volumes:
      - ./data:/var/www/html
    networks:
      - nextcloud-net

  nginx:
    image: nginx:alpine
    container_name: nextcloud-nginx
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./data:/var/www/html:ro
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./ssl:/etc/ssl/nextcloud:ro
    depends_on:
      - app
    networks:
      - nextcloud-net

  cron:
    image: nextcloud:28-fpm-alpine
    container_name: nextcloud-cron
    restart: unless-stopped
    volumes:
      - ./data:/var/www/html
    entrypoint: /cron.sh
    depends_on:
      - db
      - redis
    networks:
      - nextcloud-net

networks:
  nextcloud-net:
    driver: bridge

Nginx Configuration

hljs bash
nano /opt/nextcloud/nginx.conf
hljs nginx
worker_processes auto;

events {
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    upstream php-handler {
        server nextcloud-app:9000;
    }

    server {
        listen 80;
        server_name cloud.example.com;
        return 301 https://$host$request_uri;
    }

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

        ssl_certificate /etc/ssl/nextcloud/fullchain.pem;
        ssl_certificate_key /etc/ssl/nextcloud/privkey.pem;
        ssl_protocols TLSv1.2 TLSv1.3;

        root /var/www/html;
        index index.php index.html;

        client_max_body_size 10G;
        fastcgi_buffers 64 4K;

        add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload" always;
        add_header X-Content-Type-Options nosniff always;
        add_header X-Frame-Options SAMEORIGIN always;

        location = /robots.txt { allow all; log_not_found off; access_log off; }

        location ^~ /.well-known {
            location = /.well-known/carddav { return 301 /remote.php/dav/; }
            location = /.well-known/caldav  { return 301 /remote.php/dav/; }
            return 301 /index.php$request_uri;
        }

        location ~ ^\/(?:index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+)\.php(?:$|\/) {
            fastcgi_split_path_info ^(.+?\.php)(\/.*|)$;
            set $path_info $fastcgi_path_info;
            try_files $fastcgi_script_name =404;
            include fastcgi_params;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            fastcgi_param PATH_INFO $path_info;
            fastcgi_param HTTPS on;
            fastcgi_pass php-handler;
            fastcgi_intercept_errors on;
            fastcgi_request_buffering off;
            fastcgi_read_timeout 3600;
        }

        location ~ \.(?:css|js|svg|gif|png|jpg|ico)$ {
            try_files $uri /index.php$request_uri;
            expires 6M;
            access_log off;
        }

        location / {
            try_files $uri $uri/ /index.php$request_uri;
        }
    }
}

SSL Certificate

hljs bash
# Let's Encrypt certificate with Certbot
apt install certbot -y
certbot certonly --standalone -d cloud.example.com

# Copy certificates
mkdir -p /opt/nextcloud/ssl
cp /etc/letsencrypt/live/cloud.example.com/fullchain.pem /opt/nextcloud/ssl/
cp /etc/letsencrypt/live/cloud.example.com/privkey.pem /opt/nextcloud/ssl/

# Auto-renewal cron
echo "0 3 * * * certbot renew --quiet && cp /etc/letsencrypt/live/cloud.example.com/*.pem /opt/nextcloud/ssl/ && docker restart nextcloud-nginx" | crontab -

Starting Nextcloud

hljs bash
cd /opt/nextcloud
docker compose up -d

# Follow logs
docker compose logs -f app

# Access after installation:
# https://cloud.example.com

occ Commands

Manage Nextcloud with the occ command-line tool:

hljs bash
# Run occ command
docker exec -u www-data nextcloud-app php occ [command]

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

# Toggle maintenance mode
docker exec -u www-data nextcloud-app php occ maintenance:mode --on
docker exec -u www-data nextcloud-app php occ maintenance:mode --off

# Add missing indices
docker exec -u www-data nextcloud-app php occ db:add-missing-indices

# Scan files (detect new files)
docker exec -u www-data nextcloud-app php occ files:scan --all

# Clear cache
docker exec -u www-data nextcloud-app php occ cache:clear

# List users
docker exec -u www-data nextcloud-app php occ user:list

# Create new user
docker exec -u www-data nextcloud-app php occ user:add --password-from-env --display-name="Full Name" username

App Installation

hljs bash
# List available apps
docker exec -u www-data nextcloud-app php occ app:list

# Install apps
docker exec -u www-data nextcloud-app php occ app:install calendar
docker exec -u www-data nextcloud-app php occ app:install contacts
docker exec -u www-data nextcloud-app php occ app:install talk
docker exec -u www-data nextcloud-app php occ app:install onlyoffice

# Enable/disable apps
docker exec -u www-data nextcloud-app php occ app:enable deck
docker exec -u www-data nextcloud-app php occ app:disable activity

Performance Optimization

Redis Caching

Redis is already configured in docker-compose.yml. Add to Nextcloud config:

hljs bash
docker exec -u www-data nextcloud-app php occ config:system:set redis host --value="redis"
docker exec -u www-data nextcloud-app php occ config:system:set redis port --value=6379 --type=integer
docker exec -u www-data nextcloud-app php occ config:system:set redis password --value="redis-password"
docker exec -u www-data nextcloud-app php occ config:system:set memcache.local --value="\\OC\\Memcache\\APCu"
docker exec -u www-data nextcloud-app php occ config:system:set memcache.distributed --value="\\OC\\Memcache\\Redis"
docker exec -u www-data nextcloud-app php occ config:system:set memcache.locking --value="\\OC\\Memcache\\Redis"

Cron Job Configuration

Cron is recommended for Nextcloud background tasks:

hljs bash
# The Nextcloud cron container is already running
# Set cron mode
docker exec -u www-data nextcloud-app php occ background:cron

Large File Uploads

hljs bash
# Increase PHP limits (add to docker-compose.yml environment)
PHP_MEMORY_LIMIT=1G
PHP_UPLOAD_LIMIT=10G

Security Settings

hljs bash
# Add trusted domain
docker exec -u www-data nextcloud-app php occ config:system:set trusted_domains 1 --value="cloud.example.com"

# Brute force protection
docker exec -u www-data nextcloud-app php occ config:system:set auth.bruteforce.protection.enabled --value=true --type=boolean

# Enforce 2FA
docker exec -u www-data nextcloud-app php occ twofactorauth:enforce --on

We recommend using SSD storage for Nextcloud on REXE servers. NVMe SSD performance makes a critical difference for large file transfers.