Skip to Content

Nextcloud Docker Setup: Own Your Cloud Storage and Stop Paying for Google Drive

Learn how to deploy Nextcloud with Docker and PostgreSQL, configure HTTPS, set up desktop and mobile sync, and run a fully self-hosted cloud storage platform your team actually controls.
Nextcloud setup guide

Nextcloud Docker Setup: Own Your Cloud Storage and Stop Paying for Google Drive

Google Drive, Dropbox, OneDrive — convenient until you read the terms of service, hit a storage limit, or need your files to stay on infrastructure you control. Nextcloud is the open-source alternative that gives you file sync, sharing, calendar, contacts, document editing, and team collaboration on a server you own. This guide walks you through a complete Nextcloud Docker setup: from first container to a production-ready self-hosted cloud your team can actually use.


Prerequisites

  • A Linux server (Ubuntu 22.04 LTS recommended) with at least 2GB RAM and 20GB+ disk (more for actual file storage)
  • Docker Engine and Docker Compose v2 installed
  • A domain name with DNS access — Nextcloud needs HTTPS for mobile sync and most features to work properly
  • Ports 80 and 443 open on your firewall
  • Basic familiarity with Docker Compose and Nginx

Check your starting point:

docker --version
docker compose version
free -h
df -h /

# Confirm ports are free
sudo ss -tlnp | grep -E ':80|:443'

What Nextcloud Actually Gives You

Nextcloud is more than file storage — it's a full collaboration platform. Understanding the scope helps you plan your setup correctly from the start.

Core Features

  • File sync and share — desktop clients for Windows, macOS, and Linux; mobile apps for iOS and Android; WebDAV for anything else
  • Calendar and contacts — CalDAV and CardDAV servers compatible with every major client
  • Collaborative document editing — Nextcloud Office (powered by Collabora) for real-time document, spreadsheet, and presentation editing
  • Talk — built-in video calls, chat, and screen sharing
  • Photos — automatic photo backup from mobile, face recognition, timeline view
  • External storage — mount S3, SFTP, Google Drive, or other sources as Nextcloud folders
  • App ecosystem — 200+ apps for password management, project tracking, forms, and more

Why PostgreSQL Over SQLite

Nextcloud ships with SQLite support for testing, but it falls apart under real usage. For anything beyond a single personal user, use PostgreSQL from the start. Migrating databases later is painful — get this right on day one.


Deploying Nextcloud with Docker Compose

Project Structure

Set up a dedicated directory for the deployment:

mkdir -p ~/nextcloud
cd ~/nextcloud

The Docker Compose File

This Compose file runs Nextcloud with PostgreSQL for the database and Redis for caching and session locking — both required for any multi-user setup:

# docker-compose.yml
version: '3.8'

services:

  postgres:
    image: postgres:15-alpine
    container_name: nextcloud_db
    restart: unless-stopped
    environment:
      POSTGRES_DB: nextcloud
      POSTGRES_USER: nextcloud
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - nextcloud_net
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U nextcloud"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    container_name: nextcloud_redis
    restart: unless-stopped
    command: redis-server --requirepass ${REDIS_PASSWORD}
    volumes:
      - redis_data:/data
    networks:
      - nextcloud_net

  nextcloud:
    image: nextcloud:28-apache
    container_name: nextcloud
    restart: unless-stopped
    ports:
      - "8080:80"
    environment:
      # Database
      POSTGRES_HOST: postgres
      POSTGRES_DB: nextcloud
      POSTGRES_USER: nextcloud
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      # Admin account (only used on first run)
      NEXTCLOUD_ADMIN_USER: ${NEXTCLOUD_ADMIN_USER}
      NEXTCLOUD_ADMIN_PASSWORD: ${NEXTCLOUD_ADMIN_PASSWORD}
      # Trusted domain — required for security
      NEXTCLOUD_TRUSTED_DOMAINS: ${NEXTCLOUD_DOMAIN}
      # Redis
      REDIS_HOST: redis
      REDIS_HOST_PASSWORD: ${REDIS_PASSWORD}
      # PHP settings
      PHP_MEMORY_LIMIT: 1G
      PHP_UPLOAD_LIMIT: 10G
    volumes:
      - nextcloud_data:/var/www/html
      - ./data:/var/www/html/data   # User file storage — point this at a large disk
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_started
    networks:
      - nextcloud_net

volumes:
  postgres_data:
  redis_data:
  nextcloud_data:

networks:
  nextcloud_net:

Environment Variables

Create a .env file alongside your Compose file. Never hardcode credentials in the Compose file itself:

# .env
POSTGRES_PASSWORD=a-strong-postgres-password
REDIS_PASSWORD=a-strong-redis-password
NEXTCLOUD_ADMIN_USER=admin
NEXTCLOUD_ADMIN_PASSWORD=a-strong-admin-password
NEXTCLOUD_DOMAIN=cloud.yourdomain.com

# Generate strong passwords:
# openssl rand -base64 24

Start the stack:

docker compose up -d

# First boot takes 2-3 minutes while Nextcloud initializes
# Watch until you see the Apache startup message
docker compose logs -f nextcloud

Once running, open http://localhost:8080 to confirm the UI loads. Don't do any configuration yet — set up HTTPS first.


Configuring HTTPS with Nginx

Nextcloud requires HTTPS for mobile clients, WebDAV sync, and several security features. Put Nginx in front as a TLS-terminating reverse proxy:

# /etc/nginx/sites-available/nextcloud
upstream nextcloud_backend {
    server localhost:8080;
    keepalive 32;
}

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

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

    ssl_certificate /etc/letsencrypt/live/cloud.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/cloud.yourdomain.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;

    # Nextcloud-recommended headers
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header Referrer-Policy "no-referrer" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Permitted-Cross-Domain-Policies "none" always;
    add_header X-Robots-Tag "noindex, nofollow" always;
    add_header X-XSS-Protection "1; mode=block" always;

    # Large file uploads
    client_max_body_size 10G;
    client_body_timeout 300s;

    # Proxy timeouts for large transfers
    proxy_read_timeout 300s;
    proxy_send_timeout 300s;
    proxy_connect_timeout 300s;

    location / {
        proxy_pass http://nextcloud_backend;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_buffering off;
        proxy_request_buffering off;
    }

    # WebDAV performance
    location = /.well-known/carddav {
        return 301 $scheme://$host/remote.php/dav;
    }
    location = /.well-known/caldav {
        return 301 $scheme://$host/remote.php/dav;
    }
}

Enable the config, get a cert, and reload:

sudo ln -s /etc/nginx/sites-available/nextcloud /etc/nginx/sites-enabled/
sudo nginx -t

# Get Let's Encrypt certificate
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d cloud.yourdomain.com

# Reload with new config
sudo systemctl reload nginx

Now open https://cloud.yourdomain.com — you should see the Nextcloud login page over HTTPS with a valid certificate.


Post-Install Configuration

Running the Security and Setup Checklist

Nextcloud's admin panel has a built-in security scan. Log in as admin, go to Administration → Overview, and work through every warning. The most common ones on a fresh Docker install:

Fix the Background Jobs Scheduler

Nextcloud needs to run background jobs (cleanup, notifications, indexing). The default AJAX method is unreliable — switch to cron:

# Add to host crontab: crontab -e
# Run Nextcloud background jobs every 5 minutes
*/5 * * * * docker exec -u www-data nextcloud php -f /var/www/html/cron.php

# Then in Nextcloud admin UI:
# Administration → Basic settings → Background jobs → Select "Cron"

Configure the Nextcloud Config File

Several important settings live in config/config.php inside the container. Edit it directly for settings not exposed in the UI:

# Get a shell inside the container
docker exec -it nextcloud bash

# Edit config (nano or vi)
nano /var/www/html/config/config.php

Add or update these settings in the config array:

<?php
$CONFIG = array (
  // Your domain — must match Nginx server_name
  'trusted_domains' =>
  array (
    0 => 'cloud.yourdomain.com',
  ),

  // Tell Nextcloud it's behind a reverse proxy
  'overwrite.cli.url' => 'https://cloud.yourdomain.com',
  'overwriteprotocol' => 'https',
  'overwritehost' => 'cloud.yourdomain.com',
  'trusted_proxies' =>
  array (
    0 => '172.0.0.0/8',  // Docker network range
  ),

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

  // Default phone region (for contacts/users)
  'default_phone_region' => 'US',

  // Increase max chunk size for faster uploads
  'max_chunk_size' => 104857600,  // 100MB
);

Installing the Nextcloud Desktop and Mobile Clients

Once HTTPS is working, connect the sync clients:

  • Desktop (Windows/macOS/Linux): Download from nextcloud.com/install, enter your domain and credentials, choose which folders to sync
  • iOS/Android: Search for Nextcloud in the App Store or Play Store — the official apps handle file sync, camera backup, and document editing
  • WebDAV: Connect any WebDAV client to https://cloud.yourdomain.com/remote.php/dav/files/USERNAME/

Tips, Gotchas, and Troubleshooting

"Untrusted Domain" Error After Accessing the UI

If Nextcloud shows an "Access through untrusted domain" error, your NEXTCLOUD_TRUSTED_DOMAINS env var or trusted_domains config array doesn't match the domain you're accessing. Fix it:

# Add the domain via occ CLI inside the container
docker exec -u www-data nextcloud php occ config:system:set \
  trusted_domains 1 --value=cloud.yourdomain.com

# Verify the change
docker exec -u www-data nextcloud php occ config:system:get trusted_domains

File Uploads Failing for Large Files

Three limits need to align: PHP's upload limit, Nginx's client_max_body_size, and Nextcloud's own chunk size. The Compose file already sets PHP limits via environment variables. Confirm they're applied:

# Check active PHP limits inside the container
docker exec nextcloud php -i | grep -E 'upload_max|post_max|memory_limit'

# Should show:
# memory_limit => 1G
# post_max_size => 10G
# upload_max_filesize => 10G

Slow Performance — Redis Not Being Used

If Nextcloud feels sluggish, confirm Redis caching is active. Check the admin overview for "Memory caching" status, or verify via occ:

docker exec -u www-data nextcloud php occ config:system:get memcache.local
# Should return: \OC\Memcache\Redis

# Check Redis connection from inside Nextcloud container
docker exec nextcloud redis-cli -h redis -a your-redis-password ping
# Should return: PONG

Updating Nextcloud

Always back up before updating. Nextcloud updates can involve database migrations:

# 1. Back up the database first
docker exec nextcloud_db pg_dump -U nextcloud nextcloud \
  > ~/backups/nextcloud-db-$(date +%Y-%m-%d).sql

# 2. Pull the new image (update version tag intentionally)
# Edit docker-compose.yml: nextcloud:28-apache → nextcloud:29-apache
docker compose pull
docker compose up -d

# 3. Run database migrations and repairs
docker exec -u www-data nextcloud php occ upgrade
docker exec -u www-data nextcloud php occ db:add-missing-indices
docker exec -u www-data nextcloud php occ maintenance:repair

# 4. Disable maintenance mode if it stuck on
docker exec -u www-data nextcloud php occ maintenance:mode --off

Scanning Files Added Outside the Web UI

If you add files directly to the data/ volume (bypassing Nextcloud), they won't appear in the UI until you trigger a rescan:

# Scan all files for a specific user
docker exec -u www-data nextcloud php occ files:scan username

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

# Scan only a specific path
docker exec -u www-data nextcloud php occ files:scan --path="/username/files/Documents"

Pro Tips

  • Store user data on a separate disk — the ./data volume mount in the Compose file should point to your largest storage volume. Mount a dedicated data disk at /mnt/data and update the path accordingly before your first run.
  • Enable server-side encryption selectively — Nextcloud's server-side encryption protects data at rest but adds CPU overhead and makes backups harder. Only enable it if your threat model requires it and you understand the key management implications.
  • Use Preview Generator for thumbnails — install the Preview Generator app and run occ preview:generate-all on a schedule so photo thumbnails are pre-generated rather than built on demand (which kills performance).
  • Pin image versions in production — use nextcloud:28.0.4-apache instead of nextcloud:28-apache so updates are explicit and controlled.
  • Back up config.php separately — it contains your encryption keys and instance secret. If you lose it, you can't decrypt your data even with a full database backup.

Wrapping Up

A complete Nextcloud Docker setup gives you a fully-featured cloud platform that replaces Google Drive, Google Photos, Google Contacts, Google Calendar, and Dropbox — running on a server you own, with no storage limits except your disk size and no monthly bill except your VPS cost.

Start with the Compose stack in this guide, get HTTPS working with Nginx, install the desktop and mobile clients, and run through the admin security checklist. Once that foundation is solid, explore the app ecosystem — Nextcloud Office, Talk, and Photos turn it from a file sync tool into a complete collaboration platform that gives you everything a SaaS tool would, on your own terms.


Need Nextcloud Deployed for a Team or Business?

Running Nextcloud for more than a handful of users means dealing with performance tuning, LDAP/SSO integration, backup automation, and storage scaling. The sysbrix team deploys and manages production Nextcloud instances for teams that need reliability and control — without the operational overhead of doing it yourself.

Talk to Us →
Uptime Kuma Setup: Self-Host Your Own Uptime Monitor and Never Miss a Downtime Again
Learn how to deploy Uptime Kuma with Docker, configure monitors for your services, set up multi-channel alerts, and publish a public status page — all on your own infrastructure.